diff --git a/comet/api/core.py b/comet/api/core.py
index 6ac4306..875e06a 100644
--- a/comet/api/core.py
+++ b/comet/api/core.py
@@ -72,6 +72,8 @@ async def manifest(b64config: str = None):
debrid_extension = "AD"
elif config["debridService"] == "premiumize":
debrid_extension = "PM"
+ elif config["debridService"] == "torbox":
+ debrid_extension = "TB"
return {
"id": settings.ADDON_ID,
diff --git a/comet/api/stream.py b/comet/api/stream.py
index d39b598..54e9f48 100644
--- a/comet/api/stream.py
+++ b/comet/api/stream.py
@@ -134,6 +134,8 @@ async def stream(request: Request, b64config: str, type: str, id: str):
debrid_extension = "AD"
elif config["debridService"] == "premiumize":
debrid_extension = "PM"
+ elif config["debridService"] == "torbox":
+ debrid_extension = "TB"
balanced_hashes = await get_balanced_hashes(sorted_ranked_files, config)
@@ -282,6 +284,8 @@ async def stream(request: Request, b64config: str, type: str, id: str):
debrid_extension = "AD"
elif config["debridService"] == "premiumize":
debrid_extension = "PM"
+ elif config["debridService"] == "torbox":
+ debrid_extension = "TB"
balanced_hashes = await get_balanced_hashes(sorted_ranked_files, config)
diff --git a/comet/debrid/alldebrid.py b/comet/debrid/alldebrid.py
index 2856b21..bb6d9ea 100644
--- a/comet/debrid/alldebrid.py
+++ b/comet/debrid/alldebrid.py
@@ -35,7 +35,7 @@ class AllDebrid:
async def get_instant(self, chunk: list):
try:
get_instant = await self.session.get(
- f"{self.api_url}/magnet/instant?agent={self.agent}&magnets[]={'&magnets[]='.join(hash for hash in chunk)}"
+ f"{self.api_url}/magnet/instant?agent={self.agent}&magnets[]={'&magnets[]='.join(chunk)}"
)
return await get_instant.json()
except Exception as e:
diff --git a/comet/debrid/manager.py b/comet/debrid/manager.py
index 3003a02..92aef27 100644
--- a/comet/debrid/manager.py
+++ b/comet/debrid/manager.py
@@ -3,6 +3,7 @@ import aiohttp
from .realdebrid import RealDebrid
from .alldebrid import AllDebrid
from .premiumize import Premiumize
+from .torbox import TorBox
def getDebrid(session: aiohttp.ClientSession, config: dict):
@@ -10,7 +11,9 @@ def getDebrid(session: aiohttp.ClientSession, config: dict):
debrid_api_key = config["debridApiKey"]
if debrid_service == "realdebrid":
return RealDebrid(session, debrid_api_key)
- if debrid_service == "alldebrid":
+ elif debrid_service == "alldebrid":
return AllDebrid(session, debrid_api_key)
- if debrid_service == "premiumize":
+ elif debrid_service == "premiumize":
return Premiumize(session, debrid_api_key)
+ elif debrid_service == "torbox":
+ return TorBox(session, debrid_api_key)
diff --git a/comet/debrid/premiumize.py b/comet/debrid/premiumize.py
index c1bfa41..04328ab 100644
--- a/comet/debrid/premiumize.py
+++ b/comet/debrid/premiumize.py
@@ -11,9 +11,9 @@ class Premiumize:
def __init__(self, session: aiohttp.ClientSession, debrid_api_key: str):
self.session = session
self.proxy = None
- self.debrid_api_key = debrid_api_key
self.api_url = "https://premiumize.me/api"
+ self.debrid_api_key = debrid_api_key
async def check_premium(self):
try:
@@ -36,7 +36,7 @@ class Premiumize:
async def get_instant(self, chunk: list):
try:
response = await self.session.get(
- f"{self.api_url}/cache/check?apikey={self.debrid_api_key}&items[]={'&items[]='.join(hash for hash in chunk)}"
+ f"{self.api_url}/cache/check?apikey={self.debrid_api_key}&items[]={'&items[]='.join(chunk)}"
)
response = await response.json()
diff --git a/comet/debrid/realdebrid.py b/comet/debrid/realdebrid.py
index 32eb29d..a0ea316 100644
--- a/comet/debrid/realdebrid.py
+++ b/comet/debrid/realdebrid.py
@@ -32,7 +32,7 @@ class RealDebrid:
async def get_instant(self, chunk: list):
try:
response = await self.session.get(
- f"{self.api_url}/torrents/instantAvailability/{'/'.join(hash for hash in chunk)}"
+ f"{self.api_url}/torrents/instantAvailability/{'/'.join(chunk)}"
)
return await response.json()
except Exception as e:
@@ -135,7 +135,13 @@ class RealDebrid:
await self.session.post(
f"{self.api_url}/torrents/selectFiles/{add_magnet['id']}",
- data={"files": ",".join(str(file["id"]) for file in get_magnet_info["files"] if is_video(file["path"]))}, # "all" because bad
+ data={
+ "files": ",".join(
+ str(file["id"])
+ for file in get_magnet_info["files"]
+ if is_video(file["path"])
+ )
+ }, # "all" because bad
proxy=self.proxy,
)
diff --git a/comet/debrid/torbox.py b/comet/debrid/torbox.py
new file mode 100644
index 0000000..c59ebd8
--- /dev/null
+++ b/comet/debrid/torbox.py
@@ -0,0 +1,146 @@
+import aiohttp
+import asyncio
+
+from RTN import parse
+
+from comet.utils.general import is_video
+from comet.utils.logger import logger
+
+
+class TorBox:
+ def __init__(self, session: aiohttp.ClientSession, debrid_api_key: str):
+ session.headers["Authorization"] = f"Bearer {debrid_api_key}"
+ self.session = session
+ self.proxy = None
+
+ self.api_url = "https://api.torbox.app/v1/api"
+ self.debrid_api_key = debrid_api_key
+
+ async def check_premium(self):
+ try:
+ check_premium = await self.session.get(
+ f"{self.api_url}/user/me?settings=false"
+ )
+ check_premium = await check_premium.text()
+ if '"success":true' in check_premium and '"plan":0' not in check_premium:
+ return True
+ except Exception as e:
+ logger.warning(f"Exception while checking premium status on TorBox: {e}")
+
+ return False
+
+ async def get_instant(self, chunk: list):
+ try:
+ response = await self.session.get(
+ f"{self.api_url}/torrents/checkcached?hash={','.join(chunk)}&list_files=true"
+ )
+ return await response.json()
+ except Exception as e:
+ logger.warning(
+ f"Exception while checking hash instant availability on TorBox: {e}"
+ )
+
+ async def get_files(
+ self, torrent_hashes: list, type: str, season: str, episode: str
+ ):
+ chunk_size = 100
+ chunks = [
+ torrent_hashes[i : i + chunk_size]
+ for i in range(0, len(torrent_hashes), chunk_size)
+ ]
+
+ tasks = []
+ for chunk in chunks:
+ tasks.append(self.get_instant(chunk))
+
+ responses = await asyncio.gather(*tasks)
+
+ availability = []
+ for response in responses:
+ if response is None:
+ continue
+
+ availability.append(response)
+
+ files = {}
+ for result in availability:
+ if not result["success"]:
+ continue
+
+ if type == "series":
+ for hash in result["data"]:
+ hash_files = result["data"][hash]["files"]
+ for file in hash_files:
+ filename = file["name"].split("/")[1]
+
+ if not is_video(filename):
+ continue
+
+ filename_parsed = parse(filename)
+ if (
+ season in filename_parsed.season
+ and episode in filename_parsed.episode
+ ):
+ files[hash] = {
+ "index": hash_files.index(file),
+ "title": filename,
+ "size": file["size"],
+ }
+
+ continue
+
+ for hash in result["data"]:
+ hash_files = result["data"][hash]["files"]
+ for file in hash_files:
+ filename = file["name"].split("/")[1]
+
+ if not is_video(filename):
+ continue
+
+ files[hash] = {
+ "index": hash_files.index(file),
+ "title": filename,
+ "size": file["size"],
+ }
+
+ return files
+
+ async def generate_download_link(self, hash: str, index: str):
+ try:
+ get_torrents = await self.session.get(
+ f"{self.api_url}/torrents/mylist?bypass_cache=true"
+ )
+ get_torrents = await get_torrents.json()
+ exists = False
+ for torrent in get_torrents["data"]:
+ if torrent["hash"] == hash:
+ torrent_id = torrent["id"]
+ exists = True
+ break
+ if not exists:
+ create_torrent = await self.session.post(
+ f"{self.api_url}/torrents/createtorrent",
+ data={"magnet": f"magnet:?xt=urn:btih:{hash}"},
+ )
+ create_torrent = await create_torrent.json()
+ torrent_id = create_torrent["data"]["torrent_id"]
+
+ get_torrents = await self.session.get(
+ f"{self.api_url}/torrents/mylist?bypass_cache=true"
+ )
+ get_torrents = await get_torrents.json()
+
+ for torrent in get_torrents["data"]:
+ if torrent["id"] == torrent_id:
+ file_id = torrent["files"][int(index)]["id"]
+
+ get_download_link = await self.session.get(
+ f"{self.api_url}/torrents/requestdl?token={self.debrid_api_key}&torrent_id={torrent_id}&file_id={file_id}&zip=false",
+ )
+ get_download_link = await get_download_link.json()
+
+ return get_download_link["data"]
+ except Exception as e:
+ logger.warning(
+ f"Exception while getting download link from TorBox for {hash}|{index}: {e}"
+ )
diff --git a/comet/templates/index.html b/comet/templates/index.html
index c2d95cd..b4c981f 100644
--- a/comet/templates/index.html
+++ b/comet/templates/index.html
@@ -536,6 +536,7 @@
Real-Debrid
All-Debrid
Premiumize
+ TorBox
@@ -558,6 +559,8 @@
apiKeyLink.href = "https://alldebrid.fr/apikeys";
} else if (selectedService === "premiumize") {
apiKeyLink.href = "https://premiumize.me/account";
+ } else if (selectedService === "torbox") {
+ apiKeyLink.href = "https://torbox.app/settings";
}
});
diff --git a/comet/utils/models.py b/comet/utils/models.py
index d6a092c..e0cdd7f 100644
--- a/comet/utils/models.py
+++ b/comet/utils/models.py
@@ -67,7 +67,7 @@ class ConfigModel(BaseModel):
@field_validator("debridService")
def check_debrid_service(cls, v):
- if v not in ["realdebrid", "alldebrid", "premiumize"]:
+ if v not in ["realdebrid", "alldebrid", "premiumize", "torbox"]:
raise ValueError("Invalid debridService")
return v