ガイド
1. はじめに
AniRenaはアニメ、漫画、音声およびその他関連メディアに特化したトレントインデックスです。アカウントなしでトレントのブラウジングとダウンロードが可能です。トレントのアップロード、グループへの参加、またはAPIの利用にはアカウントが必要です。
上部のナビゲーションバーからサイトの主要エリアにアクセスできます:
- ホーム — トレント一覧と検索ページ。
- アップロード — 新しいトレントを投稿(ログインが必要)。
- ガイド — このページ。
- 統計 — サイト全体の統計(トレント、ピア、アップロード推移)。
- グループ — リリースグループのディレクトリ。
- RSS — 最新アップロードのRSSフィード(カテゴリでフィルタリング可能)。
ログイン時に右上に表示されるアカウントメニューから、プロフィールパネルを開いて設定の変更、セキュリティオプションの管理、APIキーへのアクセスができます。
2. ブラウジングと検索
ホームページにはすべてのトレントがアップロード日順に表示されます。上部の検索バーを使って結果をフィルタリングできます。
基本的な検索
検索バーに単語を入力してEnterキーを押す(または検索アイコンをクリックする)と結果が表示されます。クエリが有効な場合は関連度順に並び替えられます。
検索演算子
以下の演算子を通常のクエリと組み合わせて使用できます:
| 演算子 | 例 | 効果 |
|---|---|---|
user:"name" | user:"SubsPlease" | そのユーザーがアップロードしたトレントのみを表示します。 |
トレント一覧でアップローダー名をクリックすると、自動的にユーザー検索が実行されます。
カテゴリとサブカテゴリ
検索バー横のグリッドアイコン(カテゴリセレクター)を使って、結果を1つのカテゴリに絞り込めます。利用可能なカテゴリ:
- アニメ
- マンガ/マンファ/コミック
- 音楽
- 文学
- 実写
- 画像
- ソフトウェア
- 成人向け
- その他
各カテゴリにはサブカテゴリがあります(例: アニメ → RAW、字幕/音声、ミュージックビデオ)。カテゴリモーダル内で選択できます。
ソートとフィルター
トレント一覧の列ヘッダーをクリックしてその列でソート(昇順/降順)できます。利用可能なソート列: 日付、名前、サイズ、完了ダウンロード数。注: シーダーとリーチャーは Redis から取得されるリアルタイム値のため、ソートには使用できません。
言語フィルター
言語セレクター(旗アイコン)を使って、特定の言語タグが付いたトレントのみを表示できます。
RSSフィード
/rssのRSSフィードで最新アップロードを取得できます。?category=animeなどのカテゴリスラグを追加してフィードをフィルタリングできます。多くのトレントクライアントはこのURLから直接RSSの自動ダウンロードに対応しています。
3. トレントのダウンロード
トレント名をクリックして詳細パネルを開きます。そこから以下が可能です:
- .torrentをダウンロード — .torrentファイルを直接保存します。直接URLは
/torrents/{id}.torrent - マグネットリンク — マグネットURIプロトコルを使ってトレントクライアントに直接開きます。URLは
/torrents/{id}/magnet
詳細パネルにはトレントの説明、ファイルリスト、トラッカーリスト、シーダー/リーチャー数も表示されます。
レガシーダウンロードリンク
旧AniRenaのダウンロードリンクは引き続きサポートされており、レガシーIDを使用して正しい.torrentファイルに自動的にリダイレクトされます: /dl/{old_id}
おすすめのBitTorrentクライアント
最新のBitTorrentクライアントであれば動作します。以下のクライアントは推奨されており、BitTorrent v2 / ハイブリッドトレントを完全にサポートしています:
4. アカウントの作成
登録
ナビゲーションバーの「サインアップ」をクリックします。ユーザー名を選択し、メールアドレスとパスワード(最小文字数あり)を設定してください。アカウント作成前にサイトの利用規約を読み、同意する必要があります。
メール認証
サインアップ後、確認メールが送信されます。メール内のリンクをクリックしてアカウントを有効化してください。受信できなかった場合は、ログインページの「アカウントを有効化する」リンクから新しいコードを申請してください。
パスワードの回復
パスワードを忘れた場合は、サインインページの「パスワードをお忘れですか」をクリックしてメールアドレスを入力してください。回復リンクが送信されます。リンクは1回限り有効で、短時間で期限切れになります。
5. トレントのアップロード
ナビゲーションバーの「アップロード」に移動します。有効かつBANされていないアカウントでログインしている必要があります。アップロードページには2つのタブがあります:
アップロードタブ — 既存の.torrentファイルを投稿
.torrentファイルをドラッグ&ドロップするか選択してください。読み込み後、各フィールドを入力します:
| フィールド | 必須 | 説明 |
|---|---|---|
| トレントファイル | はい | アップロードする.torrentファイル。 |
| 名前 | いいえ | トレントの表示名を上書きします。空白の場合はトレントファイルに埋め込まれた名前が使用されます。 |
| カテゴリ | はい | コンテンツのカテゴリ(アニメ、漫画、音声など)。 |
| サブカテゴリ | いいえ | カテゴリ内のより具体的なタイプ(例: RAW、字幕/音声)。 |
| 言語 | いいえ | コンテンツの言語を表す1つ以上の言語タグ。 |
| グループ | いいえ | メンバーであるグループにこのリリースを関連付けます。 |
| 説明 | いいえ | トレント詳細ページに表示されるMarkdown形式の説明(最大65535文字)。 |
| プライベート | いいえ | トレントにプライベートフラグを設定し、DHT/PEXを無効にします。トラッカー専用トレントに有用です。 |
| アナウンスURL | いいえ | プライマリトラッカーのアナウンスURLを上書きまたは追加します。 |
| 追加トラッカー | いいえ | トレントファイルから読み取られます。アップロード中は変更できません — トラッカーリストをカスタマイズするには「作成」タブを使用してください。 |
| コメント | いいえ | ファイルに埋め込まれたトレントのコメントフィールドを上書きします。 |
トレントのアナウンスリストに少なくとも1つのAniRenaトラッカーURLが含まれている必要があります(どのティアでも可)。サイトはアップロード時にこれを確認し、AniRenaトラッカーが含まれていないトレントは拒否されます。AniRenaトラッカーを追加せずにトレントを作成した場合は、アップロードしてからサイトで再ダウンロードしてください — ダウンロードされたファイルには正しいトラッカーが自動的に設定されています。
作成タブ — 新しいトレントを構築
作成タブでは、ブラウザ上でファイルパス、トラッカーURL、その他のトレントパラメータを指定して新しい.torrentをゼロから生成できます。作成されたトレントは上記と同じメタデータフィールドで送信されます。
モデレーション
アップロードは禁止コンテンツパターン(名前、ファイル名、説明)のリストに対して自動的にチェックされます。禁止パターンに一致するトレントは拒否されます。重複トレント(同一インフォハッシュ)も拒否されます。
6. アカウント設定
右上のユーザー名をクリックしてプロフィールパネルを開きます。折りたたみ可能なセクションで構成されています:
設定
UIテーマ、フォントサイズ、カラースキーム、インターフェース言語、トレント関連の表示設定を変更できます。変更は自動的に保存されます。
パスワード
現在のパスワードと新しいパスワードを2回入力します。変更を確認するために、登録済みのメールアドレスに確認コードが送信されます。二要素認証が有効な場合はTOTPコードも必要です。
二要素認証(2FA)
認証アプリ(例: Google Authenticator、Aegis、Bitwarden)を使ってTOTPベースの二要素認証を有効にできます。2FAを有効にする手順:
- 認証アプリでQRコードをスキャン(またはシークレットを手動入力)します。
- アプリに表示される6桁のコードを入力して設定を確認します。
- 表示されたリカバリーコードを保存してください — デバイスを紛失した際にアクセスを回復するための使い捨てコードです。
2FAを無効にするには、現在のTOTPコードを入力して確認してください。
アクティブセッション
ブラウザ、OS、IPアドレス、最終確認時刻を含む現在アクティブなすべてのログインセッションを確認できます。心当たりのないセッションの「取り消し」をクリックしてください。すべてのセッションを一度に取り消して全デバイスからログアウトすることもできます。
APIキー
AniRena APIを通じてプログラム的にトレントをアップロードするための個人APIキーを生成します。「キーを生成」をクリックして作成してください — 生成直後にフルキーが1度だけ表示されます。安全に保管してください。その後は完全な形で表示されません。「取り消し」を使ってキーを永久に無効化できます。
アカウントの削除
アカウントの削除を申請すると30日間の猶予期間が開始されます。アカウントはすぐに無効化され、30日後に完全に削除されます。その期間内であれば、ログインして「削除をキャンセル」をクリックすることでいつでも削除をキャンセルできます。
7. AniRena API
AniRenaは個人APIキーを使ってトレントをプログラム的にアップロードできるJSON APIを提供しています。APIはWebインターフェースと同じルールを適用します: BANチェック、レート制限、サイトモード制限がすべて適用されます。すべてのAPIアップロードは監査ログに記録されます。
認証
APIは2段階の認証フローを使用します。まず永続的なAPIキーを短命のBearerトークンに交換し、次にそのトークンをすべてのAPIリクエストのAuthorizationヘッダーに渡します。
APIキーはアカウント > APIキーで確認できます。秘密にしてください — キーを持つ人は誰でもBearerトークンを取得し、あなたの代わりにアップロードできます。漏洩した場合は直ちに取り消し、新しいものを生成してください。
ステップ1 — Bearerトークンを取得する
/api/v1/auth/tokenAuthorizationヘッダーにAPIキーを含めてトークンエンドポイントにPOSTリクエストを送信します。リクエストボディは不要です。
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ユーザー名またはメールアドレスとパスワードを1回のリクエストで認証し、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/torrentsAuthorizationヘッダーにBearerトークンを含めたシンプルなJSON POSTリクエストを送信します。
リクエストボディ
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
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。GET /api/v1/anime/search でUUIDを取得してください。UUIDが既知のエントリと一致しない場合は400を返します。 |
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トークンがないかT期限切れか、すでにローテーションされています。POST /api/v1/auth/tokenで再認証してください。 |
403 | アカウントがBAN、無効化、またはIPがブロックされています。 |
409 | 重複トレント — 同一インフォハッシュがすでに存在します。 |
422 | torrentファイルが解析できないか、バリデーションに失敗しました(禁止パターン、無効な構造)。 |
429 | レート制限を超えました。ウィンドウがリセットされた後に再試行してください。 |
503 | サイトがメンテナンスモードまたは読み取り専用モードです。 |
レート制限
APIアップロードはWebインターフェースとは別の設定可能なレート制限が適用されます。制限とウィンドウはサイト管理者が設定します。レート制限を超えると、APIは429 Too Many Requestsを返します。制限はAPIキーごとに適用されます。
torrent-builderを使ったtorrentファイルの生成
torrent-builderはlibtorrent-rasterbar上に構築されたオープンソースのCLIツールで、コマンドラインからBitTorrent v1、v2、hybrid形式の.torrentファイルを作成できます。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 | torrentにメタデータのコメント文字列を埋め込みます。 |
--private | プライベートフラグを設定して、配布を指定したトラッカーのみに制限します。 |
--piece-size | ピースサイズ(KB単位、16〜32768)。自動選択するには設定しないままにしてください。 |
-i | ステップバイステップのインタラクティブ設定モードを起動します。 |
エンドツーエンドのワークフロー: ビルド -> アップロード
以下の例では、torrent-builderでhybrid torrentをビルドし、AniRena APIで認証してから1つのスクリプトで結果をアップロードします。
# 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"]); }
torrentメタデータの検索
/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 | いいえ | アダルトカテゴリのtorrentを除外。一般ユーザーはデフォルトで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 — このトレントの削除されていないコメント数。
検索レート制限
検索リクエストは別途設定可能なレート制限の対象です(デフォルト: APIキーごとに60秒で60リクエスト)。超過した場合は429 Too Many Requestsが返されます。スタッフアカウントは免除されます。
トレントの詳細を取得
/api/v1/torrent/{id}単一のトレントの完全なメタデータを取得します — マークダウンの説明、埋め込まれた .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 — 内部トラッカーからのライブカウントです。トラッカーストアにこの info hash のエントリがない場合、または到達できない場合は、両方とも 0 を報告します。
ext_seeders, ext_leechers — このトレントについてスクレイプされた単一の外部トラッカーから報告されたシーダーとリーチャーの最大数。同じ swarm を追跡するトラッカーは重複するため、合計ではなく最大値が使用されます。どのトラッカーにもこの info hash のスクレイプデータがない場合、両方とも 0 を報告します。
エラーレスポンス
| HTTPステータス | 意味 |
|---|---|
400 | トレント ID は、ハイフン区切りの 36 文字の UUID、または 32 文字の素の 16 進文字列である必要があります。 |
401 | BearerトークンがないかT期限切れか、すでにローテーションされています。POST /api/v1/auth/tokenで再認証してください。 |
404 | トレントが見つかりません。 |
429 | レート制限を超えました。ウィンドウがリセットされた後に再試行してください。 |
503 | サイトがメンテナンスモードまたは読み取り専用モードです。 |
torrentのコメントを取得する
/api/v1/torrents/{id}/commentstorrentのコメントをページネーションで取得します。1ページあたりのコメント数はサーバーの.envファイルのCOMMENT_PER_PAGE設定で制御されます(デフォルト20)。コメントが有効になっているtorrentのみ結果を返します — それ以外はすべて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
}
]
}コメント投稿者がBANされているか、コメントが削除されている場合、bodyフィールドは空文字列になります。author_bannedフラグがどちらの場合かを示します。
エラーレスポンス
| HTTPステータス | 意味 |
|---|---|
401 | BearerトークンがないかT期限切れか、すでにローテーションされています。POST /api/v1/auth/tokenで再認証してください。 |
403 | このtorrentではコメントが無効になっています。 |
404 | torrentが見つかりません。 |
503 | サイトがメンテナンスモードまたは読み取り専用モードです。 |
アニメエントリーを検索
/api/v1/anime/search?q=<query>タイトルでアニメエントリを検索してUUIDを取得します。UUIDはアップロード本文のanime_idとして渡すことで、アップロード時にトレントをアニメエントリに紐付けることができます。またはアップロード後にPUT /api/torrents/{id}/animeで使用することもできます。認証は不要です。トレント検索と同じレート制限が適用されます(デフォルト:IPごとに60秒あたり60リクエスト)。
# 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トークン認証が必要です。
/api/v1/groups/{id_or_slug}数値IDまたはスラッグで単一の公開グループを返します。グループが無効またはロックされている場合は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トークンがないかT期限切れか、すでにローテーションされています。POST /api/v1/auth/tokenで再認証してください。 |
404 | グループが見つからないか、公開アクセスできません。 |
429 | レート制限を超えました。ウィンドウがリセットされた後に再試行してください。 |
503 | サイトがメンテナンスモードまたは読み取り専用モードです。 |
8. 寄付
AniRenaを支援し、サーバーやサービスのホスティングコストを賄うためのご寄付を、以下のいずれかの暗号資産ウォレットにお送りいただけます:
bc1qy2h3ddq6ak5damvnf4r5vu3ydehhxrcq8gllwn0xCbaFe03832F95F86AF2536d52710e78C63b62Cd33ucetj2XDGHQg9PVRPMxerNi7c6kX7GJkjQNg9yjwGegLbpt61yX3RjGtB1Ef8vgVz6Hr6baQsTjVk大小を問わず、いただいたご支援はすべて大変ありがたく、AniRenaの運営維持に直接役立てられます。ご支援ありがとうございます!
9. ソフトウェア
AniRena Player は、このサイトでインデックスされた torrent から直接ビデオをストリーミング再生できる無料のデスクトップアプリです。ダウンロードの完了を待つ必要はありません。マグネットリンクを貼り付けるか .torrent ファイルを開くだけで、十分なデータが揃い次第すぐに再生が始まります。
両方のビルドは完全に単体で動作します — すべての依存ファイルは実行ファイル内にバンドルされています。インストーラーやランタイムのセットアップは不要。ダウンロードして実行するだけです。
インストーラー (.exe)。アプリ内で自動更新されます。
ディスクイメージ (.dmg)。Apple Silicon 搭載 Mac(M1 以降)向け。アプリ内で自動更新されます。
ポータブルな単一ファイル、インストール不要。Linux 上でアプリ内自動更新に対応する唯一の形式です。
インストール: sudo apt install ./<file>.deb — apt または再ダウンロードで更新します(アプリ内更新には非対応)。
インストール: sudo dnf install ./<file>.rpm — dnf または再ダウンロードで更新します(アプリ内更新には非対応)。
64ビット ARM Android デバイス(最近のスマートフォン / タブレットの大半)にサイドロードしてください。新しい APK をダウンロードして更新します。
以前のバージョン
32ビット ARM Android デバイス(古いスマートフォン / タブレット)にサイドロードしてください。新しい APK をダウンロードして更新します。