Files
playground/alldebrid.py
T

151 lines
4.4 KiB
Python

# /// script
# requirements = ["httpx", "pydantic"]
# ///
import dataclasses
import os
from pathlib import Path
import httpx
import argparse
import time
import pydantic
class MagnetStatus(pydantic.BaseModel):
id: int
filename: str
size: int
status: str
downloaded: int
@dataclasses.dataclass()
class AlldebridError(Exception):
code: str
message: str
class AlldebridDownloader:
def __init__(self, api_token: str):
self.client = httpx.Client(
timeout=10,
headers={
"Authorization": f"Bearer {api_token}",
"User-Agent": "alldebrid downloader for abdusco",
},
)
def _check_error(self, res: httpx.Response):
res.raise_for_status()
data = res.json()
if data["status"] == "error":
error = data["error"]
raise AlldebridError(code=error["code"], message=error["message"])
def unrestrict_url(self, url: str) -> str | None:
res = self.client.get(
"https://api.alldebrid.com/v4/link/unlock",
params={
"link": url,
},
)
self._check_error(res)
data = res.json()
if data["status"] == "success":
return data["data"].get("link")
def upload_torrent(self, torrent_path: Path) -> int:
res = self.client.post(
"https://api.alldebrid.com/v4/magnet/upload/file",
files={
"files[]": (torrent_path.name, torrent_path.read_bytes(), "application/x-bittorrent"),
},
)
self._check_error(res)
data = res.json()
if data["status"] == "success":
file = data["data"]["files"][0]
return file["id"]
def upload_magnet(self, magnet_uri: str) -> int:
res = self.client.post(
"https://api.alldebrid.com/v4/magnet/upload",
data={
"magnets[]": magnet_uri,
},
)
self._check_error(res)
data = res.json()
if data["status"] == "success":
magnet = data["data"]["magnets"][0]
return magnet["id"]
def get_torrent_links(self, torrent_id: int) -> list[str] | None:
res = self.client.post(
f"https://api.alldebrid.com/v4/magnet/files",
data={"id[]": torrent_id},
)
self._check_error(res)
data = res.json()
if data["status"] == "success":
magnets = data["data"]["magnets"]
if not magnets:
return
files = magnets[0]["files"][0]["e"]
if not files:
raise ValueError("torrent is empty")
large_files = [m for m in files if m["s"] > 1_000_000]
if not large_files:
raise ValueError("torrent only contains small files")
links = [it["l"] for it in large_files]
return links
def wait_for_download_link(self, magnet_id, timeout=600):
start = time.time()
while time.time() - start < timeout:
links = self.get_torrent_links(magnet_id)
if links:
return self.unrestrict_url(links[0])
time.sleep(5)
return None
def parse_args():
parser = argparse.ArgumentParser(description="Alldebrid Downloader")
parser.add_argument("input", help="URL, magnet link, or torrent file")
parser.add_argument("--token", default=os.getenv("ALLDEBRID_TOKEN"), help="Alldebrid API token")
return parser.parse_args()
def main():
args = parse_args()
downloader = AlldebridDownloader(args.token)
input_as_path = Path(args.input)
if input_as_path.is_file() and input_as_path.suffix == ".torrent":
torrent_id = downloader.upload_torrent(input_as_path)
if not torrent_id:
return
result = downloader.wait_for_download_link(torrent_id)
if result:
print(result)
elif args.input.startswith("magnet:"):
magnet_data = downloader.upload_magnet(args.input)
if not magnet_data:
return
result = downloader.wait_for_download_link(magnet_data["id"])
if result:
print(result)
elif args.input.startswith("http:") or args.input.startswith("https:"):
result = downloader.unrestrict_url(args.input)
if not result:
return
print(result)
else:
raise ValueError("Invalid input")
if __name__ == "__main__":
main()