Merge pull request #194 from Castrozan/master

fix: move DownloadService to session Context to prevent worker registration crash
This commit is contained in:
Type-Delta
2026-04-06 08:53:18 +07:00
committed by GitHub
2 changed files with 21 additions and 8 deletions

View File

@@ -8,7 +8,6 @@ def download_episodes(ctx: Context, state: State) -> State | InternalDirective:
"""Menu to select and download episodes synchronously.""" """Menu to select and download episodes synchronously."""
from viu_media.cli.utils.search import find_best_match_title from viu_media.cli.utils.search import find_best_match_title
from .....core.utils.normalizer import normalize_title from .....core.utils.normalizer import normalize_title
from ....service.download.service import DownloadService
feedback = ctx.feedback feedback = ctx.feedback
selector = ctx.selector selector = ctx.selector
@@ -71,16 +70,11 @@ def download_episodes(ctx: Context, state: State) -> State | InternalDirective:
feedback.info("No episodes selected for download.") feedback.info("No episodes selected for download.")
return InternalDirective.BACK return InternalDirective.BACK
# Step 3: Download episodes synchronously # Step 3: Download episodes synchronously using the session-scoped service
# TODO: move to main ctx
download_service = DownloadService(
config, ctx.media_registry, ctx.media_api, ctx.provider
)
feedback.info( feedback.info(
f"Starting download of {len(selected_episodes)} episodes. This may take a while..." 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.") feedback.success(f"Finished downloading {len(selected_episodes)} episodes.")

View File

@@ -9,6 +9,7 @@ import click
from ...core.config import AppConfig from ...core.config import AppConfig
from ...core.constants import APP_DIR, USER_CONFIG from ...core.constants import APP_DIR, USER_CONFIG
from ...core.utils.concurrency import thread_manager
from .state import InternalDirective, MenuName, State from .state import InternalDirective, MenuName, State
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -16,6 +17,7 @@ if TYPE_CHECKING:
from ...libs.provider.anime.base import BaseAnimeProvider from ...libs.provider.anime.base import BaseAnimeProvider
from ...libs.selectors.base import BaseSelector from ...libs.selectors.base import BaseSelector
from ..service.auth import AuthService from ..service.auth import AuthService
from ..service.download.service import DownloadService
from ..service.feedback import FeedbackService from ..service.feedback import FeedbackService
from ..service.player import PlayerService from ..service.player import PlayerService
from ..service.registry import MediaRegistryService from ..service.registry import MediaRegistryService
@@ -86,6 +88,7 @@ class Context:
_selector: Optional["BaseSelector"] = None _selector: Optional["BaseSelector"] = None
_media_api: Optional["BaseApiClient"] = None _media_api: Optional["BaseApiClient"] = None
_download: Optional["DownloadService"] = None
_feedback: Optional["FeedbackService"] = None _feedback: Optional["FeedbackService"] = None
_media_registry: Optional["MediaRegistryService"] = None _media_registry: Optional["MediaRegistryService"] = None
_watch_history: Optional["WatchHistoryService"] = None _watch_history: Optional["WatchHistoryService"] = None
@@ -137,6 +140,16 @@ class Context:
return self._media_api 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 @property
def player(self) -> "PlayerService": def player(self) -> "PlayerService":
if not self._player: if not self._player:
@@ -206,7 +219,12 @@ class Session:
_history: List[State] = [] _history: List[State] = []
_menus: dict[MenuName, Menu] = {} _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): def _load_context(self, config: AppConfig):
self._shutdown_download_worker()
self._context = Context(config) self._context = Context(config)
logger.info("Application context reloaded.") logger.info("Application context reloaded.")
@@ -243,6 +261,7 @@ class Session:
self._context.session.create_crash_backup(self._history) self._context.session.create_crash_backup(self._history)
raise raise
finally: finally:
self._shutdown_download_worker()
# Clean up preview workers when session ends # Clean up preview workers when session ends
self._cleanup_preview_workers() self._cleanup_preview_workers()
self._context.session.save_session(self._history) self._context.session.save_session(self._history)