Guia
1. Primeiros Passos
AniRena é um índice de torrents focado em anime, mangá, áudio e mídia relacionada. Você pode navegar e baixar torrents sem uma conta. Uma conta é necessária para enviar torrents, postar em grupos ou usar a API.
A barra de navegação no topo fornece acesso às principais áreas do site:
- Início — a página de listagem e pesquisa de torrents.
- Enviar — enviar um novo torrent (requer login).
- Guia — esta página.
- Estatísticas — estatísticas de todo o site (torrents, peers, uploads ao longo do tempo).
- Grupos — diretório de grupos de lançamento.
- RSS — feed RSS de uploads recentes, filtrável por categoria.
Seu menu de conta (canto superior direito quando conectado) abre um painel de perfil onde você pode ajustar configurações, gerenciar opções de segurança e acessar sua chave de API.
2. Navegando & Pesquisando
A página inicial lista todos os torrents ordenados por data de upload. Use a barra de pesquisa no topo para filtrar resultados.
Pesquisa básica
Digite quaisquer palavras na barra de pesquisa e pressione Enter (ou clique no ícone de pesquisa). Os resultados são classificados por relevância quando uma consulta está ativa.
Operadores de pesquisa
Os seguintes operadores podem ser combinados com uma consulta regular:
| Operador | Exemplo | Efeito |
|---|---|---|
user:"name" | user:"SubsPlease" | Mostrar apenas torrents enviados por esse usuário. |
Clicar em um nome de enviador na lista de torrents executa automaticamente uma pesquisa de usuário para você.
Categorias & subcategorias
Use o seletor de categoria (o ícone de grade próximo à barra de pesquisa) para restringir resultados a uma categoria. As categorias disponíveis são:
- Anime
- Manga/Manhwa/Quadrinhos
- Áudio
- Literatura
- Live Action
- Imagens
- Software
- Hentai
- Outros
Cada categoria tem subcategorias (ex. Anime para RAW, Sub/Áudio, Vídeo Musical) selecionáveis dentro do modal de categoria.
Ordenação & filtros
Os cabeçalhos de coluna na lista de torrents são clicáveis para ordenar por essa coluna (crescente ou decrescente). Colunas de ordenação disponíveis: data, nome, tamanho, downloads concluídos. Observação: seeders e leechers são valores em tempo real do Redis e não podem ser usados para ordenação.
Filtro de idioma
Use o seletor de idioma (ícone de bandeira) para mostrar apenas torrents marcados com um idioma específico.
Feed RSS
O feed RSS em /rss fornece os uploads mais recentes. Adicione ?category=anime (ou qualquer outro slug de categoria) para filtrar o feed. A maioria dos clientes de torrent suporta download automático via RSS diretamente desta URL.
3. Baixando Torrents
Clique em qualquer nome de torrent para abrir seu painel de detalhes. A partir daí você pode:
- Baixar .torrent — salva o arquivo .torrent diretamente. A URL direta é
/torrents/{id}.torrent - Link magnético — abre diretamente no seu cliente de torrent via protocolo de URI magnético. A URL é
/torrents/{id}/magnet
O painel de detalhes também mostra a descrição do torrent, lista de arquivos, lista de trackers e contagens de seeders/leechers.
Links de download legados
Os links de download antigos do AniRena ainda são suportados e redirecionam automaticamente para o arquivo .torrent correto usando o ID legado: /dl/{old_id}
Clientes BitTorrent recomendados
Qualquer cliente BitTorrent moderno funciona. Os clientes abaixo são recomendados e suportam totalmente BitTorrent v2 / torrents híbridos:
4. Criando uma Conta
Registro
Clique em Cadastrar na barra de navegação. Escolha um nome de usuário, forneça um endereço de e-mail e defina uma senha (comprimento mínimo imposto). Você deve ler e aceitar os termos do site antes que sua conta seja criada.
Ativação por e-mail
Após o cadastro, um e-mail de verificação é enviado para seu endereço. Clique no link no e-mail para ativar sua conta. Se você não recebeu, use o link Ativar sua conta na página de login para solicitar um novo código.
Recuperação de senha
Se você esquecer sua senha, clique em Esqueceu a senha na página de login e digite seu endereço de e-mail. Um link de recuperação será enviado para você. O link é de uso único e expira após um curto período.
5. Enviando Torrents
Navegue até Enviar na barra de navegação. Você deve estar conectado com uma conta ativa e não banida. A página de envio tem duas abas:
Aba Enviar — enviar um arquivo .torrent existente
Arraste e solte ou selecione um arquivo .torrent. Uma vez carregado, preencha os campos:
| Campo | Obrigatório | Descrição |
|---|---|---|
| Arquivo torrent | Sim | O arquivo .torrent a ser enviado. |
| Nome | Não | Substituir o nome de exibição do torrent. Se deixado em branco, o nome embutido no arquivo torrent é usado. |
| Categoria | Sim | A categoria de conteúdo (Anime, Mangá, Áudio, etc.). |
| Subcategoria | Não | Um tipo mais específico dentro da categoria (ex. RAW, Sub/Áudio). |
| Idiomas | Não | Uma ou mais tags de idioma descrevendo o idioma do conteúdo. |
| Grupo | Não | Associar este lançamento a um grupo do qual você é membro. |
| Descrição | Não | Descrição formatada em Markdown mostrada na página de detalhes do torrent (máx 65535 caracteres). |
| Privado | Não | Define a flag privada no torrent, desativando DHT/PEX. Útil para torrents somente de tracker. |
| URL de Announce | Não | Substituir ou adicionar a URL de announce do tracker primário. |
| Trackers extras | Não | Lido do arquivo torrent. Não pode ser modificado durante o envio — use a aba Criar se quiser personalizar a lista de trackers. |
| Comentário | Não | Substituir o campo de comentário do torrent embutido no arquivo. |
Seu torrent deve incluir pelo menos uma URL de tracker do AniRena na lista de announce (em qualquer tier). O site verifica isso no envio e rejeitará torrents que não incluam um tracker do AniRena. Se você criou o torrent sem adicionar o tracker do AniRena primeiro, envie-o e depois baixe-o novamente do site — o arquivo baixado terá os trackers corretos injetados automaticamente.
Aba Criar — construir um novo torrent
A aba Criar permite gerar um novo .torrent do zero especificando caminhos de arquivo, URLs de tracker e outros parâmetros de torrent diretamente no navegador. O torrent resultante é enviado com os mesmos campos de metadados acima.
Moderação
Os uploads são automaticamente verificados contra uma lista de padrões de conteúdo banidos (nomes, nomes de arquivo, descrições). Torrents que correspondem a um padrão banido serão rejeitados. Torrents duplicados (mesmo info hash) também são rejeitados.
6. Sua Conta
Clique em seu nome de usuário no canto superior direito para abrir o painel de perfil. Está organizado em seções expansíveis:
Configurações
Altere o tema da UI, tamanho da fonte, esquema de cores, idioma da interface e preferências de exibição relacionadas a torrents. As alterações são salvas automaticamente.
Senha
Digite sua senha atual e a nova senha duas vezes. Um código de verificação é enviado para seu endereço de e-mail registrado e deve ser inserido para confirmar a alteração. Se a autenticação de dois fatores estiver ativada, seu código TOTP também é necessário.
Autenticação de Dois Fatores (2FA)
Ative a autenticação de dois fatores baseada em TOTP usando qualquer aplicativo autenticador (ex. Google Authenticator, Aegis, Bitwarden). Ao ativar o 2FA:
- Escaneie o código QR (ou insira o segredo manualmente) no seu aplicativo autenticador.
- Digite o código de 6 dígitos mostrado no seu aplicativo para confirmar a configuração.
- Salve os códigos de recuperação mostrados — estes são códigos de uso único para recuperar o acesso se você perder seu dispositivo.
Para desativar o 2FA, insira seu código TOTP atual e confirme.
Sessões Ativas
Veja todas as sessões de login atualmente ativas incluindo navegador, SO, endereço IP e hora do último acesso. Clique em Revogar em qualquer sessão que você não reconheça. Você também pode revogar todas as sessões de uma vez para desconectar todos os dispositivos.
Chave de API
Gere uma chave de API pessoal usada para enviar torrents programaticamente via API AniRena. Clique em Gerar Chave para criar uma — a chave completa é mostrada uma vez imediatamente após a geração. Armazene-a com segurança; ela não será mostrada em sua totalidade novamente. Use Revogar para invalidar permanentemente a chave.
Excluir Conta
Solicitar a exclusão de conta inicia um período de carência de 30 dias. Sua conta é desativada imediatamente e excluída permanentemente após 30 dias. Você pode cancelar a exclusão a qualquer momento dentro dessa janela fazendo login e clicando em Cancelar exclusão.
7. API AniRena
AniRena fornece uma API JSON que permite enviar torrents programaticamente usando uma chave de API pessoal. A API aplica as mesmas regras que a interface web: verificações de banimento, limites de taxa e restrições de modo do site se aplicam. Cada upload de API é registrado no log de auditoria.
Autenticação
A API usa um fluxo de autenticação em duas etapas. Primeiro, troque sua chave API permanente por um token portador de curta duração, e então passe esse token no cabeçalho Authorization de cada requisição API.
Sua chave API está disponível em Sua conta > Chave API. Mantenha-a em segredo — qualquer pessoa com a chave pode obter tokens portadores e fazer uploads em seu nome. Se for comprometida, revogue-a imediatamente e gere uma nova.
Etapa 1 — Obter um token portador
/api/v1/auth/tokenEnvie uma requisição POST ao endpoint do token com sua chave API no cabeçalho Authorization. Nenhum corpo de requisição é necessário.
Authorization: ApiKey <your-api-key>
Resposta do token
{
"token": "<bearer-token>",
"token_type": "Bearer",
"expires_in": 3600
}Tempo de vida do token
Os tokens Bearer permanecem válidos por até 3600 segundos a partir da emissão e podem ser reutilizados em cada chamada até expirarem. Quando um token expira, gere um novo via POST /api/v1/auth/token. Cada resposta ainda retorna o token atual no cabeçalho X-New-Token para compatibilidade com clientes existentes.
X-New-Token: <next-bearer-token>
Login em uma única requisição (com 2FA)
/api/v1/auth/loginAutentique-se com seu nome de usuário ou e-mail e senha em uma única requisição e receba um token bearer diretamente. Se a sua conta tiver 2FA ativado, inclua o código atual do seu autenticador em totp_code (ou um código de recuperação em recovery_code). Opcionalmente, defina new_api_key como true para também gerar uma nova chave de API permanente na mesma resposta.
Corpo da requisição
{
"login": "username or email",
"password": "your-password",
"totp_code": "123456", // obrigatório se o 2FA estiver ativado (6 dígitos)
"recovery_code": "", // alternativa ao totp_code
"new_api_key": false // defina true para também gerar uma nova chave de API
}Resposta do token
{
"ok": true,
"token": "<bearer-token>",
"token_type": "Bearer",
"expires_in": 3600,
"api_key": "<new-api-key>" // presente apenas quando new_api_key era true
}O token bearer funciona exatamente como um obtido em /api/v1/auth/token. O campo api_key só é retornado quando new_api_key é true — guarde-o imediatamente, pois ele é exibido apenas uma vez e substitui qualquer chave anterior.
Exemplo — faça login e (opcionalmente) obtenha uma nova chave de 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}"); } }
Etapa 2 — Fazer upload de um torrent
/api/v1/torrentsEnvie uma requisição POST JSON simples com o token portador no cabeçalho Authorization.
Corpo da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
torrent | string | Sim | Conteúdo do arquivo .torrent codificado em Base64. |
category | string | Sim | Slug de categoria: anime, manga, audio, literature, live, pictures, software, hentai, other. |
name | string | Não | Substituir o nome de exibição do torrent. |
sub_category | string | Não | Slug de subcategoria (ex. raw, sub-audio). Deve pertencer à categoria escolhida. |
languages | string[] | Não | Array de códigos de idioma BCP 47 (ex. en, ja). |
group_id | string | Não | UUID de um grupo do qual você é membro para associar este lançamento. |
description | string | Não | Descrição de lançamento formatada em Markdown (máx 65535 caracteres). |
comment | string | Não | Substituir o campo de comentário embutido do torrent. |
is_private | boolean | Não | Definir como true para ativar a flag privada no torrent. |
comments_enabled | boolean | Não | Permitir comentários neste torrent. O padrão é true (habilitado). |
anime_id | string | Não | UUID de uma entrada de anime para vincular a este torrent. Obtenha o UUID via GET /api/v1/anime/search. Retorna 400 se o UUID não corresponder a nenhuma entrada conhecida. |
announce | string | Não | Substituir ou adicionar a URL de announce primária. |
trackers | string | Não | Lista de URLs de tracker adicionais separadas por nova linha. Uma linha em branco entre URLs cria uma nova camada de tracker. |
test | boolean | Não | Defina como true para executar uma simulação: a solicitação é totalmente validada, mas o torrent não é salvo. Use isso para verificar se o seu payload está correto antes do envio real. |
Resposta de sucesso da simulação — 200 OK
{
"ok": true,
"test": true,
"name": "My Torrent Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null
}Códigos de idioma disponíveis
abaaafaksqamarar-001anhyasavaeayazbmbaeubebnbhbibsbrbgmyyuecachcenyzhzh-HKzh-Hanszh-SGzh-TWcucvkwcocrhrcsdadvnlnl-BEdzenen-USeoeteefofjfilfifrfr-CAffgllgkadede-ATelgnguhthahehzhihohuisioigidiaieiuikgaitjajvklknkrkskkkmkirwrnkvkgkokjkukylolalvlilnltlulbmkmgmsmlmtgvmimrmhmnnanvngnendsenonbnnocorojomospipsfaplptpt-BRpaqurormrusmsgsascgdsrsr-Latnsniisdsiskslsonrsteses-419es-MXsuswsssvtltytgtatttethbotitotstntrtktwukuruguzvevivowacyfywoxhyiyozazuExemplo de requisição
# 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 }
Resposta de sucesso — 200 OK
{
"ok": true,
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Torrent Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null
}Respostas de erro
| Status HTTP | Significado |
|---|---|
400 | Corpo da requisição inválido ou campo obrigatório ausente. |
401 | Token portador ausente, expirado ou já rotacionado. Re-autentique via POST /api/v1/auth/token. |
403 | Conta banida, desativada ou IP bloqueado. |
409 | Torrent duplicado — mesmo info hash já existe. |
422 | Arquivo torrent não pôde ser analisado ou falhou na validação (padrão banido, estrutura inválida). |
429 | Limite de taxa excedido. Tente novamente após a janela ser redefinida. |
503 | Site está em modo de manutenção ou somente leitura. |
Limite de taxa
Os uploads de API estão sujeitos a um limite de taxa configurável separado da interface web. O limite e a janela são definidos pelo administrador do site. Quando o limite de taxa é excedido, a API retorna 429 Too Many Requests. O limite é por chave de API.
Gerando arquivos torrent com torrent-builder
torrent-builder é uma ferramenta CLI de código aberto construída sobre libtorrent-rasterbar que permite criar arquivos .torrent BitTorrent v1, v2 e hybrid pela linha de comando. Combina perfeitamente com a AniRena upload API — gere o arquivo localmente e depois faça um POST diretamente para o tracker. cantalupo555/torrent-builder.
Compilar a partir do código-fonte
Requer CMake >= 3.28.3 e libtorrent-rasterbar >= 2.0.11. Clone o repositório e compile com 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 .
Flags principais
| Campo | Descrição |
|---|---|
--path | Caminho para o arquivo ou diretório a ser empacotado (obrigatório). |
--output | Nome do arquivo .torrent de saída (obrigatório). |
--version | Formato BitTorrent — 1 = v1, 2 = v2, 3 = hybrid (padrão: 3). |
--tracker | Adicionar uma URL de announce de tracker. Repita a flag para adicionar múltiplos trackers. |
--comment | Incorporar uma string de comentário de metadados no torrent. |
--private | Definir a flag privada para restringir a distribuição apenas aos trackers listados. |
--piece-size | Tamanho do pedaço em KB (16-32768). Deixe sem definir para seleção automática. |
-i | Iniciar o modo de configuração interativa passo a passo. |
Fluxo de trabalho completo: build -> upload
Os exemplos abaixo criam um torrent hybrid com torrent-builder, depois se autenticam com a AniRena API e fazem upload do resultado em um único 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"]); }
Pesquisando metadados de torrent
/api/v1/torrents/searchEnvie uma requisição POST JSON simples para recuperar listagens de torrents com as mesmas opções de pesquisa e filtro disponíveis no site. O próprio arquivo .torrent não é retornado — use a rota de download normal para isso.
# 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"]); }
Parâmetros de pesquisa
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
q | string | Não | Pesquisa de texto livre. Suporta prefixos group:slug, group:"Nome", user:nome. |
category | string | Não | Slug de categoria (ex. "anime"). |
sub_category | string | Não | Slug de subcategoria (ex. "raw"). |
languages | string[] | Não | Array de códigos de idioma BCP 47 (ex. en, ja). |
sort | string | Não | Campo de ordenação: date (padrão), size, seeders, leechers, completed, title. |
order | string | Não | Direção de ordenação: desc (padrão) ou asc. |
page | integer | Não | Número da página, começando em 1 (padrão 1). |
per_page | integer | Não | Resultados por página, 1–250 (padrão 50). |
hide_adult | boolean | Não | Excluir torrents de categoria adulta. Padrão true para usuários regulares. |
show_dead | boolean | Não | Quando false (padrão), torrents mais antigos que o período de carência de torrents mortos sem seeders ativos são excluídos. Defina como true para incluí-los. |
Resposta
{
"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 — Número de comentários não eliminados neste torrent.
Limite de taxa de pesquisa
As requisições de pesquisa estão sujeitas a um limite de taxa configurável separado (padrão 60 requisições por 60 segundos por chave de API). Exceder o limite retorna 429 Too Many Requests. Contas de staff são isentas.
Obter detalhes do torrent
/api/v1/torrent/{id}Recupera os metadados completos de um torrent — incluindo campos que o endpoint de busca omite, como a descrição em Markdown, o comentário embutido do .torrent, a lista de arquivos com o tamanho de cada um e o layout completo dos níveis de trackers. Quando disponíveis, as contagens ao vivo de seeders e leechers são lidas do tracker.
Resposta
{
"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 — Contagens ao vivo do tracker interno; ambos retornam 0 quando o armazenamento do tracker não possui entrada para este info hash ou está inacessível.
ext_seeders, ext_leechers — Maior número de seeders e leechers reportado por qualquer tracker externo individual rastreado para este torrent. Trackers que acompanham o mesmo swarm se sobrepõem, por isso usa-se o máximo em vez da soma; ambos retornam 0 quando nenhum tracker tem dados de scrape para este info hash.
Respostas de erro
| Status HTTP | Significado |
|---|---|
400 | O id do torrent deve ser um UUID de 36 caracteres com hifens ou uma string hexadecimal pura de 32 caracteres. |
401 | Token portador ausente, expirado ou já rotacionado. Re-autentique via POST /api/v1/auth/token. |
404 | Torrent não encontrado. |
429 | Limite de taxa excedido. Tente novamente após a janela ser redefinida. |
503 | Site está em modo de manutenção ou somente leitura. |
Buscar comentários do torrent
/api/v1/torrents/{id}/commentsRecuperar comentários paginados de um torrent. O número de comentários por página é controlado pela configuração COMMENT_PER_PAGE no arquivo .env do servidor (padrão 20). Apenas torrents com comentários habilitados retornarão resultados — todos os outros retornam 403.
Parâmetros de consulta
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
page | integer | Não | Número da página, começando em 1 (padrão 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"]); }
Resposta
{
"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
}
]
}O campo body é uma string vazia quando o autor do comentário está banido ou o comentário foi excluído. A flag author_banned indica qual caso se aplica.
Respostas de erro
| Status HTTP | Significado |
|---|---|
401 | Token portador ausente, expirado ou já rotacionado. Re-autentique via POST /api/v1/auth/token. |
403 | Os comentários estão desabilitados para este torrent. |
404 | Torrent não encontrado. |
503 | Site está em modo de manutenção ou somente leitura. |
Pesquisar entradas de anime
/api/v1/anime/search?q=<query>Pesquise entradas de anime por título para obter seu UUID. O UUID pode ser passado como anime_id no corpo do envio para vincular um torrent a uma entrada de anime no momento do envio, ou usado com PUT /api/torrents/{id}/anime após o envio. Nenhuma autenticação é necessária. Sujeito ao mesmo limite de taxa que a pesquisa de torrents (padrão 60 solicitações por 60 segundos por 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"]);
}Parâmetros de consulta
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
q | string | Sim | String de pesquisa de título (obrigatório). Correspondência com título e sinônimos. |
page | integer | Não | Número da página, começando em 1 (padrão 1). |
per_page | integer | Não | Resultados por página, 1–50 (padrão 10). |
Resposta
{
"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
}
]
}Listar e recuperar grupos
/api/v1/groupsRetorna uma lista paginada de grupos públicos (habilitados e não bloqueados). Requer autenticação Bearer token.
/api/v1/groups/{id_or_slug}Retorna um único grupo público por ID numérico ou slug. Retorna 404 se o grupo estiver desabilitado ou bloqueado.
Parâmetros de consulta (somente lista)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
q | string | Não | Filtrar por nome do grupo (opcional, correspondência de substring). |
page | integer | Não | Número da página (padrão 1). |
per_page | integer | Não | Resultados por página, 1–100 (padrão 20). |
sort | string | Não | Coluna de ordenação: name | slug | members | torrents | created (padrão name). |
order | string | Não | Direção de ordenação: asc ou desc (padrão asc). |
Resposta (lista)
{
"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"
}
]
}Resposta (única)
{
"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"
}Respostas de erro
| Status HTTP | Significado |
|---|---|
401 | Token portador ausente, expirado ou já rotacionado. Re-autentique via POST /api/v1/auth/token. |
404 | Grupo não encontrado ou não acessível publicamente. |
429 | Limite de taxa excedido. Tente novamente após a janela ser redefinida. |
503 | Site está em modo de manutenção ou somente leitura. |
8. Doações
Se desejar apoiar a AniRena e ajudar a cobrir os custos de hospedagem dos nossos servidores e serviços, você é bem-vindo para enviar uma doação para uma das seguintes carteiras de criptomoedas:
bc1qy2h3ddq6ak5damvnf4r5vu3ydehhxrcq8gllwn0xCbaFe03832F95F86AF2536d52710e78C63b62Cd33ucetj2XDGHQg9PVRPMxerNi7c6kX7GJkjQNg9yjwGegLbpt61yX3RjGtB1Ef8vgVz6Hr6baQsTjVkQualquer doação, grande ou pequena, é muito apreciada e vai diretamente para manter o AniRena funcionando. Obrigado pelo seu apoio!
9. Software
O AniRena Player é uma aplicação de desktop gratuita que permite fazer streaming de vídeo diretamente dos torrents indexados neste site — sem precisar esperar o download completo terminar. Basta colar um link magnet ou abrir um arquivo .torrent, e a reprodução começa assim que houver dados suficientes disponíveis.
Ambas as versões são totalmente autônomas — todas as dependências estão incluídas dentro do executável. Sem instalador, sem ambiente de execução para configurar — basta baixar e executar.
Instalador (.exe). Atualiza-se sozinho na aplicação.
Imagem de disco (.dmg) para Macs com Apple Silicon (M1 e mais recentes). Atualiza-se sozinho na aplicação.
Imagem de disco (.dmg) para Macs com Intel. Atualiza-se sozinho na aplicação.
Ficheiro único portátil, sem necessidade de instalação. O único formato Linux com atualização automática na aplicação.
Instalação: sudo apt install ./<file>.deb — atualizações via apt ou novo download, não na aplicação.
Instalação: sudo dnf install ./<file>.rpm — atualizações via dnf ou novo download, não na aplicação.
Faça sideload em dispositivos Android ARM de 64 bits (a maioria dos telefones / tablets modernos). Atualiza-se descarregando um novo APK.
Versões anteriores
Instale manualmente em dispositivos Android ARM de 32 bits (telefones / tablets mais antigos). Atualiza baixando um novo APK.