Довідка
1. Початок роботи
AniRena — це індекс торентів, орієнтований на аніме, мангу, аудіо та пов'язані медіа. Ви можете переглядати та завантажувати торенти без акаунту. Акаунт необхідний для завантаження торентів, публікацій у групах або використання API.
Навігаційна панель у верхній частині надає доступ до основних розділів сайту:
- Головна — сторінка списку торентів та пошуку.
- Завантажити — надіслати новий торент (необхідний вхід).
- Довідка — ця сторінка.
- Статистика — загальносайтова статистика (торенти, однолітки, завантаження з часом).
- Групи — каталог груп релізів.
- RSS — RSS-стрічка останніх завантажень, яку можна фільтрувати за категорією.
Меню вашого акаунту (верхній правий кут при вході) відкриває панель профілю, де можна налаштувати параметри, керувати параметрами безпеки та отримати доступ до вашого API-ключа.
2. Перегляд та пошук
Головна сторінка містить список усіх торентів, упорядкованих за датою завантаження. Використайте рядок пошуку вгорі для фільтрації результатів.
Базовий пошук
Введіть будь-які слова в рядок пошуку та натисніть Enter (або клацніть значок пошуку). Результати впорядковуються за релевантністю, коли запит активний.
Оператори пошуку
Наступні оператори можна поєднувати з регулярним запитом:
| Оператор | Приклад | Ефект |
|---|---|---|
user:"name" | user:"SubsPlease" | Показувати лише торенти, завантажені цим користувачем. |
Натискання на ім'я завантажувача у списку торентів автоматично виконує пошук за користувачем.
Категорії та підкатегорії
Використайте вибір категорії (значок сітки біля рядка пошуку) для обмеження результатів однією категорією. Доступні категорії:
- Аніме
- Манга/Манхва/Комікс
- Аудіо
- Література
- Живий екшн
- Зображення
- Програмне забезпечення
- Хентай
- Інше
Кожна категорія має підкатегорії (напр. Аніме: RAW, Субтитри/Аудіо, Музичне відео), які можна вибрати всередині модального вікна категорії.
Сортування та фільтри
Заголовки стовпців у списку торентів можна натискати для сортування за цим стовпцем (за зростанням або спаданням). Доступні стовпці сортування: дата, назва, розмір, завершені завантаження. Примітка: сідери та лічери — це живі значення з Redis, і їх не можна використовувати для сортування.
Фільтр мови
Використайте вибір мови (значок прапора) для відображення лише торентів із мітками певної мови.
RSS-стрічка
RSS-стрічка за адресою /rss містить останні завантаження. Додайте ?category=anime (або будь-який інший ярлик категорії) для фільтрації стрічки. Більшість торент-клієнтів підтримують автоматичне завантаження RSS безпосередньо з цього URL.
3. Завантаження торентів
Натисніть на будь-яку назву торента для відкриття панелі деталей. Звідси ви можете:
- Завантажити .torrent — зберігає .torrent файл безпосередньо. Прямий URL:
/torrents/{id}.torrent - Магнет-посилання — відкривається безпосередньо у вашому торент-клієнті через протокол URI магнету. URL:
/torrents/{id}/magnet
Панель деталей також показує опис торента, список файлів, список трекерів та кількість сідерів/лічерів.
Застарілі посилання для завантаження
Старі посилання для завантаження AniRena все ще підтримуються та автоматично перенаправляють до правильного .torrent файлу за застарілим ID: /dl/{old_id}
Рекомендовані клієнти BitTorrent
Підійде будь-який сучасний клієнт BitTorrent. Наведені нижче клієнти рекомендовані і повністю підтримують BitTorrent v2 / гібридні торенти:
4. Створення акаунту
Реєстрація
Натисніть «Реєстрація» у навігаційній панелі. Виберіть ім'я користувача, вкажіть email-адресу та встановіть пароль (мінімальна довжина обов'язкова). Ви повинні прочитати та прийняти умови сайту перед створенням акаунту.
Активація email
Після реєстрації на вашу адресу надсилається лист підтвердження. Натисніть посилання в листі для активації акаунту. Якщо ви його не отримали, використайте посилання «Активувати акаунт» на сторінці входу для запиту нового коду.
Відновлення пароля
Якщо ви забули пароль, натисніть «Забули пароль» на сторінці входу та введіть вашу email-адресу. Вам буде надіслано посилання для відновлення. Посилання є одноразовим та дійсним протягом короткого часу.
5. Завантаження торентів
Перейдіть до «Завантажити» у навігаційній панелі. Ви повинні бути авторизовані з активним незаблокованим акаунтом. Сторінка завантаження має дві вкладки:
Вкладка Завантажити — надіслати існуючий .torrent файл
Перетягніть або виберіть .torrent файл. Після завантаження заповніть поля:
| Поле | Обов'язкове | Опис |
|---|---|---|
| Файл торента | Так | Файл .torrent для завантаження. |
| Назва | Ні | Замінити відображувану назву торента. Якщо залишити порожнім, використовується назва, вбудована у файл торента. |
| Категорія | Так | Категорія вмісту (Аніме, Манга, Аудіо тощо). |
| Підкатегорія | Ні | Більш конкретний тип в межах категорії (напр. RAW, Субтитри/Аудіо). |
| Мови | Ні | Один або більше мовних тегів, що описують мову вмісту. |
| Група | Ні | Пов'язати цей реліз з групою, учасником якої ви є. |
| Опис | Ні | Опис у форматі Markdown, що відображається на сторінці деталей торента (максимум 65535 символів). |
| Приватний | Ні | Встановлює прапор приватності в торенті, вимикаючи DHT/PEX. Корисно для торентів лише для трекерів. |
| URL оголошення | Ні | Замінити або додати первинний URL оголошення трекера. |
| Додаткові трекери | Ні | Зчитується з файлу торента. Не можна змінити під час завантаження — використовуйте вкладку «Створити», щоб налаштувати список трекерів. |
| Коментар | Ні | Замінити поле коментаря торента, вбудоване у файл. |
Ваш торент повинен містити принаймні одну URL-адресу трекера AniRena у списку announce (на будь-якому рівні). Сайт перевіряє це при завантаженні і відхилить торенти, які не містять трекера AniRena. Якщо ви створили торент без додавання трекера AniRena, завантажте його, а потім повторно завантажте з сайту — завантажений файл автоматично міститиме правильні трекери.
Вкладка Створити — побудувати новий торент
Вкладка «Створити» дозволяє створити новий .torrent з нуля, вказавши шляхи до файлів, URL трекерів та інші параметри торента безпосередньо в браузері. Отриманий торент надсилається з тими самими полями метаданих, що й вище.
Модерація
Завантаження автоматично перевіряються за списком шаблонів забороненого вмісту (назви, імена файлів, описи). Торенти, що відповідають забороненому шаблону, будуть відхилені. Дублікати торентів (той самий info hash) також відхиляються.
6. Ваш акаунт
Натисніть ваше ім'я користувача у верхньому правому куті для відкриття панелі профілю. Вона організована у складні розділи:
Налаштування
Змінюйте тему інтерфейсу, розмір шрифту, колірну схему, мову інтерфейсу та налаштування відображення, пов'язані з торентами. Зміни зберігаються автоматично.
Пароль
Введіть ваш поточний пароль та новий пароль двічі. Код підтвердження надсилається на вашу зареєстровану email-адресу та повинен бути введений для підтвердження зміни. Якщо увімкнено двофакторну автентифікацію, також потрібен ваш TOTP-код.
Двофакторна автентифікація (2FA)
Увімкніть двофакторну автентифікацію на основі TOTP за допомогою будь-якого додатку автентифікатора (напр. Google Authenticator, Aegis, Bitwarden). При увімкненні 2FA:
- Відскануйте QR-код (або введіть секрет вручну) у вашому додатку автентифікатора.
- Введіть 6-значний код, показаний у вашому додатку, для підтвердження налаштування.
- Збережіть показані коди відновлення — це одноразові коди для відновлення доступу, якщо ви втратите ваш пристрій.
Для вимкнення 2FA введіть ваш поточний TOTP-код та підтвердіть.
Активні сесії
Переглядайте всі поточні активні сесії входу, включаючи браузер, ОС, IP-адресу та час останньої активності. Натисніть «Відкликати» на будь-якій сесії, яку ви не впізнаєте. Ви також можете відкликати всі сесії одночасно для виходу з усіх пристроїв.
API-ключ
Згенеруйте особистий API-ключ для програмного завантаження торентів через AniRena API. Натисніть «Згенерувати ключ» для створення — повний ключ відображається один раз безпосередньо після генерації. Зберігайте його в безпечному місці; він не буде показаний повністю знову. Використайте «Відкликати» для постійного анулювання ключа.
Видалення акаунту
Запит на видалення акаунту починає 30-денний пільговий термін. Ваш акаунт негайно вимикається та остаточно видаляється через 30 днів. Ви можете скасувати видалення в будь-який час в цей термін, увійшовши та натиснувши «Скасувати видалення».
7. AniRena API
AniRena надає JSON API, що дозволяє програмно завантажувати торенти за допомогою особистого API-ключа. API застосовує ті самі правила, що й веб-інтерфейс: перевірки заборон, ліміти запитів та обмеження режиму сайту — все це діє. Кожне завантаження через API реєструється в журналі аудиту.
Автентифікація
API використовує двоетапний процес автентифікації. Спочатку обміняйте свій постійний ключ API на короткочасний bearer-токен, а потім передавайте цей токен у заголовку Authorization кожного запиту API.
Ваш ключ API доступний у розділі Ваш обліковий запис > Ключ API. Тримайте його в таємниці — будь-хто з ключем може отримувати bearer-токени і завантажувати від вашого імені. У разі компрометації негайно відкличте його і створіть новий.
Крок 1 — Отримати bearer-токен
/api/v1/auth/tokenНадішліть POST-запит на кінцеву точку токена з вашим ключем API у заголовку Authorization. Тіло запиту не потрібне.
Authorization: ApiKey <your-api-key>
Відповідь із токеном
{
"token": "<bearer-token>",
"token_type": "Bearer",
"expires_in": 3600
}Час життя токена
Bearer-токени залишаються дійсними до 3600 секунд з моменту видачі і можуть повторно використовуватися в кожному виклику до закінчення терміну дії. Коли токен закінчується, створіть новий через POST /api/v1/auth/token. Кожна відповідь все ще повертає поточний токен у заголовку X-New-Token для зворотної сумісності.
X-New-Token: <next-bearer-token>
Вхід одним запитом (з 2FA)
/api/v1/auth/loginПройдіть автентифікацію за іменем користувача або електронною поштою та паролем в одному запиті й отримайте bearer-токен напряму. Якщо у вашому обліковому записі ввімкнено 2FA, додайте поточний код з застосунку-автентифікатора в totp_code (або код відновлення в recovery_code). За бажанням установіть new_api_key у true, щоб у тій самій відповіді також створити абсолютно новий постійний ключ API.
Тіло запиту
{
"login": "username or email",
"password": "your-password",
"totp_code": "123456", // обов’язково, якщо ввімкнено 2FA (6 цифр)
"recovery_code": "", // альтернатива totp_code
"new_api_key": false // установіть true, щоб також створити новий ключ API
}Відповідь із токеном
{
"ok": true,
"token": "<bearer-token>",
"token_type": "Bearer",
"expires_in": 3600,
"api_key": "<new-api-key>" // присутнє лише коли new_api_key було true
}Bearer-токен працює точно так само, як отриманий через /api/v1/auth/token. Поле api_key повертається лише коли new_api_key дорівнює true — збережіть його одразу, оскільки воно показується лише один раз і замінює будь-який попередній ключ.
Приклад — вхід і (за бажанням) отримання нового ключа 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}"); } }
Крок 2 — Завантажити торент
/api/v1/torrentsНадішліть простий JSON POST-запит із bearer-токеном у заголовку Authorization.
Тіло запиту
| Поле | Тип | Обов'язкове | Опис |
|---|---|---|---|
torrent | string | Так | Вміст .torrent файлу, закодований в Base64. |
category | string | Так | Ярлик категорії: anime, manga, audio, literature, live, pictures, software, hentai, other. |
name | string | Ні | Замінити відображувану назву торента. |
sub_category | string | Ні | Ярлик підкатегорії (напр. raw, sub-audio). Повинен належати вибраній категорії. |
languages | string[] | Ні | Масив мовних кодів BCP 47 (напр. en, ja). |
group_id | string | Ні | UUID групи, учасником якої ви є, для пов'язання з цим релізом. |
description | string | Ні | Опис релізу у форматі Markdown (максимум 65535 символів). |
comment | string | Ні | Замінити вбудоване поле коментаря торента. |
is_private | boolean | Ні | Встановити значення true для увімкнення прапора приватності в торенті. |
comments_enabled | boolean | Ні | Дозволити коментарі до цього торента. За замовчуванням true (увімкнено). |
anime_id | string | Ні | UUID запису аніме для прив'язки до цього торента. Отримайте UUID через GET /api/v1/anime/search. Повертає 400, якщо UUID не відповідає жодному відомому запису. |
announce | string | Ні | Замінити або додати первинний URL оголошення. |
trackers | string | Ні | Розділений символом нового рядка список додаткових URL трекерів. Порожній рядок між URL створює новий рівень трекерів. |
test | boolean | Ні | Встановіть на true для пробного запуску: запит повністю перевіряється, але торент не зберігається. Використовуйте це для перевірки коректності даних перед реальним надсиланням. |
Відповідь на успішний пробний запуск — 200 OK
{
"ok": true,
"test": true,
"name": "My Torrent Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null
}Доступні коди мов
abaaafaksqamarar-001anhyasavaeayazbmbaeubebnbhbibsbrbgmyyuecachcenyzhzh-HKzh-Hanszh-SGzh-TWcucvkwcocrhrcsdadvnlnl-BEdzenen-USeoeteefofjfilfifrfr-CAffgllgkadede-ATelgnguhthahehzhihohuisioigidiaieiuikgaitjajvklknkrkskkkmkirwrnkvkgkokjkukylolalvlilnltlulbmkmgmsmlmtgvmimrmhmnnanvngnendsenonbnnocorojomospipsfaplptpt-BRpaqurormrusmsgsascgdsrsr-Latnsniisdsiskslsonrsteses-419es-MXsuswsssvtltytgtatttethbotitotstntrtktwukuruguzvevivowacyfywoxhyiyozazuПриклад запиту
# 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 }
Успішна відповідь — 200 OK
{
"ok": true,
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Torrent Title",
"info_hash_v1": "aabbccddeeff...",
"info_hash_v2": null
}Відповіді про помилки
| HTTP статус | Значення |
|---|---|
400 | Невірне тіло запиту або відсутнє обов'язкове поле. |
401 | Відсутній, прострочений або вже ротований bearer-токен. Повторно автентифікуйтесь через POST /api/v1/auth/token. |
403 | Акаунт заблокований, вимкнений або IP заблоковано. |
409 | Дублікат торента — торент з тим самим info hash вже існує. |
422 | Файл торента не вдалося розібрати або він не пройшов перевірку (заборонений шаблон, невірна структура). |
429 | Перевищено ліміт запитів. Повторіть після скидання вікна. |
503 | Сайт у режимі обслуговування або лише для читання. |
Обмеження запитів
Завантаження через API підпадають під окремий налаштовуваний ліміт запитів, відмінний від веб-інтерфейсу. Ліміт та вікно встановлюються адміністратором сайту. При перевищенні ліміту API повертає 429 Too Many Requests. Ліміт є на API-ключ.
Створення файлів торента за допомогою torrent-builder
torrent-builder — це CLI-інструмент з відкритим вихідним кодом, побудований на libtorrent-rasterbar, що дозволяє створювати файли .torrent у форматах BitTorrent v1, v2 та hybrid з командного рядка. Він ідеально поєднується з AniRena upload API — створіть файл локально, а потім надішліть його через POST безпосередньо на трекер. cantalupo555/torrent-builder.
Збирання з вихідного коду
Вимагає CMake >= 3.28.3 та libtorrent-rasterbar >= 2.0.11. Клонуйте репозиторій та зберіть за допомогою 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 .
Основні прапори
| Поле | Опис |
|---|---|
--path | Шлях до файлу або каталогу для пакування (обов'язково). |
--output | Ім'я вихідного файлу .torrent (обов'язково). |
--version | Формат BitTorrent — 1 = v1, 2 = v2, 3 = hybrid (за замовчуванням: 3). |
--tracker | Додати URL оголошення трекера. Повторіть прапор для додавання кількох трекерів. |
--comment | Вбудувати рядок коментаря метаданих у торент. |
--private | Встановити прапор конфіденційності для обмеження розповсюдження лише переліченими трекерами. |
--piece-size | Розмір частини в КБ (16-32768). Залиште без значення для автоматичного вибору. |
-i | Запустити покроковий інтерактивний режим конфігурації. |
Наскрізний робочий процес: збирання -> завантаження
Наведені нижче приклади створюють hybrid-торент за допомогою torrent-builder, потім виконують автентифікацію через AniRena API та завантажують результат в одному скрипті.
# 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"]); }
Пошук метаданих торентів
/api/v1/torrents/searchНадішліть простий JSON POST-запит для отримання списків торентів із тими самими параметрами пошуку та фільтрації, доступними на сайті. Сам файл .torrent не повертається — для цього використовуйте звичайний маршрут завантаження.
# 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"]); }
Параметри пошуку
| Поле | Тип | Обов'язкове | Опис |
|---|---|---|---|
q | string | Ні | Повнотекстовий пошук. Підтримує префікси group:slug, group:"Name", user:name. |
category | string | Ні | Ярлик категорії (напр. "anime"). |
sub_category | string | Ні | Ярлик підкатегорії (напр. "raw"). |
languages | string[] | Ні | Масив мовних кодів BCP 47 (напр. en, ja). |
sort | string | Ні | Поле сортування: date (за замовчуванням), size, seeders, leechers, completed, title. |
order | string | Ні | Напрямок сортування: desc (за замовчуванням) або asc. |
page | integer | Ні | Номер сторінки, починаючи з 1 (за замовчуванням 1). |
per_page | integer | Ні | Результатів на сторінці, 1–250 (за замовчуванням 50). |
hide_adult | boolean | Ні | Виключити торенти категорії для дорослих. За замовчуванням true для звичайних користувачів. |
show_dead | boolean | Ні | Коли false (за замовчуванням), торенти, старіші за пільговий період для мертвих торентів і без активних сидерів, виключаються. Установіть true, щоб їх включити. |
Відповідь
{
"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 — Кількість незвидалених коментарів до цього торента.
Обмеження запитів для пошуку
Пошукові запити підпадають під окремий налаштовуваний ліміт запитів (за замовчуванням 60 запитів за 60 секунд на API-ключ). При перевищенні ліміту повертається 429 Too Many Requests. Акаунти персоналу звільнені від ліміту.
Отримати деталі торента
/api/v1/torrent/{id}Отримує повні метадані одного торента — включаючи поля, які кінцева точка пошуку пропускає, такі як опис у Markdown, вбудований коментар .torrent, список файлів із розміром кожного та повний макет рівнів трекерів. Коли доступно, живі лічильники сидерів і лічерів читаються з трекера.
Відповідь
{
"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 — Живі лічильники з внутрішнього трекера; обидва повертають 0, коли сховище трекера не має запису для цього info hash або недоступне.
ext_seeders, ext_leechers — Найбільша кількість сидерів і лічерів, повідомлена будь-яким окремим зовнішнім трекером, який було scrape для цього торента. Трекери, що відстежують той самий рій, перетинаються, тому використовується максимум, а не сума; обидва повертають 0, коли жоден трекер не має даних scrape для цього info hash.
Відповіді про помилки
| HTTP статус | Значення |
|---|---|
400 | Ідентифікатор торента має бути UUID із 36 символів із дефісами або 32-символьним шістнадцятковим рядком. |
401 | Відсутній, прострочений або вже ротований bearer-токен. Повторно автентифікуйтесь через POST /api/v1/auth/token. |
404 | Торент не знайдено. |
429 | Перевищено ліміт запитів. Повторіть після скидання вікна. |
503 | Сайт у режимі обслуговування або лише для читання. |
Отримання коментарів до торента
/api/v1/torrents/{id}/commentsОтримати сторінкові коментарі для торента. Кількість коментарів на сторінку контролюється налаштуванням COMMENT_PER_PAGE у файлі .env сервера (за замовчуванням 20). Лише торенти з увімкненими коментарями повертатимуть результати — усі інші повертають 403.
Параметри запиту
| Поле | Тип | Обов'язкове | Опис |
|---|---|---|---|
page | integer | Ні | Номер сторінки, починаючи з 1 (за замовчуванням 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"]); }
Відповідь
{
"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
}
]
}Поле body є порожнім рядком, коли автора коментаря заблоковано або коментар видалено. Прапор author_banned вказує, який випадок застосовується.
Відповіді про помилки
| HTTP статус | Значення |
|---|---|
401 | Відсутній, прострочений або вже ротований bearer-токен. Повторно автентифікуйтесь через POST /api/v1/auth/token. |
403 | Коментарі вимкнено для цього торента. |
404 | Торент не знайдено. |
503 | Сайт у режимі обслуговування або лише для читання. |
Пошук записів аніме
/api/v1/anime/search?q=<query>Шукайте записи аніме за назвою, щоб отримати їх UUID. UUID можна передати як anime_id у тілі завантаження, щоб прив'язати торент до запису аніме під час завантаження, або використати з PUT /api/torrents/{id}/anime після завантаження. Автентифікація не потрібна. Підпорядковується тому самому обмеженню швидкості, що й пошук торентів (за замовчуванням 60 запитів за 60 секунд на 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"]);
}Параметри запиту
| Поле | Тип | Обов'язкове | Опис |
|---|---|---|---|
q | string | Так | Рядок пошуку заголовку (обов'язково). Зіставляється з заголовком та синонімами. |
page | integer | Ні | Номер сторінки, починаючи з 1 (за замовчуванням 1). |
per_page | integer | Ні | Результатів на сторінці, 1–50 (за замовчуванням 10). |
Відповідь
{
"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
}
]
}Перегляд і отримання груп
/api/v1/groupsПовертає пагіновий список публічних груп (активних та незаблокованих). Потрібна автентифікація Bearer token.
/api/v1/groups/{id_or_slug}Повертає одну публічну групу за числовим ID або slug. Повертає 404, якщо групу вимкнено або заблоковано.
Параметри запиту (лише список)
| Поле | Тип | Обов'язкове | Опис |
|---|---|---|---|
q | string | Ні | Фільтр за назвою групи (необов'язково, часткове збіг). |
page | integer | Ні | Номер сторінки (за замовчуванням 1). |
per_page | integer | Ні | Результатів на сторінку, 1–100 (за замовчуванням 20). |
sort | string | Ні | Стовпець сортування: name | slug | members | torrents | created (за замовчуванням name). |
order | string | Ні | Напрямок сортування: asc або desc (за замовчуванням asc). |
Відповідь (список)
{
"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"
}
]
}Відповідь (одна)
{
"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"
}Відповіді про помилки
| HTTP статус | Значення |
|---|---|
401 | Відсутній, прострочений або вже ротований bearer-токен. Повторно автентифікуйтесь через POST /api/v1/auth/token. |
404 | Групу не знайдено або вона не є публічно доступною. |
429 | Перевищено ліміт запитів. Повторіть після скидання вікна. |
503 | Сайт у режимі обслуговування або лише для читання. |
8. Пожертви
Якщо ви бажаєте підтримати AniRena та допомогти покрити витрати на хостинг наших серверів і сервісів, ви можете надіслати пожертву на один із наступних криптовалютних гаманців:
bc1qy2h3ddq6ak5damvnf4r5vu3ydehhxrcq8gllwn0xCbaFe03832F95F86AF2536d52710e78C63b62Cd33ucetj2XDGHQg9PVRPMxerNi7c6kX7GJkjQNg9yjwGegLbpt61yX3RjGtB1Ef8vgVz6Hr6baQsTjVkБудь-яке пожертвування, велике чи мале, дуже цінується та йде безпосередньо на підтримку роботи AniRena. Дякуємо за вашу підтримку!
9. Програмне забезпечення
AniRena Player — це безкоштовний десктопний застосунок, який дозволяє транслювати відео безпосередньо з торрентів, проіндексованих на цьому сайті, не чекаючи завершення повного завантаження. Просто вставте magnet-посилання або відкрийте файл .torrent — відтворення розпочнеться, щойно буде достатньо даних.
Обидві збірки повністю автономні — усі залежності упаковано всередині виконуваного файлу. Не потрібен інсталятор чи середовище виконання — просто завантажте та запустіть.
Інсталятор (.exe). Оновлюється сам у застосунку.
Образ диска (.dmg) для Mac з Apple Silicon (M1 і новіші). Оновлюється сам у застосунку.
Образ диска (.dmg) для Mac з Intel. Оновлюється сам у застосунку.
Портативний єдиний файл, інсталяція не потрібна. Єдиний формат Linux із оновленням у застосунку.
Інсталяція: sudo apt install ./<file>.deb — оновлення через apt або нове завантаження, не у застосунку.
Інсталяція: sudo dnf install ./<file>.rpm — оновлення через dnf або нове завантаження, не у застосунку.
Встановіть вручну на 64-бітних ARM-пристроях Android (більшість сучасних телефонів / планшетів). Оновлюється завантаженням нового APK.
Старіші версії
Встановіть вручну на 32-бітні ARM-пристрої Android (старіші телефони / планшети). Оновлюється завантаженням нового APK.