Add support for EasyDebrid streaming provider

This commit is contained in:
mhdzumair
2025-01-02 11:50:16 +05:30
parent 7f7826d3d8
commit 3b5aac130a
12 changed files with 180 additions and 0 deletions
+1
View File
@@ -39,6 +39,7 @@ class Settings(BaseSettings):
"premiumize",
"qbittorrent",
"stremthru",
"easydebrid",
]
] = Field(default_factory=list)
+1
View File
@@ -142,6 +142,7 @@ class StreamingProvider(BaseModel):
"premiumize",
"qbittorrent",
"stremthru",
"easydebrid",
] = Field(alias="sv")
stremthru_store_name: (
Literal[
+1
View File
@@ -35,6 +35,7 @@ These settings define the basic configuration and identity of your MediaFusion i
- "premiumize": Premiumize service
- "qbittorrent": qBittorrent client
- "stremthru": StremThru service
- "easydebrid": EasyDebrid service
## Database and Cache Settings
+1
View File
@@ -128,6 +128,7 @@ async def get_debrid_cache_metrics() -> Dict[str, Any]:
"realdebrid",
"seedr",
"torbox",
"easydebrid",
]
try:
+5
View File
@@ -100,6 +100,11 @@
"label": "AllDebrid",
"type": "Premium"
},
{
"value": "easydebrid",
"label": "EasyDebrid",
"type": "Premium"
},
{
"value": "qbittorrent",
"label": "qBittorrent - WebDav",
+1
View File
@@ -46,6 +46,7 @@
<li>🔗 Debrid-Link (Premium)</li>
<li>✨ Premiumize (Premium)</li>
<li>🏠 AllDebrid (Premium)</li>
<li>📦 EasyDebrid (Premium)</li>
<li>🔒 qBittorrent - WebDav (Free/Premium)</li>
</ul>
+1
View File
@@ -12,6 +12,7 @@ const providerSignupLinks = {
debridlink: 'https://debrid-link.com/id/kHgZs',
alldebrid: 'https://alldebrid.com/?uid=3ndha&lang=en',
torbox: ['https://torbox.app/subscription?referral=339b923e-fb23-40e7-8031-4af39c212e3c', 'https://torbox.app/subscription?referral=e2a28977-99ed-43cd-ba2c-e90dc398c49c', 'https://torbox.app/subscription?referral=38f1c266-8a6c-40b2-a6d2-2148e77dafc9'],
easydebrid: 'https://paradise-cloud.com/products/easydebrid',
premiumize: 'https://www.premiumize.me',
qbittorrent: 'https://github.com/mhdzumair/MediaFusion/tree/main/streaming_providers/qbittorrent#qbittorrent-webdav-setup-options-with-mediafusion',
stremthru: 'https://github.com/MunifTanjim/stremthru?tab=readme-ov-file#configuration',
+71
View File
@@ -0,0 +1,71 @@
from typing import Optional
from streaming_providers.debrid_client import DebridClient
from streaming_providers.exceptions import ProviderException
class EasyDebrid(DebridClient):
BASE_URL = "https://easydebrid.com/api/v1"
def __init__(self, token: Optional[str] = None, user_ip: Optional[str] = None):
self.user_ip = user_ip
super().__init__(token)
async def initialize_headers(self):
self.headers = {"Authorization": f"Bearer {self.token}"}
if self.user_ip:
self.headers['X-Forwarded-For'] = self.user_ip
async def disable_access_token(self):
pass
async def __aexit__(self, exc_type, exc_val, exc_tb):
await super().__aexit__(exc_type, exc_val, exc_tb)
async def _handle_service_specific_errors(self, error_data: dict, status_code: int):
error_code = error_data.get("error")
match error_code:
case "Unsupported link for direct download.":
raise ProviderException(
"Unsupported link for direct download.",
"unsupported_direct_download.mp4",
)
async def _make_request(
self,
method: str,
url: str,
data: Optional[dict | str] = None,
json: Optional[dict] = None,
params: Optional[dict] = None,
is_return_none: bool = False,
is_expected_to_fail: bool = False,
retry_count: int = 0,
) -> dict:
params = params or {}
url = self.BASE_URL + url
return await super()._make_request(
method, url, data, json, params, is_return_none, is_expected_to_fail
)
async def get_torrent_instant_availability(self, urls: list[str]):
response = await self._make_request(
"POST",
"/link/lookup",
json={"urls": urls},
)
return response.get("cached", [])
async def create_download_link(self, magnet):
response = await self._make_request(
"POST",
"/link/generate",
json={"url": magnet},
)
return response
async def get_torrent_info(self, torrent_id: str) -> dict:
pass
async def get_user_info(self):
return await self._make_request("GET", "/user/details")
+89
View File
@@ -0,0 +1,89 @@
import asyncio
import logging
from typing import Any, Dict, List, Optional, Iterator
from db.models import TorrentStreams
from db.schemas import UserData
from streaming_providers.exceptions import ProviderException
from streaming_providers.parser import select_file_index_from_torrent
from streaming_providers.easydebrid.client import EasyDebrid
async def get_video_url_from_easydebrid(
magnet_link: str,
user_data: UserData,
filename: str,
user_ip: str,
episode: Optional[int] = None,
**kwargs: Any,
) -> str:
async with EasyDebrid(
token=user_data.streaming_provider.token,
user_ip=user_ip,
) as easydebrid_client:
torrent_info = await easydebrid_client.create_download_link(magnet_link)
file_index = await select_file_index_from_torrent(
torrent_info,
filename,
episode,
name_key="filename",
)
return torrent_info["files"][file_index]["url"]
def divide_chunks(lst: List[Any], n: int) -> Iterator[List[Any]]:
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i : i + n]
async def update_chunk_cache_status(
easydebrid_client: EasyDebrid, streams_chunk: List[TorrentStreams]
) -> None:
"""Update cache status for a chunk of streams."""
try:
instant_availability_data = (
await easydebrid_client.get_torrent_instant_availability(
[f"magnet:?xt=urn:btih:{stream.id}" for stream in streams_chunk]
)
)
for stream, instant_availability in zip(
streams_chunk, instant_availability_data
):
stream.cached = instant_availability
except ProviderException as e:
logging.error(f"Failed to get cached status from easydebrid for a chunk: {e}")
async def update_easydebrid_cache_status(
streams: List[TorrentStreams], user_data: UserData, user_ip: str, **kwargs: Any
) -> None:
"""Updates the cache status of streams based on Easydebrid's instant availability."""
async with EasyDebrid(
token=user_data.streaming_provider.token,
user_ip=user_ip,
) as easydebrid_client:
chunks = list(divide_chunks(streams, 50))
update_tasks = [
update_chunk_cache_status(easydebrid_client, chunk) for chunk in chunks
]
await asyncio.gather(*update_tasks)
async def validate_easydebrid_credentials(
user_data: UserData, user_ip: str, **kwargs: Any
) -> Dict[str, str]:
"""Validates the EasyDebrid credentials."""
try:
async with EasyDebrid(
token=user_data.streaming_provider.token,
user_ip=user_ip,
) as easydebrid_client:
await easydebrid_client.get_user_info()
return {"status": "success"}
except ProviderException as error:
return {
"status": "error",
"message": f"Failed to validate EasyDebrid credentials: {error.message}",
}
+8
View File
@@ -68,6 +68,11 @@ from streaming_providers.stremthru.utils import (
get_video_url_from_stremthru,
validate_stremthru_credentials,
)
from streaming_providers.easydebrid.utils import (
update_easydebrid_cache_status,
get_video_url_from_easydebrid,
validate_easydebrid_credentials,
)
# Define provider-specific cache update functions
CACHE_UPDATE_FUNCTIONS = {
@@ -81,6 +86,7 @@ CACHE_UPDATE_FUNCTIONS = {
"premiumize": update_pm_cache_status,
"qbittorrent": update_qbittorrent_cache_status,
"stremthru": update_st_cache_status,
"easydebrid": update_easydebrid_cache_status,
}
# Define provider-specific downloaded info hashes fetch functions
@@ -123,6 +129,7 @@ GET_VIDEO_URL_FUNCTIONS = {
"seedr": get_video_url_from_seedr,
"torbox": get_video_url_from_torbox,
"stremthru": get_video_url_from_stremthru,
"easydebrid": get_video_url_from_easydebrid,
}
@@ -137,4 +144,5 @@ VALIDATE_CREDENTIALS_FUNCTIONS = {
"seedr": validate_seedr_credentials,
"torbox": validate_torbox_credentials,
"stremthru": validate_stremthru_credentials,
"easydebrid": validate_easydebrid_credentials,
}
+1
View File
@@ -183,6 +183,7 @@ STREAMING_PROVIDERS_SHORT_NAMES = {
"seedr": "SDR",
"torbox": "TRB",
"stremthru": "ST",
"easydebrid": "ED",
}
CERTIFICATION_MAPPING = {