Guide
1. Démarrage rapide
AniRena est un index de torrents spécialisé dans l'anime, le manga, l'audio et les médias associés. Vous pouvez naviguer et télécharger des torrents sans compte. Un compte est nécessaire pour envoyer des torrents, poster dans des groupes ou utiliser l'API.
La barre de navigation en haut donne accès aux principales sections du site :
- Accueil — la liste des torrents et la page de recherche.
- Envoyer — soumettre un nouveau torrent (connexion requise).
- Guide — cette page.
- Statistiques — statistiques globales du site (torrents, pairs, envois au fil du temps).
- Groupes — répertoire des groupes de publication.
- RSS — flux RSS des derniers envois, filtrable par catégorie.
Votre menu de compte (coin supérieur droit lorsque vous êtes connecté) ouvre un panneau de profil où vous pouvez modifier les paramètres, gérer les options de sécurité et accéder à votre clé API.
2. Navigation et recherche
La page d'accueil liste tous les torrents par date d'envoi. Utilisez la barre de recherche en haut pour filtrer les résultats.
Recherche de base
Tapez des mots dans la barre de recherche et appuyez sur Entrée (ou cliquez sur l'icône de recherche). Les résultats sont classés par pertinence lorsqu'une requête est active.
Opérateurs de recherche
Les opérateurs suivants peuvent être combinés avec une requête normale :
| Opérateur | Exemple | Effet |
|---|---|---|
user:"name" | user:"SubsPlease" | Afficher uniquement les torrents uploadés par cet utilisateur. |
Cliquer sur un nom d'auteur dans la liste de torrents lance automatiquement une recherche par utilisateur.
Catégories et sous-catégories
Utilisez le sélecteur de catégorie (l'icône de grille à côté de la barre de recherche) pour restreindre les résultats à une catégorie. Les catégories disponibles sont :
- Anime
- Manga/Manhwa/Comic
- Audio
- Littérature
- Prise de vue réelle
- Images
- Logiciel
- Hentai
- Autre
Chaque catégorie possède des sous-catégories (ex. Anime vers RAW, Sub/Audio, Clip musical) sélectionnables dans la fenêtre de catégorie.
Tri et filtres
Les en-têtes de colonnes de la liste de torrents sont cliquables pour trier par cette colonne (croissant ou décroissant). Colonnes de tri disponibles : date, nom, taille, téléchargements complétés. Remarque : les seedeurs et leecheurs sont des valeurs en direct issues de Redis et ne peuvent pas être utilisés pour le tri.
Filtre de langue
Utilisez le sélecteur de langue (icône drapeau) pour afficher uniquement les torrents étiquetés avec une langue spécifique.
Flux RSS
Le flux RSS sur /rss fournit les derniers envois. Ajoutez ?category=anime (ou tout autre slug de catégorie) pour filtrer le flux. La plupart des clients torrent supportent le téléchargement automatique RSS directement depuis cette URL.
3. Télécharger des torrents
Cliquez sur un nom de torrent pour ouvrir son panneau de détails. Vous pouvez alors :
- Télécharger le .torrent — enregistre directement le fichier .torrent. L'URL directe est
/torrents/{id}.torrent - Lien magnet — s'ouvre directement dans votre client torrent via le protocole URI magnet. L'URL est
/torrents/{id}/magnet
Le panneau de détails affiche également la description du torrent, la liste des fichiers, la liste des trackers et les compteurs de seedeurs / leecheurs.
Liens de téléchargement anciens
Les anciens liens de téléchargement AniRena sont toujours pris en charge et redirigent automatiquement vers le bon fichier .torrent en utilisant l'ID hérité : /dl/{old_id}
Clients BitTorrent recommandés
N'importe quel client BitTorrent moderne fonctionne. Les clients ci-dessous sont recommandés et prennent entièrement en charge BitTorrent v2 / les torrents hybrides :
4. Créer un compte
Inscription
Cliquez sur S'inscrire dans la barre de navigation. Choisissez un nom d'utilisateur, fournissez une adresse e-mail et définissez un mot de passe (longueur minimale imposée). Vous devez lire et accepter les conditions du site avant la création du compte.
Activation par e-mail
Après l'inscription, un e-mail de vérification est envoyé à votre adresse. Cliquez sur le lien dans l'e-mail pour activer votre compte. Si vous ne l'avez pas reçu, utilisez le lien Activer votre compte sur la page de connexion pour demander un nouveau code.
Récupération de mot de passe
Si vous oubliez votre mot de passe, cliquez sur Mot de passe oublié sur la page de connexion et saisissez votre adresse e-mail. Un lien de récupération vous sera envoyé. Le lien est à usage unique et expire après un court délai.
5. Envoyer des torrents
Accédez à Envoyer dans la barre de navigation. Vous devez être connecté avec un compte actif et non banni. La page d'envoi comporte deux onglets :
Onglet Envoyer — soumettre un fichier .torrent existant
Glissez-déposez ou sélectionnez un fichier .torrent. Une fois chargé, remplissez les champs :
| Champ | Requis | Description |
|---|---|---|
| Fichier torrent | Oui | Le fichier .torrent à envoyer. |
| Nom | Non | Remplacer le nom d'affichage du torrent. Si laissé vide, le nom intégré dans le fichier torrent est utilisé. |
| Catégorie | Oui | La catégorie du contenu (Anime, Manga, Audio, etc.). |
| Sous-catégorie | Non | Un type plus spécifique dans la catégorie (ex. RAW, Sub/Audio). |
| Langues | Non | Une ou plusieurs balises de langue décrivant la langue du contenu. |
| Groupe | Non | Associer cette publication à un groupe dont vous êtes membre. |
| Description | Non | Description formatée en Markdown affichée sur la page de détails du torrent (max 65535 caractères). |
| Privé | Non | Active le drapeau privé dans le torrent, désactivant DHT/PEX. Utile pour les torrents réservés au tracker. |
| URL d'annonce | Non | Remplacer ou ajouter l'URL d'annonce du tracker principal. |
| Trackers supplémentaires | Non | Lu depuis le fichier torrent. Ne peut pas être modifié lors de l'envoi — utilisez l'onglet Créer si vous souhaitez personnaliser la liste des trackers. |
| Commentaire | Non | Remplacer le champ de commentaire intégré dans le fichier torrent. |
Votre torrent doit inclure au moins une URL de tracker AniRena dans sa liste d'announce (dans n'importe quel tier). Le site vérifie cela lors de l'envoi et rejettera les torrents qui n'incluent pas de tracker AniRena. Si vous avez créé le torrent sans ajouter d'abord le tracker AniRena, envoyez-le puis re-téléchargez-le depuis le site — le fichier téléchargé aura les bons trackers injectés automatiquement.
Onglet Créer — construire un nouveau torrent
L'onglet Créer vous permet de générer un nouveau .torrent de zéro en spécifiant les chemins de fichiers, les URLs de trackers et d'autres paramètres directement dans le navigateur. Le torrent résultant est soumis avec les mêmes champs de métadonnées qu'au-dessus.
Modération
Les envois sont automatiquement vérifiés par rapport à une liste de motifs de contenu interdits (noms, noms de fichiers, descriptions). Les torrents correspondant à un motif interdit seront rejetés. Les torrents en double (même hash info) sont également rejetés.
6. Votre compte
Cliquez sur votre nom d'utilisateur dans le coin supérieur droit pour ouvrir le panneau de profil. Il est organisé en sections repliables :
Paramètres
Changez le thème de l'interface, la taille de police, la palette de couleurs, la langue de l'interface et les préférences d'affichage liées aux torrents. Les modifications sont enregistrées automatiquement.
Mot de passe
Saisissez votre mot de passe actuel et le nouveau mot de passe deux fois. Un code de vérification est envoyé à votre adresse e-mail enregistrée et doit être saisi pour confirmer le changement. Si la double authentification est activée, votre code TOTP est également requis.
Double authentification (2FA)
Activez la double authentification TOTP avec n'importe quelle application d'authentification (ex. Google Authenticator, Aegis, Bitwarden). Lors de l'activation de la 2FA :
- Scannez le QR code (ou saisissez le secret manuellement) dans votre application d'authentification.
- Saisissez le code à 6 chiffres affiché dans votre application pour confirmer la configuration.
- Sauvegardez les codes de récupération affichés — ce sont des codes à usage unique pour retrouver l'accès si vous perdez votre appareil.
Pour désactiver la 2FA, saisissez votre code TOTP actuel et confirmez.
Sessions actives
Consultez toutes les sessions de connexion actives, y compris le navigateur, l'OS, l'adresse IP et la dernière connexion. Cliquez sur Révoquer pour toute session non reconnue. Vous pouvez également révoquer toutes les sessions en même temps pour vous déconnecter de tous les appareils.
Clé API
Générez une clé API personnelle pour envoyer des torrents par programme via l'API AniRena. Cliquez sur Générer une clé pour en créer une — la clé complète est affichée une seule fois immédiatement après la génération. Conservez-la en lieu sûr ; elle ne sera plus affichée en entier. Utilisez Révoquer pour l'invalider définitivement.
Supprimer le compte
La demande de suppression de compte déclenche une période de grâce de 30 jours. Votre compte est immédiatement désactivé et définitivement supprimé après 30 jours. Vous pouvez annuler la suppression à tout moment pendant cette période en vous connectant et en cliquant sur Annuler la suppression.
7. API AniRena
AniRena fournit une API JSON pour envoyer des torrents par programme via une clé API personnelle. L'API applique les mêmes règles que l'interface web : vérifications des interdictions, limites de débit et restrictions de mode du site s'appliquent. Chaque envoi via l'API est enregistré dans le journal d'audit.
Authentification
L'API utilise un flux d'authentification en deux étapes. Échangez d'abord votre clé API permanente contre un token porteur de courte durée, puis transmettez ce token dans l'en-tête Authorization de chaque requête API.
Votre clé API est disponible sous Votre compte > Clé API. Gardez-la secrète — quiconque la possède peut obtenir des tokens porteurs et envoyer des fichiers en votre nom. En cas de compromission, révoquez-la immédiatement et générez-en une nouvelle.
Étape 1 — Obtenir un token porteur
/api/v1/auth/tokenEnvoyez une requête POST à l'endpoint du token avec votre clé API dans l'en-tête Authorization. Aucun corps de requête n'est nécessaire.
Authorization: ApiKey <your-api-key>
Réponse du token
{
"token": "<bearer-token>",
"token_type": "Bearer",
"expires_in": 3600
}Durée de vie du jeton
Les jetons Bearer restent valides jusqu'à 3600 secondes après leur émission et peuvent être réutilisés pour chaque appel jusqu'à leur expiration. Lorsqu'un jeton expire, créez-en un nouveau via POST /api/v1/auth/token. Chaque réponse renvoie toujours le jeton actuel dans l'en-tête X-New-Token pour la compatibilité ascendante.
X-New-Token: <next-bearer-token>
Connexion en une seule requête (avec 2FA)
/api/v1/auth/loginAuthentifiez-vous avec votre nom d'utilisateur ou e-mail et votre mot de passe en une seule requête et recevez directement un jeton bearer. Si votre compte a la 2FA activée, incluez le code actuel de votre application d'authentification dans totp_code (ou un code de récupération dans recovery_code). Vous pouvez aussi définir new_api_key sur true pour générer, dans la même réponse, une toute nouvelle clé API permanente.
Corps de la requête
{
"login": "username or email",
"password": "your-password",
"totp_code": "123456", // requis si la 2FA est activée (6 chiffres)
"recovery_code": "", // alternative à totp_code
"new_api_key": false // mettez true pour générer aussi une nouvelle clé API
}Réponse du token
{
"ok": true,
"token": "<bearer-token>",
"token_type": "Bearer",
"expires_in": 3600,
"api_key": "<new-api-key>" // présent uniquement quand new_api_key valait true
}Le jeton bearer fonctionne exactement comme celui obtenu via /api/v1/auth/token. Le champ api_key n'est renvoyé que lorsque new_api_key vaut true — enregistrez-le immédiatement, car il n'est affiché qu'une seule fois et remplace toute clé précédente.
Exemple — se connecter et (en option) obtenir une nouvelle clé 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}"); } }
Étape 2 — Uploader un torrent
/api/v1/torrentsEnvoyez une requête POST JSON simple avec le token porteur dans l'en-tête Authorization.
Corps de la requête
| Champ | Type | Requis | Description |
|---|---|---|---|
torrent | string | Oui | Contenu du fichier .torrent encodé en Base64. |
category | string | Oui | Slug de catégorie : anime, manga, audio, literature, live, pictures, software, hentai, other. |
name | string | Non | Remplacer le nom d'affichage du torrent. |
sub_category | string | Non | Slug de sous-catégorie (ex. raw, sub-audio). Doit appartenir à la catégorie choisie. |
languages | string[] | Non | Tableau de codes de langue BCP 47 (ex. en, ja). |
group_id | string | Non | UUID d'un groupe dont vous êtes membre pour associer cette publication. |
description | string | Non | Description de la publication formatée en Markdown (max 65535 caractères). |
comment | string | Non | Remplacer le champ de commentaire intégré dans le torrent. |
is_private | boolean | Non | Mettre à true pour activer le drapeau privé dans le torrent. |
comments_enabled | boolean | Non | Autoriser les commentaires sur ce torrent. Par défaut true (activé). |
anime_id | string | Non | UUID d'une entrée anime à associer à ce torrent. Obtenez l'UUID via GET /api/v1/anime/search. Renvoie 400 si l'UUID ne correspond à aucune entrée connue. |
announce | string | Non | Remplacer ou ajouter l'URL d'annonce principale. |
trackers | string | Non | Liste d'URLs de trackers supplémentaires séparées par des sauts de ligne. Une ligne vide entre les URLs crée un nouveau niveau de tracker. |
test | boolean | Non | Définissez sur true pour effectuer une simulation : la requête est entièrement validée mais le torrent n'est pas enregistré. Utilisez-le pour vérifier que votre charge utile est correcte avant une soumission réelle. |
Réponse de succès en simulation — 200 OK
{
"ok": true,
"test": true,
"name": "My Torrent Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null
}Codes de langue disponibles
abaaafaksqamarar-001anhyasavaeayazbmbaeubebnbhbibsbrbgmyyuecachcenyzhzh-HKzh-Hanszh-SGzh-TWcucvkwcocrhrcsdadvnlnl-BEdzenen-USeoeteefofjfilfifrfr-CAffgllgkadede-ATelgnguhthahehzhihohuisioigidiaieiuikgaitjajvklknkrkskkkmkirwrnkvkgkokjkukylolalvlilnltlulbmkmgmsmlmtgvmimrmhmnnanvngnendsenonbnnocorojomospipsfaplptpt-BRpaqurormrusmsgsascgdsrsr-Latnsniisdsiskslsonrsteses-419es-MXsuswsssvtltytgtatttethbotitotstntrtktwukuruguzvevivowacyfywoxhyiyozazuExemple de requête
# 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 }
Réponse de succès — 200 OK
{
"ok": true,
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Torrent Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null
}Réponses d'erreur
| Statut HTTP | Signification |
|---|---|
400 | Corps de requête invalide ou champ requis manquant. |
401 | Token porteur manquant, expiré ou déjà tourné. Réauthentifiez-vous via POST /api/v1/auth/token. |
403 | Compte banni, désactivé ou IP bloquée. |
409 | Torrent en double — le même hash info existe déjà. |
422 | Le fichier torrent n'a pas pu être analysé ou a échoué à la validation (motif interdit, structure invalide). |
429 | Limite de débit dépassée. Réessayez après la réinitialisation de la fenêtre. |
503 | Le site est en maintenance ou en mode lecture seule. |
Limitation de débit
Les envois via l'API sont soumis à une limite de débit configurable séparée de l'interface web. La limite et la fenêtre sont définies par l'administrateur du site. Lorsque la limite de débit est dépassée, l'API retourne 429 Too Many Requests. La limite est par clé API.
Générer des fichiers torrent avec torrent-builder
torrent-builder est un outil CLI open source construit sur libtorrent-rasterbar qui vous permet de créer des fichiers .torrent BitTorrent v1, v2 et hybrid en ligne de commande. Il s'intègre parfaitement avec l'AniRena upload API — générez le fichier localement, puis faites un POST directement au tracker. cantalupo555/torrent-builder.
Compiler depuis les sources
Nécessite CMake >= 3.28.3 et libtorrent-rasterbar >= 2.0.11. Clonez le dépôt et compilez avec 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 .
Options principales
| Champ | Description |
|---|---|
--path | Chemin vers le fichier ou le répertoire à packager (requis). |
--output | Nom du fichier .torrent de sortie (requis). |
--version | Format BitTorrent — 1 = v1, 2 = v2, 3 = hybrid (défaut : 3). |
--tracker | Ajouter une URL d'annonce de tracker. Répétez l'option pour ajouter plusieurs trackers. |
--comment | Intégrer une chaîne de commentaire de métadonnées dans le torrent. |
--private | Définir le drapeau privé pour restreindre la distribution aux trackers listés uniquement. |
--piece-size | Taille des pièces en Ko (16-32768). Laisser non défini pour la sélection automatique. |
-i | Lancer le mode de configuration interactive étape par étape. |
Flux de travail complet : build -> upload
Les exemples ci-dessous créent un torrent hybrid avec torrent-builder, s'authentifient ensuite auprès de l'AniRena API et téléversent le résultat dans un seul 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"]); }
Rechercher des métadonnées de torrents
/api/v1/torrents/searchEnvoyez une requête POST JSON simple pour récupérer des listes de torrents avec les mêmes options de recherche et de filtrage disponibles sur le site web. Le fichier .torrent lui-même n'est pas retourné — utilisez la route de téléchargement normale pour cela.
# 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"]); }
Paramètres de recherche
| Champ | Type | Requis | Description |
|---|---|---|---|
q | string | Non | Recherche en texte libre. Supporte les préfixes group:slug, group:"Nom", user:nom. |
category | string | Non | Slug de catégorie (ex. « anime »). |
sub_category | string | Non | Slug de sous-catégorie (ex. « raw »). |
languages | string[] | Non | Tableau de codes de langue BCP 47 (ex. en, ja). |
sort | string | Non | Champ de tri : date (défaut), size, seeders, leechers, completed, title. |
order | string | Non | Direction de tri : desc (défaut) ou asc. |
page | integer | Non | Numéro de page, à partir de 1 (défaut 1). |
per_page | integer | Non | Résultats par page, 1–250 (défaut 50). |
hide_adult | boolean | Non | Exclure les torrents de catégories adultes. Par défaut true pour les utilisateurs réguliers. |
show_dead | boolean | Non | Lorsque false (par défaut), les torrents plus anciens que la période de grâce des torrents morts et sans seeders actifs sont exclus. Définissez sur true pour les inclure. |
Réponse
{
"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 — Nombre de commentaires non supprimés sur ce torrent.
Limitation de débit pour la recherche
Les requêtes de recherche sont soumises à une limite de débit configurable séparée (par défaut 60 requêtes par 60 secondes par clé API). Dépasser la limite retourne 429 Too Many Requests. Les comptes staff sont exemptés.
Obtenir les détails du torrent
/api/v1/torrent/{id}Récupère les métadonnées complètes d'un torrent unique — y compris les champs que le point de terminaison de recherche omet, comme la description Markdown, le commentaire .torrent intégré, la liste des fichiers avec leur taille, et la disposition complète des niveaux de trackers. Les compteurs en direct de seeders et leechers sont lus depuis le tracker lorsqu'ils sont disponibles.
Réponse
{
"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 — Compteurs en direct du tracker interne ; les deux renvoient 0 lorsque le magasin du tracker n'a pas d'entrée pour ce info hash ou est inaccessible.
ext_seeders, ext_leechers — Nombre maximal de seeders et leechers signalé par un seul tracker externe scrapé pour ce torrent. Les trackers suivant le même swarm se chevauchent, donc le maximum est utilisé plutôt que la somme ; les deux retournent 0 lorsqu'aucun tracker ne dispose de données de scrape pour ce info hash.
Réponses d'erreur
| Statut HTTP | Signification |
|---|---|
400 | L'identifiant du torrent doit être un UUID de 36 caractères avec tirets ou une chaîne hexadécimale brute de 32 caractères. |
401 | Token porteur manquant, expiré ou déjà tourné. Réauthentifiez-vous via POST /api/v1/auth/token. |
404 | Torrent introuvable. |
429 | Limite de débit dépassée. Réessayez après la réinitialisation de la fenêtre. |
503 | Le site est en maintenance ou en mode lecture seule. |
Récupérer les commentaires d'un torrent
/api/v1/torrents/{id}/commentsRécupérer les commentaires paginés d'un torrent. Le nombre de commentaires par page est contrôlé par le paramètre COMMENT_PER_PAGE dans le fichier .env du serveur (par défaut 20). Seuls les torrents avec les commentaires activés retourneront des résultats — tous les autres retournent 403.
Paramètres de requête
| Champ | Type | Requis | Description |
|---|---|---|---|
page | integer | Non | Numéro de page, à partir de 1 (par défaut 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"]); }
Réponse
{
"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
}
]
}Le champ body est une chaîne vide lorsque l'auteur du commentaire est banni ou que le commentaire a été supprimé. Le drapeau author_banned indique le cas applicable.
Réponses d'erreur
| Statut HTTP | Signification |
|---|---|
401 | Token porteur manquant, expiré ou déjà tourné. Réauthentifiez-vous via POST /api/v1/auth/token. |
403 | Les commentaires sont désactivés pour ce torrent. |
404 | Torrent introuvable. |
503 | Le site est en maintenance ou en mode lecture seule. |
Rechercher des entrées anime
/api/v1/anime/search?q=<query>Recherchez des entrées anime par titre pour obtenir leur UUID. L'UUID peut être transmis en tant que anime_id dans le corps de l'envoi pour lier un torrent à une entrée anime lors de l'envoi, ou utilisé avec PUT /api/torrents/{id}/anime après l'envoi. Aucune authentification n'est requise. Soumis à la même limite de débit que la recherche de torrents (par défaut 60 requêtes par 60 secondes par 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"]);
}Paramètres de requête
| Champ | Type | Requis | Description |
|---|---|---|---|
q | string | Oui | Chaîne de recherche de titre (requise). Correspond au titre et aux synonymes. |
page | integer | Non | Numéro de page, à partir de 1 (défaut 1). |
per_page | integer | Non | Résultats par page, 1–50 (défaut 10). |
Réponse
{
"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
}
]
}Lister et récupérer des groupes
/api/v1/groupsRetourne une liste paginée de groupes publics (activés et non verrouillés). Nécessite une authentification Bearer token.
/api/v1/groups/{id_or_slug}Retourne un seul groupe public par ID numérique ou slug. Retourne 404 si le groupe est désactivé ou verrouillé.
Paramètres de requête (liste uniquement)
| Champ | Type | Requis | Description |
|---|---|---|---|
q | string | Non | Filtrer par nom de groupe (optionnel, correspondance partielle). |
page | integer | Non | Numéro de page (défaut 1). |
per_page | integer | Non | Résultats par page, 1–100 (défaut 20). |
sort | string | Non | Colonne de tri : name | slug | members | torrents | created (défaut name). |
order | string | Non | Direction de tri : asc ou desc (défaut asc). |
Réponse (liste)
{
"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"
}
]
}Réponse (unique)
{
"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"
}Réponses d'erreur
| Statut HTTP | Signification |
|---|---|
401 | Token porteur manquant, expiré ou déjà tourné. Réauthentifiez-vous via POST /api/v1/auth/token. |
404 | Groupe introuvable ou non accessible publiquement. |
429 | Limite de débit dépassée. Réessayez après la réinitialisation de la fenêtre. |
503 | Le site est en maintenance ou en mode lecture seule. |
8. Dons
Si vous souhaitez soutenir AniRena et aider à couvrir les coûts d'hébergement de nos serveurs et services, nous vous invitons à envoyer un don à l'un des portefeuilles de cryptomonnaies suivants :
bc1qy2h3ddq6ak5damvnf4r5vu3ydehhxrcq8gllwn0xCbaFe03832F95F86AF2536d52710e78C63b62Cd33ucetj2XDGHQg9PVRPMxerNi7c6kX7GJkjQNg9yjwGegLbpt61yX3RjGtB1Ef8vgVz6Hr6baQsTjVkTout don, grand ou petit, est très apprécié et va directement à maintenir AniRena en fonctionnement. Merci pour votre soutien !
9. Logiciels
AniRena Player est une application de bureau gratuite qui vous permet de diffuser des vidéos directement depuis les torrents indexés sur ce site — sans attendre la fin du téléchargement complet. Il suffit de coller un lien magnet ou d'ouvrir un fichier .torrent, et la lecture commence dès que suffisamment de données sont disponibles.
Les deux versions sont entièrement autonomes — toutes les dépendances sont intégrées dans l'exécutable. Pas d'installateur, pas d'environnement d'exécution à configurer — il suffit de télécharger et de lancer.
Programme d'installation (.exe). Se met à jour automatiquement dans l'application.
Image disque (.dmg) pour les Mac Apple Silicon (M1 et plus récents). Se met à jour automatiquement dans l'application.
Image disque (.dmg) pour les Mac Intel. Se met à jour automatiquement dans l'application.
Fichier unique portable, aucune installation requise. Le seul format Linux avec mise à jour automatique dans l'application.
Installation : sudo apt install ./<file>.deb — mise à jour via apt ou via un nouveau téléchargement, pas dans l'application.
Installation : sudo dnf install ./<file>.rpm — mise à jour via dnf ou via un nouveau téléchargement, pas dans l'application.
Sideload sur les appareils Android ARM 64 bits (la plupart des téléphones / tablettes modernes). Mise à jour en téléchargeant un nouvel APK.
Versions antérieures
Sideload sur les appareils Android ARM 32 bits (téléphones / tablettes plus anciens). Mise à jour en téléchargeant un nouvel APK.