Files
MediaManager-maxdorninger/media_manager/torrent/manager.py
maxDorninger 831f9dc1ac format files
2025-07-15 20:05:37 +02:00

161 lines
6.0 KiB
Python

import logging
from enum import Enum
from media_manager.config import AllEncompassingConfig
from media_manager.indexer.schemas import IndexerQueryResult
from media_manager.torrent.download_clients.abstractDownloadClient import (
AbstractDownloadClient,
)
from media_manager.torrent.download_clients.qbittorrent import QbittorrentDownloadClient
from media_manager.torrent.download_clients.transmission import (
TransmissionDownloadClient,
)
from media_manager.torrent.download_clients.sabnzbd import SabnzbdDownloadClient
from media_manager.torrent.schemas import Torrent, TorrentStatus
log = logging.getLogger(__name__)
class DownloadClientType(Enum):
"""Types of download clients supported"""
TORRENT = "torrent"
USENET = "usenet"
class DownloadManager:
"""
Manages download clients and routes downloads to the appropriate client
based on the content type (torrent vs usenet).
Only one torrent client and one usenet client are active at a time.
"""
def __init__(self):
self._torrent_client: AbstractDownloadClient | None = None
self._usenet_client: AbstractDownloadClient | None = None
self.config = AllEncompassingConfig().torrents
self._initialize_clients()
def _initialize_clients(self) -> None:
"""Initialize and register the default download clients"""
# Initialize torrent clients (prioritize qBittorrent, fallback to Transmission)
if self.config.qbittorrent.enabled:
try:
self._torrent_client = QbittorrentDownloadClient()
log.info(
"qBittorrent client initialized and set as active torrent client"
)
except Exception as e:
log.error(f"Failed to initialize qBittorrent client: {e}")
# If qBittorrent is not available or failed, try Transmission
if self._torrent_client is None and self.config.transmission.enabled:
try:
self._torrent_client = TransmissionDownloadClient()
log.info(
"Transmission client initialized and set as active torrent client"
)
except Exception as e:
log.error(f"Failed to initialize Transmission client: {e}")
# Initialize SABnzbd client for usenet
if self.config.sabnzbd.enabled:
try:
self._usenet_client = SabnzbdDownloadClient()
log.info("SABnzbd client initialized and set as active usenet client")
except Exception as e:
log.error(f"Failed to initialize SABnzbd client: {e}")
active_clients = []
if self._torrent_client:
active_clients.append(f"torrent ({self._torrent_client.name})")
if self._usenet_client:
active_clients.append(f"usenet ({self._usenet_client.name})")
log.info(
f"Download manager initialized with active download clients: {', '.join(active_clients) if active_clients else 'none'}"
)
def _get_appropriate_client(
self, indexer_result: IndexerQueryResult | Torrent
) -> AbstractDownloadClient:
"""
Select the appropriate download client based on the indexer result
:param indexer_result: The indexer query result to determine client type
:return: The appropriate download client
:raises RuntimeError: If no suitable client is available
"""
# Use the usenet flag from the indexer result to determine the client type
if indexer_result.usenet:
if not self._usenet_client:
raise RuntimeError("No usenet download client configured")
log.info(
f"Selected usenet client: {self._usenet_client.__class__.__name__}"
)
return self._usenet_client
else:
if not self._torrent_client:
raise RuntimeError("No torrent download client configured")
log.info(
f"Selected torrent client: {self._torrent_client.__class__.__name__}"
)
return self._torrent_client
def download(self, indexer_result: IndexerQueryResult) -> Torrent:
"""
Download content using the appropriate client
:param indexer_result: The indexer query result to download
:return: The torrent object representing the download
"""
log.info(f"Processing download request for: {indexer_result.title}")
client = self._get_appropriate_client(indexer_result)
return client.download_torrent(indexer_result)
def remove_torrent(self, torrent: Torrent, delete_data: bool = False) -> None:
"""
Remove a torrent using the appropriate client
:param torrent: The torrent to remove
:param delete_data: Whether to delete the downloaded data
"""
log.info(f"Removing torrent: {torrent.title}")
client = self._get_appropriate_client(torrent)
client.remove_torrent(torrent, delete_data)
def get_torrent_status(self, torrent: Torrent) -> TorrentStatus:
"""
Get the status of a torrent using the appropriate client
:param torrent: The torrent to get status for
:return: The current status of the torrent
"""
client = self._get_appropriate_client(torrent)
return client.get_torrent_status(torrent)
def pause_torrent(self, torrent: Torrent) -> None:
"""
Pause a torrent using the appropriate client
:param torrent: The torrent to pause
"""
log.info(f"Pausing torrent: {torrent.title}")
client = self._get_appropriate_client(torrent)
client.pause_torrent(torrent)
def resume_torrent(self, torrent: Torrent) -> None:
"""
Resume a torrent using the appropriate client
:param torrent: The torrent to resume
"""
log.info(f"Resuming torrent: {torrent.title}")
client = self._get_appropriate_client(torrent)
client.resume_torrent(torrent)