Guida
1. Iniziare
AniRena è un indice di torrent focalizzato su anime, manga, audio e media correlati. Puoi navigare e scaricare torrent senza un account. Un account è necessario per caricare torrent, postare nei gruppi o usare l'API.
La barra di navigazione in alto fornisce accesso alle aree principali del sito:
- Home — la pagina di elenco e ricerca torrent.
- Carica — invia un nuovo torrent (richiede login).
- Guida — questa pagina.
- Statistiche — statistiche del sito (torrent, peer, upload nel tempo).
- Gruppi — directory dei gruppi di rilascio.
- RSS — feed RSS degli ultimi upload, filtrabile per categoria.
Il tuo menu account (in alto a destra quando sei loggato) apre un pannello profilo dove puoi regolare le impostazioni, gestire le opzioni di sicurezza e accedere alla tua chiave API.
2. Navigare e cercare
La home page elenca tutti i torrent ordinati per data di upload. Usa la barra di ricerca in alto per filtrare i risultati.
Ricerca di base
Digita qualsiasi parola nella barra di ricerca e premi Invio (o clicca l'icona di ricerca). I risultati sono classificati per rilevanza quando una query è attiva.
Operatori di ricerca
I seguenti operatori possono essere combinati con una query normale:
| Operatore | Esempio | Effetto |
|---|---|---|
user:"name" | user:"SubsPlease" | Mostra solo i torrent caricati da quell'utente. |
Cliccando il nome di un caricatore nell'elenco torrent si esegue automaticamente una ricerca utente.
Categorie e sottocategorie
Usa il selettore categoria (l'icona griglia accanto alla barra di ricerca) per limitare i risultati a una categoria.
- Anime
- Manga/Manhwa/Comic
- Audio
- Letteratura
- Live Action
- Immagini
- Software
- Hentai
- Altro
Ogni categoria ha sottocategorie (es. Anime in RAW, Sub/Audio, Video musicale) selezionabili nel modal categoria.
Ordinamento e filtri
Le intestazioni delle colonne nell'elenco torrent sono cliccabili per ordinare per quella colonna. Nota: seeders e leechers sono valori in tempo reale da Redis e non possono essere usati per l'ordinamento.
Filtro lingua
Usa il selettore lingua (icona bandiera) per mostrare solo i torrent con una lingua specifica.
Feed RSS
Il feed RSS su /rss fornisce gli ultimi upload. Aggiungi ?category=anime (o qualsiasi altro slug categoria) per filtrare il feed.
3. Scaricare torrent
Clicca su qualsiasi nome di torrent per aprire il pannello dettagli. Da lì puoi:
- Scarica .torrent — salva il file .torrent direttamente. L'URL diretto è
/torrents/{id}.torrent - Link Magnet — si apre direttamente nel tuo client torrent tramite il protocollo URI magnet. L'URL è
/torrents/{id}/magnet
Il pannello dettagli mostra anche la descrizione del torrent, l'elenco file, la lista tracker e i conteggi seeder/leecher.
Link di download legacy
I vecchi link di download AniRena sono ancora supportati e reindirizzano automaticamente al file .torrent corretto usando l'ID legacy: /dl/{old_id}
Client BitTorrent consigliati
Qualsiasi client BitTorrent moderno funziona. I client di seguito sono consigliati e supportano completamente BitTorrent v2 / torrent ibridi:
4. Creare un account
Registrazione
Clicca su Registrati nella barra di navigazione. Scegli un nome utente, fornisci un indirizzo email e imposta una password.
Attivazione email
Dopo la registrazione, un'email di verifica viene inviata al tuo indirizzo. Clicca il link nell'email per attivare il tuo account.
Recupero password
Se dimentichi la password, clicca su Password dimenticata nella pagina di login e inserisci il tuo indirizzo email.
5. Caricare torrent
Vai su Carica nella barra di navigazione. Devi essere loggato con un account attivo e non bannato.
Tab Upload — invia un file .torrent esistente
Trascina o seleziona un file .torrent. Una volta caricato, compila i campi:
| Campo | Obbligatorio | Descrizione |
|---|---|---|
| File torrent | Sì | Il file .torrent da caricare. |
| Nome | No | Sostituisci il nome visualizzato del torrent. |
| Categoria | Sì | La categoria del contenuto (Anime, Manga, Audio, ecc.). |
| Sottocategoria | No | Un tipo più specifico all'interno della categoria. |
| Lingue | No | Uno o più tag lingua che descrivono la lingua del contenuto. |
| Gruppo | No | Associa questo rilascio a un gruppo di cui fai parte. |
| Descrizione | No | Descrizione in formato Markdown mostrata nella pagina dettagli (max 65535 caratteri). |
| Privato | No | Imposta il flag privato nel torrent, disabilitando DHT/PEX. |
| URL Announce | No | Sostituisci o aggiungi l'URL announce principale del tracker. |
| Tracker extra | No | Letto dal file torrent. Non può essere modificato durante il caricamento — usa la scheda Crea se vuoi personalizzare l'elenco dei tracker. |
| Commento | No | Sostituisci il campo commento torrent incorporato nel file. |
Il tuo torrent deve includere almeno un URL tracker di AniRena nella sua lista announce (in qualsiasi tier). Il sito lo verifica al momento del caricamento e rifiuterà i torrent che non includono un tracker AniRena. Se hai creato il torrent senza aggiungere prima il tracker AniRena, caricalo e poi riscaricaricalo dal sito — il file scaricato avrà i tracker corretti iniettati automaticamente.
Tab Crea — crea un nuovo torrent
Il tab Crea ti permette di generare un nuovo .torrent da zero specificando percorsi file, URL tracker e altri parametri direttamente nel browser.
Moderazione
Gli upload vengono automaticamente verificati rispetto a una lista di pattern di contenuto vietato. I torrent corrispondenti vengono rifiutati. I torrent duplicati (stesso info hash) vengono anch'essi rifiutati.
6. Il tuo account
Clicca sul tuo nome utente in alto a destra per aprire il pannello profilo.
Impostazioni
Cambia il tema UI, la dimensione del font, lo schema colori, la lingua dell'interfaccia e le preferenze di visualizzazione torrent.
Password
Inserisci la password attuale e la nuova password due volte. Un codice di verifica viene inviato alla tua email registrata.
Autenticazione a due fattori (2FA)
Abilita l'autenticazione a due fattori basata su TOTP usando qualsiasi app di autenticazione. Quando abiliti il 2FA:
- Scansiona il codice QR (o inserisci il segreto manualmente) nella tua app di autenticazione.
- Inserisci il codice a 6 cifre mostrato nella tua app per confermare la configurazione.
- Salva i codici di recupero mostrati — sono codici monouso per recuperare l'accesso.
Per disabilitare il 2FA, inserisci il tuo codice TOTP attuale e conferma.
Sessioni attive
Visualizza tutte le sessioni di login attualmente attive. Clicca Revoca su qualsiasi sessione non riconosciuta.
Chiave API
Genera una chiave API personale usata per caricare torrent programmaticamente tramite l'API AniRena.
Elimina account
Richiedere l'eliminazione dell'account avvia un periodo di grazia di 30 giorni. Puoi annullare l'eliminazione in qualsiasi momento entro quel periodo.
7. API AniRena
AniRena fornisce un'API JSON che ti permette di caricare torrent programmaticamente usando una chiave API personale.
Autenticazione
L'API utilizza un flusso di autenticazione in due fasi. Prima scambia la tua chiave API permanente con un bearer token di breve durata, poi passa quel token nell'intestazione Authorization di ogni richiesta API.
La tua chiave API è disponibile in Il tuo account > Chiave API. Tienila segreta — chiunque la possieda può ottenere bearer token e caricare a tuo nome. Se viene compromessa, revocala immediatamente e genera una nuova.
Fase 1 — Ottieni un bearer token
/api/v1/auth/tokenInvia una richiesta POST all'endpoint del token con la tua chiave API nell'intestazione Authorization. Non è richiesto un corpo della richiesta.
Authorization: ApiKey <your-api-key>
Risposta del token
{
"token": "<bearer-token>",
"token_type": "Bearer",
"expires_in": 3600
}Durata del token
I token Bearer rimangono validi fino a 3600 secondi dall'emissione e possono essere riutilizzati per ogni chiamata fino alla scadenza. Quando un token scade, genera un nuovo token tramite POST /api/v1/auth/token. Ogni risposta restituisce comunque il token corrente nell'intestazione X-New-Token per compatibilità con i client esistenti.
X-New-Token: <next-bearer-token>
Accesso in un'unica richiesta (con 2FA)
/api/v1/auth/loginAutenticati con nome utente o email e password in un'unica richiesta e ricevi direttamente un token bearer. Se sul tuo account è attiva la 2FA, includi il codice attuale dell'app di autenticazione in totp_code (o un codice di recupero in recovery_code). Facoltativamente imposta new_api_key su true per generare, nella stessa risposta, anche una nuova chiave API permanente.
Corpo della richiesta
{
"login": "username or email",
"password": "your-password",
"totp_code": "123456", // obbligatorio se la 2FA è attiva (6 cifre)
"recovery_code": "", // alternativa a totp_code
"new_api_key": false // imposta true per generare anche una nuova chiave API
}Risposta del token
{
"ok": true,
"token": "<bearer-token>",
"token_type": "Bearer",
"expires_in": 3600,
"api_key": "<new-api-key>" // presente solo quando new_api_key era true
}Il token bearer funziona esattamente come quello ottenuto da /api/v1/auth/token. Il campo api_key viene restituito solo quando new_api_key è true — salvalo subito, poiché viene mostrato una sola volta e sostituisce qualsiasi chiave precedente.
Esempio — accedi e (facoltativamente) ottieni una nuova chiave API
# pip install requests import requests BASE_URL = "https://www.anirena.com" # One request: authenticate (with 2FA if enabled) and get a bearer token. # Set new_api_key=True to also receive a brand-new permanent API key. resp = requests.post( f"{BASE_URL}/api/v1/auth/login", json={ "login": "your-username", # username or email "password": "your-password", "totp_code": "123456", # omit if 2FA is not enabled "new_api_key": True, # optional }, ) resp.raise_for_status() data = resp.json() token = data["token"] # use as: Authorization: Bearer <token> if "api_key" in data: print("New API key — store it now:", data["api_key"])
// Built-in fetch — requires Node.js 18+ const BASE_URL = "https://www.anirena.com"; // One request: authenticate (with 2FA if enabled) and get a bearer token. // Set new_api_key:true to also receive a brand-new permanent API key. const resp = await fetch(`${BASE_URL}/api/v1/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ login: "your-username", // username or email password: "your-password", totp_code: "123456", // omit if 2FA is not enabled new_api_key: true, // optional }), }); const data = await resp.json(); const token = data.token; // use as: Authorization: Bearer <token> if (data.api_key) console.log("New API key — store it now:", data.api_key);
// Requires: curl extension (enabled by default in PHP 8+) <?php define("BASE_URL", "https://www.anirena.com"); // One request: authenticate (with 2FA if enabled) and get a bearer token. // Set new_api_key => true to also receive a brand-new permanent API key. $ch = curl_init(BASE_URL . "/api/v1/auth/login"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode([ "login" => "your-username", // username or email "password" => "your-password", "totp_code" => "123456", // omit if 2FA is not enabled "new_api_key" => true, // optional ]), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], ]); $data = json_decode(curl_exec($ch), true); curl_close($ch); $token = $data["token"]; // use as: Authorization: Bearer <token> if (isset($data["api_key"])) echo "New API key — store it now: " . $data["api_key"];
// Cargo.toml: // serde_json = "1" // reqwest = { version = "0.12", features = ["blocking", "json"] } const BASE_URL: &str = "https://www.anirena.com"; fn main() { let client = reqwest::blocking::Client::new(); // One request: authenticate (with 2FA if enabled) and get a bearer token. // Set new_api_key:true to also receive a brand-new permanent API key. let data: serde_json::Value = client .post(format!("{BASE_URL}/api/v1/auth/login")) .json(&serde_json::json!({ "login": "your-username", // username or email "password": "your-password", "totp_code": "123456", // omit if 2FA is not enabled "new_api_key": true, // optional })) .send().unwrap() .json().unwrap(); let token = data["token"].as_str().unwrap(); // Authorization: Bearer <token> if let Some(key) = data["api_key"].as_str() { println!("New API key — store it now: {key}"); } }
Fase 2 — Carica un torrent
/api/v1/torrentsInvia una semplice richiesta POST JSON con il bearer token nell'intestazione Authorization.
Corpo della richiesta
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
torrent | string | Sì | Contenuto del file .torrent codificato in Base64. |
category | string | Sì | Slug categoria: anime, manga, audio, literature, live, pictures, software, hentai, other. |
name | string | No | Sostituisci il nome visualizzato del torrent. |
sub_category | string | No | Slug sottocategoria (es. raw, sub-audio). Deve appartenere alla categoria scelta. |
languages | string[] | No | Array di codici lingua BCP 47 (es. en, ja). |
group_id | string | No | UUID di un gruppo di cui fai parte per associare questo rilascio. |
description | string | No | Descrizione del rilascio in formato Markdown (max 65535 caratteri). |
comment | string | No | Sostituisci il campo commento torrent incorporato. |
is_private | boolean | No | Impostare su true per abilitare il flag privato nel torrent. |
comments_enabled | boolean | No | Consenti commenti su questo torrent. Il valore predefinito è true (abilitato). |
anime_id | string | No | UUID di un'entry anime da collegare a questo torrent. Ottieni l'UUID tramite GET /api/v1/anime/search. Restituisce 400 se l'UUID non corrisponde a nessuna entry conosciuta. |
announce | string | No | Sostituisci o aggiungi l'URL announce principale. |
trackers | string | No | Lista separata da newline di URL tracker aggiuntivi. |
test | boolean | No | Imposta su true per eseguire una simulazione: la richiesta viene completamente validata ma il torrent non viene salvato. Usalo per verificare che il tuo payload sia corretto prima dell'invio reale. |
Risposta di successo simulazione — 200 OK
{
"ok": true,
"test": true,
"name": "My Torrent Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null
}Codici lingua disponibili
abaaafaksqamarar-001anhyasavaeayazbmbaeubebnbhbibsbrbgmyyuecachcenyzhzh-HKzh-Hanszh-SGzh-TWcucvkwcocrhrcsdadvnlnl-BEdzenen-USeoeteefofjfilfifrfr-CAffgllgkadede-ATelgnguhthahehzhihohuisioigidiaieiuikgaitjajvklknkrkskkkmkirwrnkvkgkokjkukylolalvlilnltlulbmkmgmsmlmtgvmimrmhmnnanvngnendsenonbnnocorojomospipsfaplptpt-BRpaqurormrusmsgsascgdsrsr-Latnsniisdsiskslsonrsteses-419es-MXsuswsssvtltytgtatttethbotitotstntrtktwukuruguzvevivowacyfywoxhyiyozazuRichiesta di esempio
# pip install requests import base64, pathlib, requests API_KEY = "YOUR_API_KEY" BASE_URL = "https://www.anirena.com" # Step 1: exchange API key for a short-lived bearer token auth = requests.post( f"{BASE_URL}/api/v1/auth/token", headers={"Authorization": f"ApiKey {API_KEY}"}, ) auth.raise_for_status() token = auth.json()["token"] # Step 2: upload — plain JSON with the bearer token torrent_b64 = base64.b64encode(pathlib.Path("file.torrent").read_bytes()).decode() resp = requests.post( f"{BASE_URL}/api/v1/torrents", json={ "torrent": torrent_b64, "category": "anime", "sub_category": "raw", "languages": ["ja"], "description": "# My Release\n\nRelease notes here.", "is_private": False, }, headers={"Authorization": f"Bearer {token}"}, ) resp.raise_for_status() data = resp.json() token = resp.headers.get("X-New-Token", token) # save for next request print(data["id"], data["name"]) # torrent UUID and title
// Built-in modules only — requires Node.js 18+ (for global fetch) const fs = require("fs"); const API_KEY = "YOUR_API_KEY"; const BASE_URL = "https://www.anirena.com"; // Step 1: exchange API key for a short-lived bearer token let authResp = await fetch(`${BASE_URL}/api/v1/auth/token`, { method: "POST", headers: { Authorization: `ApiKey ${API_KEY}` }, }); let { token } = await authResp.json(); // Step 2: upload — plain JSON with the bearer token const torrentB64 = fs.readFileSync("file.torrent").toString("base64"); const resp = await fetch(`${BASE_URL}/api/v1/torrents`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify({ torrent: torrentB64, category: "anime", sub_category: "raw", languages: ["ja"], description: "# My Release\n\nRelease notes here.", is_private: false, }), }); const data = await resp.json(); token = resp.headers.get("x-new-token") ?? token; // save for next request console.log(data.id, data.name); // torrent UUID and title
// Requires: curl extension (enabled by default in PHP 8+) <?php define("API_KEY", "YOUR_API_KEY"); define("BASE_URL", "https://www.anirena.com"); // Step 1: exchange API key for a short-lived bearer token $ch = curl_init(BASE_URL . "/api/v1/auth/token"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: ApiKey " . API_KEY], ]); $token = json_decode(curl_exec($ch), true)["token"]; curl_close($ch); // Step 2: upload — plain JSON with the bearer token $torrentB64 = base64_encode(file_get_contents("file.torrent")); $respHeaders = []; $ch = curl_init(BASE_URL . "/api/v1/torrents"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode([ "torrent" => $torrentB64, "category" => "anime", "sub_category" => "raw", "languages" => ["ja"], "description" => "# My Release\n\nRelease notes here.", "is_private" => false, ]), CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADERFUNCTION => function($ch, $h) use (&$respHeaders) { $respHeaders[] = $h; return strlen($h); }, CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "Authorization: Bearer " . $token, ], ]); $data = json_decode(curl_exec($ch), true); curl_close($ch); foreach ($respHeaders as $h) { // save new token for next request if (stripos($h, "X-New-Token:") === 0) $token = trim(substr($h, strlen("X-New-Token:"))); } echo $data["id"] . " " . $data["name"]; // torrent UUID and title
// Cargo.toml: // base64 = "0.22" // serde_json = "1" // reqwest = { version = "0.12", features = ["blocking", "json"] } use base64::{engine::general_purpose::STANDARD as B64, Engine}; const API_KEY: &str = "YOUR_API_KEY"; const BASE_URL: &str = "https://www.anirena.com"; fn main() { let client = reqwest::blocking::Client::new(); // Step 1: exchange API key for a short-lived bearer token let auth: serde_json::Value = client .post(format!("{BASE_URL}/api/v1/auth/token")) .header("Authorization", format!("ApiKey {API_KEY}")) .send().unwrap().json().unwrap(); let mut token = auth["token"].as_str().unwrap().to_string(); // Step 2: upload — plain JSON with the bearer token let torrent_b64 = B64.encode(std::fs::read("file.torrent").unwrap()); let resp = client .post(format!("{BASE_URL}/api/v1/torrents")) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "torrent": torrent_b64, "category": "anime", "sub_category": "raw", "languages": ["ja"], "description": "# My Release\n\nRelease notes here.", "is_private": false })) .send().unwrap(); if let Some(t) = resp.headers().get("x-new-token") { token = t.to_str().unwrap().to_string(); // save for next request } let data: serde_json::Value = resp.json().unwrap(); println!("{} {}", data["id"], data["name"]); // torrent UUID and title }
Risposta di successo — 200 OK
{
"ok": true,
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Torrent Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null
}Risposte di errore
| Stato HTTP | Significato |
|---|---|
400 | Corpo della richiesta non valido o campo obbligatorio mancante. |
401 | Bearer token mancante, scaduto o già ruotato. Ri-autenticati tramite POST /api/v1/auth/token. |
403 | Account bannato, disabilitato o IP bloccato. |
409 | Torrent duplicato — stesso info hash già esistente. |
422 | File torrent non analizzabile o validazione fallita. |
429 | Limite di frequenza superato. Riprova dopo il reset della finestra. |
503 | Il sito è in modalità manutenzione o sola lettura. |
Limitazione della frequenza
Gli upload API sono soggetti a un limite di frequenza configurabile separato dall'interfaccia web. Il limite è per chiave API.
Generare file torrent con torrent-builder
torrent-builder è uno strumento CLI open source costruito su libtorrent-rasterbar che consente di creare file .torrent BitTorrent v1, v2 e hybrid dalla riga di comando. Si abbina perfettamente con l'AniRena upload API — genera il file in locale, poi fai un POST direttamente al tracker. cantalupo555/torrent-builder.
Compilare dal sorgente
Richiede CMake >= 3.28.3 e libtorrent-rasterbar >= 2.0.11. Clona il repository e compila con CMake:
# Install system dependencies sudo apt-get install build-essential cmake libtorrent-rasterbar-dev # Clone & build git clone https://github.com/cantalupo555/torrent-builder.git cd torrent-builder mkdir build && cd build cmake .. && cmake --build .
# Install dependencies via Homebrew brew install cmake libtorrent-rasterbar # Clone & build git clone https://github.com/cantalupo555/torrent-builder.git cd torrent-builder mkdir build && cd build cmake .. && cmake --build .
Flag principali
| Campo | Descrizione |
|---|---|
--path | Percorso al file o alla directory da pacchettizzare (obbligatorio). |
--output | Nome del file .torrent di output (obbligatorio). |
--version | Formato BitTorrent — 1 = v1, 2 = v2, 3 = hybrid (predefinito: 3). |
--tracker | Aggiunge un URL di announce del tracker. Ripeti il flag per aggiungere più tracker. |
--comment | Incorpora una stringa di commento nei metadati del torrent. |
--private | Imposta il flag privato per limitare la distribuzione ai soli tracker elencati. |
--piece-size | Dimensione del pezzo in KB (16-32768). Lascia non impostato per la selezione automatica. |
-i | Avvia la modalità di configurazione interattiva passo dopo passo. |
Flusso di lavoro end-to-end: build -> upload
Gli esempi seguenti creano un torrent hybrid con torrent-builder, poi si autenticano con l'AniRena API e caricano il risultato in un unico script.
# pip install requests import base64, subprocess, requests API_KEY = "YOUR_API_KEY" BASE_URL = "https://www.anirena.com" # Step 1: build the torrent with torrent-builder # --version 1=v1 2=v2 3=hybrid (default) subprocess.run([ "./torrent-builder/build/torrent_builder", "--path", "/data/my_release", "--output", "my_release.torrent", "--version", "3", # hybrid "--tracker", "udp://open.tracker.gg:6969/announce", "--comment", "My Release", "--creator", "--creation-date", ], check=True) # Step 2: authenticate token = requests.post( f"{BASE_URL}/api/v1/auth/token", headers={"Authorization": f"ApiKey {API_KEY}"}, ).json()["token"] # Step 3: upload torrent_b64 = base64.b64encode(open("my_release.torrent", "rb").read()).decode() resp = requests.post( f"{BASE_URL}/api/v1/torrents", json={ "torrent": torrent_b64, "category": "anime", "sub_category": "raw", "languages": ["ja"], "comments_enabled": True, }, headers={"Authorization": f"Bearer {token}"}, ) resp.raise_for_status() data = resp.json() print(data["id"], data["name"])
// Built-in modules — Node.js 18+ const fs = require("fs"); const { execFileSync } = require("child_process"); const API_KEY = "YOUR_API_KEY"; const BASE_URL = "https://www.anirena.com"; // Step 1: build the torrent (--version 1=v1, 2=v2, 3=hybrid) execFileSync("./torrent-builder/build/torrent_builder", [ "--path", "/data/my_release", "--output", "my_release.torrent", "--version", "3", "--tracker", "udp://open.tracker.gg:6969/announce", "--comment", "My Release", "--creator", "--creation-date", ]); // Step 2: authenticate let { token } = await (await fetch(`${BASE_URL}/api/v1/auth/token`, { method: "POST", headers: { Authorization: `ApiKey ${API_KEY}` }, })).json(); // Step 3: upload const torrentB64 = fs.readFileSync("my_release.torrent").toString("base64"); const resp = await fetch(`${BASE_URL}/api/v1/torrents`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify({ torrent: torrentB64, category: "anime", sub_category: "raw", languages: ["ja"], comments_enabled: true, }), }); const data = await resp.json(); console.log(data.id, data.name);
// PHP 8+ with curl and proc_open <?php define("API_KEY", "YOUR_API_KEY"); define("BASE_URL", "https://www.anirena.com"); // Step 1: build the torrent (version: 1=v1, 2=v2, 3=hybrid) exec(implode(" ", array_map("escapeshellarg", [ "./torrent-builder/build/torrent_builder", "--path", "/data/my_release", "--output", "my_release.torrent", "--version", "3", "--tracker", "udp://open.tracker.gg:6969/announce", "--comment", "My Release", "--creator", "--creation-date", ]))); // Step 2: authenticate $ch = curl_init(BASE_URL . "/api/v1/auth/token"); curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: ApiKey " . API_KEY]]); $token = json_decode(curl_exec($ch), true)["token"]; curl_close($ch); // Step 3: upload $torrentB64 = base64_encode(file_get_contents("my_release.torrent")); $respHeaders = []; $ch = curl_init(BASE_URL . "/api/v1/torrents"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode([ "torrent" => $torrentB64, "category" => "anime", "sub_category" => "raw", "languages" => ["ja"], "comments_enabled" => true, ]), CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADERFUNCTION => function($ch, $h) use (&$respHeaders) { $respHeaders[] = $h; return strlen($h); }, CURLOPT_HTTPHEADER => ["Content-Type: application/json", "Authorization: Bearer " . $token], ]); $data = json_decode(curl_exec($ch), true); curl_close($ch); echo $data["id"] . " " . $data["name"];
// Cargo.toml: // base64 = "0.22" // serde_json = "1" // reqwest = { version = "0.12", features = ["blocking", "json"] } use base64::{engine::general_purpose::STANDARD as B64, Engine}; use std::process::Command; const API_KEY: &str = "YOUR_API_KEY"; const BASE_URL: &str = "https://www.anirena.com"; fn main() { // Step 1: build the torrent (--version 1=v1, 2=v2, 3=hybrid) Command::new("./torrent-builder/build/torrent_builder") .args([ "--path", "/data/my_release", "--output", "my_release.torrent", "--version", "3", "--tracker", "udp://open.tracker.gg:6969/announce", "--comment", "My Release", "--creator", "--creation-date", ]) .status().expect("torrent_builder failed"); let client = reqwest::blocking::Client::new(); // Step 2: authenticate let auth: serde_json::Value = client .post(format!("{BASE_URL}/api/v1/auth/token")) .header("Authorization", format!("ApiKey {API_KEY}")) .send().unwrap().json().unwrap(); let token = auth["token"].as_str().unwrap().to_string(); // Step 3: upload let torrent_b64 = B64.encode(std::fs::read("my_release.torrent").unwrap()); let resp: serde_json::Value = client .post(format!("{BASE_URL}/api/v1/torrents")) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "torrent": torrent_b64, "category": "anime", "sub_category": "raw", "languages": ["ja"], "comments_enabled": true, })) .send().unwrap().json().unwrap(); println!("{} {}", resp["id"], resp["name"]); }
Cercare metadati torrent
/api/v1/torrents/searchInvia una semplice richiesta POST JSON per recuperare elenchi di torrent con le stesse opzioni di ricerca e filtro disponibili sul sito web. Il file .torrent stesso non viene restituito — usa la normale route di download per quello.
# pip install requests (token already obtained — see upload example) resp = requests.post( f"{BASE_URL}/api/v1/torrents/search", json={"q": "Sword Art Online", "category": "anime", "per_page": 25}, headers={"Authorization": f"Bearer {token}"}, ) resp.raise_for_status() data = resp.json() token = resp.headers.get("X-New-Token", token) # save for next request for t in data["torrents"]: print(t["title"], "-", t["magnet"])
// token already obtained — see upload example const resp = await fetch(`${BASE_URL}/api/v1/torrents/search`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify({ q: "Sword Art Online", category: "anime", per_page: 25 }), }); const data = await resp.json(); token = resp.headers.get("x-new-token") ?? token; // save for next request data.torrents.forEach(t => console.log(t.title, "-", t.magnet));
// token already obtained — see upload example $respHeaders = []; $ch = curl_init(BASE_URL . "/api/v1/torrents/search"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode([ "q" => "Sword Art Online", "category" => "anime", "per_page" => 25 ]), CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADERFUNCTION => function($ch, $h) use (&$respHeaders) { $respHeaders[] = $h; return strlen($h); }, CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "Authorization: Bearer " . $token, ], ]); $data = json_decode(curl_exec($ch), true); curl_close($ch); foreach ($respHeaders as $h) { // save new token for next request if (stripos($h, "X-New-Token:") === 0) $token = trim(substr($h, strlen("X-New-Token:"))); } foreach ($data["torrents"] as $t) { echo $t["title"] . " - " . $t["magnet"] . "\n"; }
// client and token already obtained — see upload example let resp = client .post(format!("{BASE_URL}/api/v1/torrents/search")) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({"q": "Sword Art Online", "category": "anime", "per_page": 25})) .send().unwrap(); if let Some(t) = resp.headers().get("x-new-token") { token = t.to_str().unwrap().to_string(); // save for next request } let data: serde_json::Value = resp.json().unwrap(); for t in data["torrents"].as_array().unwrap() { println!("{} - {}", t["title"], t["magnet"]); }
Parametri di ricerca
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
q | string | No | Ricerca testo libero. Supporta i prefissi group:slug, group:"Name", user:name. |
category | string | No | Slug categoria (es. "anime"). |
sub_category | string | No | Slug sottocategoria (es. "raw"). |
languages | string[] | No | Array di codici lingua BCP 47 (es. en, ja). |
sort | string | No | Campo di ordinamento: date (predefinito), size, seeders, leechers, completed, title. |
order | string | No | Direzione di ordinamento: desc (predefinito) o asc. |
page | integer | No | Numero pagina, a partire da 1 (predefinito 1). |
per_page | integer | No | Risultati per pagina, 1–250 (predefinito 50). |
hide_adult | boolean | No | Esclude i torrent di categoria adulti. Predefinito true per gli utenti normali. |
show_dead | boolean | No | Quando è false (predefinito), i torrent più vecchi del periodo di tolleranza per i torrent morti e senza seeder attivi vengono esclusi. Imposta su true per includerli. |
Risposta
{
"total": 1234,
"page": 1,
"per_page": 50,
"total_pages": 25,
"from": 1,
"to": 50,
"torrents": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Release Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null,
"size_fmt": "1.4 GB",
"completed": 42,
"seeders": 10,
"leechers": 3,
"languages": ["ja", "en"],
"comment_count": 7,
"created_at": "2024-01-15 12:34",
"cat_slug": "anime",
"sub_slug": "raw",
"group_name": null,
"uploader": "username",
"magnet": "magnet:?xt=urn:btih:..."
}
]
}comment_count — Numero di commenti non eliminati su questo torrent.
Limitazione della frequenza ricerca
Le richieste di ricerca sono soggette a un limite di frequenza configurabile separato (predefinito 60 richieste per 60 secondi per chiave API). Gli account staff sono esenti.
Ottieni dettagli del torrent
/api/v1/torrent/{id}Recupera i metadati completi di un singolo torrent — inclusi i campi che l'endpoint di ricerca omette, come la descrizione in Markdown, il commento .torrent incorporato, l'elenco dei file con la dimensione di ciascuno e il layout completo dei tier dei tracker. Quando disponibili, i conteggi live di seeders e leechers vengono letti dal tracker.
Risposta
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Release Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null,
"size_fmt": "1.4 GB",
"completed": 42,
"seeders": 10,
"leechers": 3,
"ext_seeders": 128,
"ext_leechers": 14,
"created_at": "2024-01-15 12:34",
"torrent_created": "2024-01-15 12:30",
"created_by_client": "mktorrent 1.1",
"cat_name": "Anime",
"cat_slug": "anime",
"sub_name": "Raw",
"sub_slug": "raw",
"group_name": null,
"group_slug": null,
"uploader": "username",
"uploader_id": "...",
"description": "# My Release\n\nRelease notes here.",
"comment": "",
"is_private": false,
"magnet": "magnet:?xt=urn:btih:...",
"languages": [
{ "code": "ja", "name": "Japanese", "country_code": "jp" }
],
"tracker_tiers": [
{ "tier": 0, "urls": ["udp://tracker.example.org:6969/announce"] }
],
"files": [
{ "path": "My Release/episode-01.mkv", "size": 1503238553 }
],
"comments_enabled": true,
"comments_locked": false,
"comment_count": 7
}seeders, leechers — Conteggi live dal tracker interno; entrambi riportano 0 quando l'archivio del tracker non ha una voce per questo info hash o non è raggiungibile.
ext_seeders, ext_leechers — Numero massimo di seeder e leecher segnalato da un singolo tracker esterno scrapato per questo torrent. I tracker che seguono lo stesso swarm si sovrappongono, quindi si usa il massimo invece della somma; entrambi riportano 0 quando nessun tracker dispone di dati di scrape per questo info hash.
Risposte di errore
| Stato HTTP | Significato |
|---|---|
400 | L'id del torrent deve essere un UUID di 36 caratteri con trattini o una stringa esadecimale di 32 caratteri. |
401 | Bearer token mancante, scaduto o già ruotato. Ri-autenticati tramite POST /api/v1/auth/token. |
404 | Torrent non trovato. |
429 | Limite di frequenza superato. Riprova dopo il reset della finestra. |
503 | Il sito è in modalità manutenzione o sola lettura. |
Recuperare i commenti del torrent
/api/v1/torrents/{id}/commentsRecupera i commenti paginati per un torrent. Il numero di commenti per pagina è controllato dall'impostazione COMMENT_PER_PAGE nel file .env del server (predefinito 20). Solo i torrent con i commenti abilitati restituiranno risultati — tutti gli altri restituiscono 403.
Parametri di query
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
page | integer | No | Numero di pagina, a partire da 1 (predefinito 1). |
# pip install requests (token already obtained — see upload example) TORRENT_ID = "550e8400-e29b-41d4-a716-446655440000" resp = requests.get( f"{BASE_URL}/api/v1/torrents/{TORRENT_ID}/comments", params={"page": 1}, headers={"Authorization": f"Bearer {token}"}, ) resp.raise_for_status() data = resp.json() token = resp.headers.get("X-New-Token", token) # save for next request for c in data["comments"]: print(c["username"], "-", c["body"])
// token already obtained — see upload example const TORRENT_ID = "550e8400-e29b-41d4-a716-446655440000"; const resp = await fetch(`${BASE_URL}/api/v1/torrents/${TORRENT_ID}/comments?page=1`, { headers: { Authorization: `Bearer ${token}` }, }); const data = await resp.json(); token = resp.headers.get("x-new-token") ?? token; // save for next request data.comments.forEach(c => console.log(c.username, "-", c.body));
// token already obtained — see upload example $torrentId = "550e8400-e29b-41d4-a716-446655440000"; $respHeaders = []; $ch = curl_init(BASE_URL . "/api/v1/torrents/{$torrentId}/comments?page=1"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADERFUNCTION => function($ch, $h) use (&$respHeaders) { $respHeaders[] = $h; return strlen($h); }, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $token], ]); $data = json_decode(curl_exec($ch), true); curl_close($ch); foreach ($respHeaders as $h) { // save new token for next request if (stripos($h, "X-New-Token:") === 0) $token = trim(substr($h, strlen("X-New-Token:"))); } foreach ($data["comments"] as $c) { echo $c["username"] . " - " . $c["body"] . "\n"; }
// client and token already obtained — see upload example let torrent_id = "550e8400-e29b-41d4-a716-446655440000"; let resp = client .get(format!("{BASE_URL}/api/v1/torrents/{torrent_id}/comments?page=1")) .header("Authorization", format!("Bearer {token}")) .send().unwrap(); if let Some(t) = resp.headers().get("x-new-token") { token = t.to_str().unwrap().to_string(); // save for next request } let data: serde_json::Value = resp.json().unwrap(); for c in data["comments"].as_array().unwrap() { println!("{} - {}", c["username"], c["body"]); }
Risposta
{
"torrent_id": "550e8400-e29b-41d4-a716-446655440000",
"page": 1,
"per_page": 20,
"total": 45,
"total_pages": 3,
"comments": [
{
"id": "...",
"user_id": "...",
"username": "uploader",
"role": "user",
"author_banned": false,
"body": "Great release!",
"created_at": "2024-01-15 12:34:00",
"edited_at": null,
"edited_by_username": null,
"deleted_at": null
}
]
}Il campo body è una stringa vuota quando l'autore del commento è bannato o il commento è stato eliminato. Il flag author_banned indica quale caso si applica.
Risposte di errore
| Stato HTTP | Significato |
|---|---|
401 | Bearer token mancante, scaduto o già ruotato. Ri-autenticati tramite POST /api/v1/auth/token. |
403 | I commenti sono disabilitati per questo torrent. |
404 | Torrent non trovato. |
503 | Il sito è in modalità manutenzione o sola lettura. |
Cercare voci anime
/api/v1/anime/search?q=<query>Cerca entry anime per titolo per ottenere il loro UUID. L'UUID può essere passato come anime_id nel corpo dell'upload per collegare un torrent a un'entry anime al momento del caricamento, oppure usato con PUT /api/torrents/{id}/anime dopo il caricamento. Non è richiesta autenticazione. Soggetto allo stesso limite di frequenza della ricerca torrent (predefinito 60 richieste per 60 secondi per IP).
# pip install requests (no authentication required)
resp = requests.get(
f"{BASE_URL}/api/v1/anime/search",
params={"q": "Sword Art Online", "page": 1, "per_page": 10},
)
resp.raise_for_status()
for item in resp.json()["results"]:
print(item["id"], "-", item["title"])// No authentication required
const resp = await fetch(
`${BASE_URL}/api/v1/anime/search?q=${encodeURIComponent("Sword Art Online")}&page=1&per_page=10`
);
const data = await resp.json();
data.results.forEach(item => console.log(item.id, "-", item.title));// No authentication required
$ch = curl_init(BASE_URL . "/api/v1/anime/search?" . http_build_query([
"q" => "Sword Art Online", "page" => 1, "per_page" => 10,
]));
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($data["results"] as $item) {
echo $item["id"] . " - " . $item["title"] . "\n";
}// No authentication required
let data: serde_json::Value = reqwest::blocking::get(
format!("{BASE_URL}/api/v1/anime/search?q=Sword+Art+Online&page=1&per_page=10")
).unwrap().json().unwrap();
for item in data["results"].as_array().unwrap() {
println!("{} - {}", item["id"], item["title"]);
}Parametri di query
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
q | string | Sì | Stringa di ricerca titolo (obbligatoria). Corrisposta a titolo e sinonimi. |
page | integer | No | Numero pagina, a partire da 1 (predefinito 1). |
per_page | integer | No | Risultati per pagina, 1–50 (predefinito 10). |
Risposta
{
"total": 42,
"page": 1,
"per_page": 10,
"total_pages": 5,
"results": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Sword Art Online",
"anime_type": "TV",
"episodes": 25,
"status": "FINISHED",
"season": "FALL",
"season_year": 2012,
"picture": "https://cdn.myanimelist.net/images/anime/...",
"thumbnail": "https://cdn.myanimelist.net/images/anime/...",
"duration_secs": 1440
}
]
}Elenco e recupero dei gruppi
/api/v1/groupsRestituisce un elenco paginato di gruppi pubblici (abilitati e non bloccati). Richiede autenticazione Bearer token.
/api/v1/groups/{id_or_slug}Restituisce un singolo gruppo pubblico tramite ID numerico o slug. Restituisce 404 se il gruppo è disabilitato o bloccato.
Parametri di query (solo elenco)
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
q | string | No | Filtra per nome gruppo (facoltativo, corrispondenza parziale). |
page | integer | No | Numero di pagina (predefinito 1). |
per_page | integer | No | Risultati per pagina, 1–100 (predefinito 20). |
sort | string | No | Colonna di ordinamento: name | slug | members | torrents | created (predefinito name). |
order | string | No | Direzione di ordinamento: asc o desc (predefinito asc). |
Risposta (elenco)
{
"total": 12,
"page": 1,
"per_page": 20,
"total_pages": 1,
"groups": [
{
"id": 1,
"name": "SubsPlease",
"slug": "subsplease",
"subdomain_slug": "subsplease",
"description": "Weekly simulcast batches.",
"owner": "admin",
"member_count": 42,
"torrent_count": 1337,
"created_at": "2024-01-15 12:34"
}
]
}Risposta (singolo)
{
"id": 1,
"name": "SubsPlease",
"slug": "subsplease",
"subdomain_slug": "subsplease",
"description": "Weekly simulcast batches.",
"owner": "admin",
"member_count": 42,
"torrent_count": 1337,
"created_at": "2024-01-15 12:34"
}Risposte di errore
| Stato HTTP | Significato |
|---|---|
401 | Bearer token mancante, scaduto o già ruotato. Ri-autenticati tramite POST /api/v1/auth/token. |
404 | Gruppo non trovato o non accessibile pubblicamente. |
429 | Limite di frequenza superato. Riprova dopo il reset della finestra. |
503 | Il sito è in modalità manutenzione o sola lettura. |
8. Donazioni
Se desideri supportare AniRena e contribuire a coprire i costi di hosting dei nostri server e servizi, puoi inviare una donazione a uno dei seguenti portafogli di criptovalute:
bc1qy2h3ddq6ak5damvnf4r5vu3ydehhxrcq8gllwn0xCbaFe03832F95F86AF2536d52710e78C63b62Cd33ucetj2XDGHQg9PVRPMxerNi7c6kX7GJkjQNg9yjwGegLbpt61yX3RjGtB1Ef8vgVz6Hr6baQsTjVkQualsiasi donazione, grande o piccola, è molto apprezzata e va direttamente al mantenimento di AniRena. Grazie per il tuo supporto!
9. Software
AniRena Player è un'app desktop gratuita che consente di trasmettere video direttamente dai torrent indicizzati su questo sito — senza dover attendere il completamento dell'intero download. Basta incollare un link magnet o aprire un file .torrent, e la riproduzione inizia non appena sono disponibili dati sufficienti.
Entrambe le versioni sono completamente autonome — tutte le dipendenze sono incluse all'interno dell'eseguibile. Nessun installer, nessun ambiente di runtime — basta scaricare ed eseguire.
Programma di installazione (.exe). Si aggiorna da solo nell'app.
Immagine disco (.dmg) per Mac con Apple Silicon (M1 e successivi). Si aggiorna da solo nell'app.
Immagine disco (.dmg) per Mac con Intel. Si aggiorna da solo nell'app.
File singolo portatile, nessuna installazione necessaria. L'unico formato Linux con aggiornamento automatico nell'app.
Installazione: sudo apt install ./<file>.deb — aggiornamenti tramite apt o tramite un nuovo download, non nell'app.
Installazione: sudo dnf install ./<file>.rpm — aggiornamenti tramite dnf o tramite un nuovo download, non nell'app.
Sideload su dispositivi Android ARM a 64 bit (la maggior parte di telefoni / tablet moderni). Aggiornamenti scaricando un nuovo APK.
Versioni precedenti
Sideload su dispositivi Android ARM a 32 bit (telefoni / tablet meno recenti). Si aggiorna scaricando un nuovo APK.