From 15b22e75aa4c4e0d32e3dbc8d76cd1c45eeedde6 Mon Sep 17 00:00:00 2001 From: Goldy <153996346+g0ldyy@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:07:33 +0100 Subject: [PATCH] feat: easydebrid support --- comet/api/stream.py | 21 ++--- comet/debrid/easydebrid.py | 178 +++++++++++++++++++++++++++++++++++++ comet/debrid/manager.py | 3 + comet/templates/index.html | 25 +++++- comet/utils/general.py | 22 ++++- comet/utils/models.py | 2 +- 6 files changed, 232 insertions(+), 19 deletions(-) create mode 100644 comet/debrid/easydebrid.py diff --git a/comet/api/stream.py b/comet/api/stream.py index 239c21c2..be2a8485 100644 --- a/comet/api/stream.py +++ b/comet/api/stream.py @@ -147,7 +147,7 @@ async def stream( config["debridApiKey"] = settings.PROXY_DEBRID_STREAM_DEBRID_DEFAULT_APIKEY if config["debridApiKey"] == "": - services = ["realdebrid", "alldebrid", "premiumize", "torbox", "debridlink"] + services = ["realdebrid", "alldebrid", "premiumize", "torbox", "debridlink", "easydebrid"] debrid_emoji = "⬇️" else: services = [config["debridService"]] @@ -445,17 +445,6 @@ async def stream( ) if len_sorted_ranked_files == 0: - if config["debridApiKey"] == "realdebrid": - return { - "streams": [ - { - "name": "[⚠️] Comet", - "description": "RealDebrid API is unstable!", - "url": "https://comet.fast", - } - ] - } - return {"streams": []} sorted_ranked_files = { @@ -470,8 +459,14 @@ async def stream( sorted_ranked_files[hash]["data"]["tracker"] = torrents_by_hash[hash][ "Tracker" ] - sorted_ranked_files[hash]["data"]["size"] = files[hash]["size"] torrent_size = torrents_by_hash[hash]["Size"] + sorted_ranked_files[hash]["data"]["size"] = ( + files[hash]["size"] + if config["debridService"] != "easydebrid" + else torrent_size + if torrent_size + else 0 + ) sorted_ranked_files[hash]["data"]["torrent_size"] = ( torrent_size if torrent_size else files[hash]["size"] ) diff --git a/comet/debrid/easydebrid.py b/comet/debrid/easydebrid.py new file mode 100644 index 00000000..7af08048 --- /dev/null +++ b/comet/debrid/easydebrid.py @@ -0,0 +1,178 @@ +import aiohttp +import asyncio + +from RTN import parse + +from comet.utils.general import is_video +from comet.utils.logger import logger + + +class EasyDebrid: + def __init__(self, session: aiohttp.ClientSession, debrid_api_key: str, ip: str): + self.session = session + self.ip = ip + self.proxy = None + + self.api_url = "https://easydebrid.com/api/v1" + self.headers = {"Authorization": f"Bearer {debrid_api_key}"} + + if ip: + self.headers["X-Forwarded-For"] = ip + + async def check_premium(self): + try: + response = await self.session.get( + f"{self.api_url}/user/details", headers=self.headers + ) + data = await response.json() + return bool(data["paid_until"]) + except Exception as e: + logger.warning(f"Failed to check EasyDebrid premium status: {e}") + + return False + + async def get_instant(self, chunk): + try: + response = await self.session.post( + f"{self.api_url}/link/lookup", + json={"urls": chunk}, + headers=self.headers, + ) + data = await response.json() + + if not data or "cached" not in data: + return None + + return { + "status": "success", + "response": data["cached"], + "filename": data.get("filenames", []), + "filesize": [None] * len(chunk), + "hashes": chunk, + } + except Exception as e: + logger.warning( + f"Exception while checking hash instant availability on EasyDebrid: {e}" + ) + + async def get_files(self, torrent_hashes, type, season, episode, kitsu): + 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) + + files = {} + + if type == "series": + for result in responses: + if result["status"] != "success": + continue + + responses = result["response"] + filenames = result["filename"] + hashes = result["hashes"] + + for index, (is_cached, hash) in enumerate(zip(responses, hashes)): + if not is_cached: + continue + + hash_files = filenames[index] + + for filename in hash_files: + if not is_video(filename): + continue + + if "sample" in filename.lower(): + continue + + filename_parsed = parse(filename) + if not filename_parsed: + continue + + if episode not in filename_parsed.episodes: + continue + + if kitsu: + if filename_parsed.seasons: + continue + elif season not in filename_parsed.seasons: + continue + + files[hash] = { + "index": f"{season}|{episode}", + "title": filename, + "size": 0, # Size not available in lookup response + } + break # Found matching video file + else: + for result in responses: + if result["status"] != "success": + continue + + responses = result["response"] + filenames = result["filename"] + hashes = result["hashes"] + + for index, (is_cached, hash) in enumerate(zip(responses, hashes)): + if not is_cached: + continue + + hash_files = filenames[index] + + video_files = [f for f in hash_files if is_video(f)] + if not video_files: + continue + + # Use first valid video file found + files[hash] = { + "index": 0, + "title": video_files[0], + "size": 0, # Size not available in lookup response + } + + return files + + async def generate_download_link(self, hash, index): + try: + response = await self.session.post( + f"{self.api_url}/link/generate", + headers={**self.headers, "Content-Type": "application/json"}, + json={"url": f"magnet:?xt=urn:btih:{hash}"}, + ) + data = await response.json() + + if not data or "files" not in data: + return None + + video_files = [ + f + for f in data["files"] + if is_video(f["filename"]) and "sample" not in f["filename"].lower() + ] + + if not video_files: + return None + + if "|" in str(index): + season, episode = map(int, index.split("|")) + for file in video_files: + parsed = parse(file["filename"]) + if ( + parsed + and season in parsed.seasons + and episode in parsed.episodes + ): + return file["url"] + + largest_file = max(video_files, key=lambda x: x["size"]) + + return largest_file["url"] + except Exception as e: + logger.warning(f"Error generating link for {hash}|{index}: {e}") diff --git a/comet/debrid/manager.py b/comet/debrid/manager.py index 8b9385bd..415348c0 100644 --- a/comet/debrid/manager.py +++ b/comet/debrid/manager.py @@ -5,6 +5,7 @@ from .premiumize import Premiumize from .torbox import TorBox from .debridlink import DebridLink +from .easydebrid import EasyDebrid def getDebrid(session: aiohttp.ClientSession, config: dict, ip: str): @@ -20,3 +21,5 @@ def getDebrid(session: aiohttp.ClientSession, config: dict, ip: str): return TorBox(session, debrid_api_key) elif debrid_service == "debridlink": return DebridLink(session, debrid_api_key) + elif debrid_service == "easydebrid": + return EasyDebrid(session, debrid_api_key, ip) \ No newline at end of file diff --git a/comet/templates/index.html b/comet/templates/index.html index d4270595..361b11bc 100644 --- a/comet/templates/index.html +++ b/comet/templates/index.html @@ -540,9 +540,10 @@