가이드
1. 시작하기
AniRena는 애니메이션, 만화, 오디오 및 관련 미디어에 특화된 토렌트 인덱스입니다. 계정 없이도 토렌트를 탐색하고 다운로드할 수 있습니다. 토렌트 업로드, 그룹 게시 또는 API 사용에는 계정이 필요합니다.
상단의 내비게이션 바에서 사이트 주요 영역에 액세스할 수 있습니다:
- 홈 — 토렌트 목록 및 검색 페이지.
- 업로드 — 새 토렌트 제출 (로그인 필요).
- 가이드 — 이 페이지.
- 통계 — 사이트 전체 통계 (토렌트, 피어, 시간별 업로드).
- 그룹 — 릴리스 그룹 디렉토리.
- RSS — 최신 업로드의 RSS 피드, 카테고리별 필터 가능.
계정 메뉴 (로그인 시 오른쪽 상단)를 클릭하면 설정 조정, 보안 옵션 관리, API 키 액세스가 가능한 프로필 패널이 열립니다.
2. 탐색 및 검색
홈 페이지에서는 업로드 날짜 순으로 정렬된 모든 토렌트를 볼 수 있습니다. 상단의 검색창을 사용하여 결과를 필터링하세요.
기본 검색
검색창에 단어를 입력하고 Enter를 누르세요 (또는 검색 아이콘 클릭). 검색어가 활성화된 경우 결과는 관련성 순으로 정렬됩니다.
검색 연산자
다음 연산자를 일반 검색어와 함께 사용할 수 있습니다:
| 연산자 | 예시 | 효과 |
|---|---|---|
user:"name" | user:"SubsPlease" | 해당 사용자가 업로드한 토렌트만 표시합니다. |
토렌트 목록에서 업로더 이름을 클릭하면 자동으로 사용자 검색이 실행됩니다.
카테고리 및 하위 카테고리
카테고리 선택기 (검색창 옆 격자 아이콘)를 사용하여 결과를 한 카테고리로 제한하세요. 사용 가능한 카테고리:
- 애니메이션
- 만화/만화책
- 오디오
- 문학
- 실사
- 사진
- 소프트웨어
- 헨타이
- 기타
각 카테고리에는 하위 카테고리가 있습니다 (예: 애니메이션의 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. 계정 만들기
등록
내비게이션 바에서 회원가입을 클릭하세요. 사용자명을 선택하고 이메일 주소를 제공하고 비밀번호를 설정하세요 (최소 길이 필요). 계정을 만들기 전에 사이트 약관을 읽고 동의해야 합니다.
이메일 활성화
가입 후 주소로 인증 이메일이 발송됩니다. 이메일의 링크를 클릭하여 계정을 활성화하세요. 받지 못한 경우 로그인 페이지의 계정 활성화 링크를 사용하여 새 코드를 요청하세요.
비밀번호 복구
비밀번호를 잊은 경우 로그인 페이지에서 비밀번호를 잊으셨나요?를 클릭하고 이메일 주소를 입력하세요. 복구 링크가 발송됩니다. 링크는 일회용이며 짧은 시간 후 만료됩니다.
5. 토렌트 업로드
내비게이션 바에서 업로드로 이동하세요. 활성화되고 차단되지 않은 계정으로 로그인해야 합니다. 업로드 페이지에는 두 탭이 있습니다:
업로드 탭 — 기존 .torrent 파일 제출
드래그 앤 드롭하거나 .torrent 파일을 선택하세요. 로드된 후 필드를 채우세요:
| 필드 | 필수 | 설명 |
|---|---|---|
| 토렌트 파일 | 예 | 업로드할 .torrent 파일. |
| 이름 | 아니요 | 토렌트 표시 이름 재정의. 비워두면 토렌트 파일에 내장된 이름이 사용됩니다. |
| 카테고리 | 예 | 콘텐츠 카테고리 (애니메이션, 만화, 오디오 등). |
| 하위 카테고리 | 아니요 | 카테고리 내의 더 구체적인 유형 (예: RAW, 자막/오디오). |
| 언어 | 아니요 | 콘텐츠 언어를 설명하는 하나 이상의 언어 태그. |
| 그룹 | 아니요 | 회원인 그룹과 이 릴리스를 연결합니다. |
| 설명 | 아니요 | 토렌트 상세 페이지에 표시되는 Markdown 형식 설명 (최대 65535자). |
| 비공개 | 아니요 | 토렌트에 비공개 플래그를 설정하여 DHT/PEX를 비활성화합니다. 트래커 전용 토렌트에 유용합니다. |
| 어나운스 URL | 아니요 | 기본 트래커 announce URL을 재정의하거나 추가합니다. |
| 추가 트래커 | 아니요 | 토렌트 파일에서 읽어옵니다. 업로드 중에는 수정할 수 없습니다 — 트래커 목록을 커스터마이즈하려면 만들기 탭을 사용하세요. |
| 댓글 | 아니요 | 파일에 내장된 토렌트 댓글 필드를 재정의합니다. |
토렌트에는 공지 목록에 AniRena 트래커 URL이 최소 하나 포함되어야 합니다(어떤 tier든 가능). 사이트는 업로드 시 이를 확인하며, AniRena 트래커가 없는 토렌트는 거부됩니다. AniRena 트래커를 추가하지 않고 토렌트를 만든 경우, 업로드 후 사이트에서 다시 다운로드하세요 — 다운로드된 파일에는 올바른 트래커가 자동으로 포함됩니다.
만들기 탭 — 새 토렌트 구축
만들기 탭에서는 파일 경로, 트래커 URL 및 기타 토렌트 매개변수를 브라우저에서 직접 지정하여 처음부터 새 .torrent를 생성할 수 있습니다. 생성된 토렌트는 위와 동일한 메타데이터 필드로 제출됩니다.
검토
업로드는 금지된 콘텐츠 패턴 목록 (이름, 파일명, 설명)에 대해 자동으로 확인됩니다. 금지된 패턴과 일치하는 토렌트는 거부됩니다. 중복 토렌트 (동일한 info hash)도 거부됩니다.
6. 내 계정
오른쪽 상단의 사용자명을 클릭하면 프로필 패널이 열립니다. 접을 수 있는 섹션으로 구성되어 있습니다:
설정
UI 테마, 글꼴 크기, 색상 구성표, 인터페이스 언어 및 토렌트 관련 표시 설정을 변경합니다. 변경 사항은 자동으로 저장됩니다.
비밀번호
현재 비밀번호와 새 비밀번호를 두 번 입력하세요. 변경을 확인하기 위해 등록된 이메일 주소로 인증 코드가 발송됩니다. 이중 인증이 활성화된 경우 TOTP 코드도 필요합니다.
이중 인증 (2FA)
인증 앱 (예: Google Authenticator, Aegis, Bitwarden)을 사용하여 TOTP 기반 이중 인증을 활성화합니다. 2FA 활성화 시:
- 인증 앱에서 QR 코드를 스캔하거나 수동으로 비밀을 입력합니다.
- 설정을 확인하기 위해 앱에 표시된 6자리 코드를 입력합니다.
- 표시된 복구 코드를 저장합니다 — 기기를 분실할 경우 액세스를 복구하기 위한 일회용 코드입니다.
2FA를 비활성화하려면 현재 TOTP 코드를 입력하고 확인하세요.
활성 세션
브라우저, OS, IP 주소, 마지막 활동 시간을 포함한 현재 활성 로그인 세션을 모두 확인합니다. 인식하지 못하는 세션에서 취소를 클릭하세요. 모든 기기에서 로그아웃하려면 모든 세션을 한 번에 취소할 수도 있습니다.
API 키
AniRena API를 통해 토렌트를 프로그래밍 방식으로 업로드하기 위한 개인 API 키를 생성합니다. 키 생성을 클릭하여 생성하세요 — 전체 키는 생성 직후 한 번만 표시됩니다. 안전하게 보관하세요. 다시는 전체가 표시되지 않습니다. 취소를 사용하면 키가 영구적으로 무효화됩니다.
계정 삭제
계정 삭제를 요청하면 30일 유예 기간이 시작됩니다. 계정은 즉시 비활성화되고 30일 후 영구 삭제됩니다. 로그인하고 삭제 취소를 클릭하면 언제든지 취소할 수 있습니다.
7. AniRena API
AniRena는 개인 API 키를 사용하여 토렌트를 프로그래밍 방식으로 업로드할 수 있는 JSON API를 제공합니다. API는 웹 인터페이스와 동일한 규칙을 적용합니다: 차단 확인, 속도 제한, 사이트 모드 제한이 모두 적용됩니다. 모든 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사용자 이름 또는 이메일과 비밀번호로 한 번의 요청에서 인증하고 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 | 아니요 | 기본 announce 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는 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 | 트래커 announce URL을 추가합니다. 여러 트래커를 추가하려면 플래그를 반복하세요. |
--comment | 토렌트에 메타데이터 코멘트 문자열을 삽입합니다. |
--private | 비공개 플래그를 설정하여 배포를 나열된 트래커로만 제한합니다. |
--piece-size | KB 단위의 피스 크기 (16-32768). 자동 선택을 위해 설정하지 않고 두세요. |
-i | 단계별 대화형 구성 모드를 시작합니다. |
종단 간 워크플로우: 빌드 -> 업로드
아래 예시는 torrent-builder로 hybrid 토렌트를 빌드하고, 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 — 이 토렌트에서 삭제되지 않은 댓글 수입니다.
검색 속도 제한
검색 요청은 별도의 구성 가능한 속도 제한을 받습니다 (기본값: 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 — 이 토렌트에 대해 스크레이프된 단일 외부 트래커가 보고한 최대 시더 및 리처 수입니다. 동일한 스웜을 추적하는 트래커는 중복되므로 합계 대신 최대값이 사용됩니다. 어떤 트래커도 이 info hash에 대한 스크레이프 데이터를 보유하고 있지 않으면 둘 다 0을 보고합니다.
오류 응답
| HTTP 상태 | 의미 |
|---|---|
400 | 토렌트 ID는 하이픈이 포함된 36자 UUID이거나 32자 순수 16진 문자열이어야 합니다. |
401 | Bearer 토큰이 없거나, 만료되었거나, 이미 교체되었습니다. POST /api/v1/auth/token을 통해 재인증하세요. |
404 | 토렌트를 찾을 수 없습니다. |
429 | 속도 제한 초과. 창이 재설정된 후 다시 시도하세요. |
503 | 사이트가 점검 또는 읽기 전용 모드입니다. |
토렌트 댓글 가져오기
/api/v1/torrents/{id}/comments토렌트의 댓글을 페이지별로 가져옵니다. 페이지당 댓글 수는 서버의 .env 파일의 COMMENT_PER_PAGE 설정으로 제어됩니다 (기본값 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와 함께 사용할 수 있습니다. 인증이 필요하지 않습니다. 토렌트 검색과 동일한 속도 제한이 적용됩니다(기본값: 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 또는 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는 이 사이트에 인덱싱된 토렌트에서 영상을 바로 스트리밍할 수 있는 무료 데스크톱 앱입니다. 전체 다운로드가 끝날 때까지 기다릴 필요가 없습니다. 매그넷 링크를 붙여넣거나 .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를 다운로드하여 업데이트합니다.