mirror of
https://github.com/Viren070/MediaFusion.git
synced 2025-12-01 23:21:11 +01:00
ec20feca52
Add support for telegram notification when unable to detect episode files, refactor file selection and update db function
232 lines
7.5 KiB
Python
232 lines
7.5 KiB
Python
import asyncio
|
|
from typing import Optional
|
|
|
|
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.realdebrid.client import RealDebrid
|
|
|
|
|
|
async def create_download_link(
|
|
rd_client: RealDebrid,
|
|
magnet_link: str,
|
|
torrent_info: dict,
|
|
filename: Optional[str],
|
|
episode: Optional[int],
|
|
season: Optional[int],
|
|
stream: TorrentStreams,
|
|
max_retries: int,
|
|
retry_interval: int,
|
|
) -> str:
|
|
selected_file_index = await select_file_index_from_torrent(
|
|
torrent_info=torrent_info,
|
|
torrent_stream=stream,
|
|
filename=filename,
|
|
season=season,
|
|
episode=episode,
|
|
file_key="files",
|
|
name_key="path",
|
|
size_key="bytes",
|
|
is_filename_trustable=True,
|
|
is_index_trustable=True,
|
|
)
|
|
|
|
relevant_file = torrent_info["files"][selected_file_index]
|
|
selected_files = [file for file in torrent_info["files"] if file["selected"] == 1]
|
|
|
|
if relevant_file.get("selected") != 1 or len(selected_files) != len(
|
|
torrent_info["links"]
|
|
):
|
|
await rd_client.delete_torrent(torrent_info["id"])
|
|
torrent_id = (await rd_client.add_magnet_link(magnet_link)).get("id")
|
|
torrent_info = await rd_client.wait_for_status(
|
|
torrent_id, "waiting_files_selection", max_retries, retry_interval
|
|
)
|
|
await rd_client.start_torrent_download(
|
|
torrent_info["id"],
|
|
file_ids=torrent_info["files"][selected_file_index]["id"],
|
|
)
|
|
torrent_info = await rd_client.wait_for_status(
|
|
torrent_id, "downloaded", max_retries, retry_interval
|
|
)
|
|
link_index = 0
|
|
else:
|
|
link_index = selected_files.index(relevant_file)
|
|
|
|
response = await rd_client.create_download_link(torrent_info["links"][link_index])
|
|
|
|
if not response.get("mimeType", "").startswith("video"):
|
|
# await rd_client.delete_torrent(torrent_info["id"])
|
|
raise ProviderException(
|
|
f"Requested file is not a video file, deleting torrent and retrying. {response['mimeType']}",
|
|
"torrent_not_downloaded.mp4",
|
|
)
|
|
|
|
return response.get("download")
|
|
|
|
|
|
async def get_video_url_from_realdebrid(
|
|
info_hash: str,
|
|
magnet_link: str,
|
|
user_data: UserData,
|
|
user_ip: str,
|
|
filename: Optional[str],
|
|
stream: TorrentStreams,
|
|
max_retries=5,
|
|
retry_interval=5,
|
|
episode: Optional[int] = None,
|
|
season: Optional[int] = None,
|
|
**kwargs,
|
|
) -> str:
|
|
async with RealDebrid(
|
|
token=user_data.streaming_provider.token, user_ip=user_ip
|
|
) as rd_client:
|
|
torrent_info = await rd_client.get_available_torrent(info_hash)
|
|
|
|
if not torrent_info:
|
|
torrent_info = await add_new_torrent(
|
|
rd_client, magnet_link, info_hash, stream
|
|
)
|
|
|
|
torrent_id = torrent_info["id"]
|
|
status = torrent_info["status"]
|
|
|
|
if status in ["magnet_error", "error", "virus", "dead"]:
|
|
await rd_client.delete_torrent(torrent_id)
|
|
raise ProviderException(
|
|
f"Torrent cannot be downloaded due to status: {status}",
|
|
"transfer_error.mp4",
|
|
)
|
|
|
|
if status not in ["queued", "downloading", "downloaded"]:
|
|
torrent_info = await rd_client.wait_for_status(
|
|
torrent_id,
|
|
"waiting_files_selection",
|
|
max_retries,
|
|
retry_interval,
|
|
torrent_info,
|
|
)
|
|
try:
|
|
await rd_client.start_torrent_download(
|
|
torrent_info["id"],
|
|
file_ids="all",
|
|
)
|
|
except ProviderException as error:
|
|
await rd_client.delete_torrent(torrent_id)
|
|
raise ProviderException(
|
|
f"Failed to start torrent download, {error}", "transfer_error.mp4"
|
|
)
|
|
|
|
torrent_info = await rd_client.wait_for_status(
|
|
torrent_id, "downloaded", max_retries, retry_interval
|
|
)
|
|
|
|
return await create_download_link(
|
|
rd_client,
|
|
magnet_link,
|
|
torrent_info,
|
|
filename,
|
|
episode,
|
|
season,
|
|
stream,
|
|
max_retries,
|
|
retry_interval,
|
|
)
|
|
|
|
|
|
async def add_new_torrent(rd_client, magnet_link, info_hash, stream):
|
|
response = await rd_client.get_active_torrents()
|
|
if response["limit"] == response["nb"]:
|
|
raise ProviderException(
|
|
"Torrent limit reached. Please try again later.", "torrent_limit.mp4"
|
|
)
|
|
if info_hash in response["list"]:
|
|
raise ProviderException(
|
|
"Torrent is already being downloading", "torrent_not_downloaded.mp4"
|
|
)
|
|
|
|
if stream.torrent_file:
|
|
torrent_id = (await rd_client.add_torrent_file(stream.torrent_file)).get("id")
|
|
else:
|
|
torrent_id = (await rd_client.add_magnet_link(magnet_link)).get("id")
|
|
|
|
if not torrent_id:
|
|
raise ProviderException(
|
|
"Failed to add magnet link to Real-Debrid", "transfer_error.mp4"
|
|
)
|
|
|
|
return await rd_client.get_torrent_info(torrent_id)
|
|
|
|
|
|
async def update_rd_cache_status(
|
|
streams: list[TorrentStreams], user_data: UserData, user_ip: str, **kwargs
|
|
):
|
|
"""Updates the cache status of streams based on user's downloaded torrents in RealDebrid."""
|
|
|
|
try:
|
|
downloaded_hashes = set(
|
|
await fetch_downloaded_info_hashes_from_rd(user_data, user_ip, **kwargs)
|
|
)
|
|
if not downloaded_hashes:
|
|
return
|
|
for stream in streams:
|
|
stream.cached = stream.id in downloaded_hashes
|
|
|
|
except ProviderException:
|
|
pass
|
|
|
|
|
|
async def fetch_downloaded_info_hashes_from_rd(
|
|
user_data: UserData, user_ip: str, **kwargs
|
|
) -> list[str]:
|
|
"""Fetches the info_hashes of all torrents downloaded in the RealDebrid account."""
|
|
try:
|
|
async with RealDebrid(
|
|
token=user_data.streaming_provider.token, user_ip=user_ip
|
|
) as rd_client:
|
|
available_torrents = await rd_client.get_user_torrent_list()
|
|
return [
|
|
torrent["hash"]
|
|
for torrent in available_torrents
|
|
if torrent["status"] == "downloaded"
|
|
]
|
|
|
|
except ProviderException:
|
|
return []
|
|
|
|
|
|
async def delete_all_watchlist_rd(user_data: UserData, user_ip: str, **kwargs):
|
|
"""Deletes all torrents from the RealDebrid watchlist."""
|
|
async with RealDebrid(
|
|
token=user_data.streaming_provider.token, user_ip=user_ip
|
|
) as rd_client:
|
|
torrents = await rd_client.get_user_torrent_list()
|
|
semaphore = asyncio.Semaphore(3)
|
|
|
|
async def delete_torrent(torrent_id):
|
|
async with semaphore:
|
|
await rd_client.delete_torrent(torrent_id)
|
|
|
|
await asyncio.gather(
|
|
*[delete_torrent(torrent["id"]) for torrent in torrents],
|
|
return_exceptions=True,
|
|
)
|
|
|
|
|
|
async def validate_realdebrid_credentials(user_data: UserData, user_ip: str) -> dict:
|
|
"""Validates the RealDebrid credentials."""
|
|
try:
|
|
async with RealDebrid(
|
|
token=user_data.streaming_provider.token, user_ip=user_ip
|
|
) as rd_client:
|
|
await rd_client.get_user_info()
|
|
return {"status": "success"}
|
|
except ProviderException as error:
|
|
return {
|
|
"status": "error",
|
|
"message": f"Failed to verify RealDebrid credential, error: {error.message}",
|
|
}
|