diff --git a/viu_media/cli/interactive/menu/media/download_episodes.py b/viu_media/cli/interactive/menu/media/download_episodes.py index 5de2978..7a14469 100644 --- a/viu_media/cli/interactive/menu/media/download_episodes.py +++ b/viu_media/cli/interactive/menu/media/download_episodes.py @@ -8,7 +8,6 @@ def download_episodes(ctx: Context, state: State) -> State | InternalDirective: """Menu to select and download episodes synchronously.""" from viu_media.cli.utils.search import find_best_match_title from .....core.utils.normalizer import normalize_title - from ....service.download.service import DownloadService feedback = ctx.feedback selector = ctx.selector @@ -71,16 +70,11 @@ def download_episodes(ctx: Context, state: State) -> State | InternalDirective: feedback.info("No episodes selected for download.") return InternalDirective.BACK - # Step 3: Download episodes synchronously - # TODO: move to main ctx - download_service = DownloadService( - config, ctx.media_registry, ctx.media_api, ctx.provider - ) - + # Step 3: Download episodes synchronously using the session-scoped service feedback.info( f"Starting download of {len(selected_episodes)} episodes. This may take a while..." ) - download_service.download_episodes_sync(media_item, selected_episodes) + ctx.download.download_episodes_sync(media_item, selected_episodes) feedback.success(f"Finished downloading {len(selected_episodes)} episodes.") diff --git a/viu_media/cli/interactive/session.py b/viu_media/cli/interactive/session.py index fff1218..2a1adb7 100644 --- a/viu_media/cli/interactive/session.py +++ b/viu_media/cli/interactive/session.py @@ -9,6 +9,7 @@ import click from ...core.config import AppConfig from ...core.constants import APP_DIR, USER_CONFIG +from ...core.utils.concurrency import thread_manager from .state import InternalDirective, MenuName, State if TYPE_CHECKING: @@ -16,6 +17,7 @@ if TYPE_CHECKING: from ...libs.provider.anime.base import BaseAnimeProvider from ...libs.selectors.base import BaseSelector from ..service.auth import AuthService + from ..service.download.service import DownloadService from ..service.feedback import FeedbackService from ..service.player import PlayerService from ..service.registry import MediaRegistryService @@ -86,6 +88,7 @@ class Context: _selector: Optional["BaseSelector"] = None _media_api: Optional["BaseApiClient"] = None + _download: Optional["DownloadService"] = None _feedback: Optional["FeedbackService"] = None _media_registry: Optional["MediaRegistryService"] = None _watch_history: Optional["WatchHistoryService"] = None @@ -137,6 +140,16 @@ class Context: return self._media_api + @property + def download(self) -> "DownloadService": + if not self._download: + from ..service.download.service import DownloadService + + self._download = DownloadService( + self.config, self.media_registry, self.media_api, self.provider + ) + return self._download + @property def player(self) -> "PlayerService": if not self._player: @@ -206,7 +219,12 @@ class Session: _history: List[State] = [] _menus: dict[MenuName, Menu] = {} + def _shutdown_download_worker(self): + if hasattr(self, "_context") and self._context._download: + thread_manager.shutdown_worker("download_worker", wait=False, timeout=5.0) + def _load_context(self, config: AppConfig): + self._shutdown_download_worker() self._context = Context(config) logger.info("Application context reloaded.") @@ -243,6 +261,7 @@ class Session: self._context.session.create_crash_backup(self._history) raise finally: + self._shutdown_download_worker() # Clean up preview workers when session ends self._cleanup_preview_workers() self._context.session.save_session(self._history)