"""Subtitle domain services - Business logic.""" import logging from typing import List, Optional from ..shared.value_objects import ImdbId, FilePath from .entities import Subtitle from .value_objects import Language, SubtitleFormat from .repositories import SubtitleRepository from .exceptions import SubtitleNotFound logger = logging.getLogger(__name__) class SubtitleService: """ Domain service for subtitle-related business logic. This service is SHARED between movies and TV shows domains. Both can use this service to manage subtitles. """ def __init__(self, repository: SubtitleRepository): """ Initialize subtitle service. Args: repository: Subtitle repository for persistence """ self.repository = repository def add_subtitle(self, subtitle: Subtitle) -> None: """ Add a subtitle to the library. Args: subtitle: Subtitle entity to add """ self.repository.save(subtitle) logger.info(f"Added subtitle: {subtitle.language.value} for {subtitle.media_imdb_id}") def find_subtitles_for_movie( self, imdb_id: ImdbId, languages: Optional[List[Language]] = None ) -> List[Subtitle]: """ Find subtitles for a movie. Args: imdb_id: IMDb ID of the movie languages: Optional list of languages to filter by Returns: List of matching subtitles """ if languages: all_subtitles = [] for lang in languages: subs = self.repository.find_by_media(imdb_id, language=lang) all_subtitles.extend(subs) return all_subtitles else: return self.repository.find_by_media(imdb_id) def find_subtitles_for_episode( self, imdb_id: ImdbId, season: int, episode: int, languages: Optional[List[Language]] = None ) -> List[Subtitle]: """ Find subtitles for a TV show episode. Args: imdb_id: IMDb ID of the TV show season: Season number episode: Episode number languages: Optional list of languages to filter by Returns: List of matching subtitles """ if languages: all_subtitles = [] for lang in languages: subs = self.repository.find_by_media( imdb_id, language=lang, season=season, episode=episode ) all_subtitles.extend(subs) return all_subtitles else: return self.repository.find_by_media( imdb_id, season=season, episode=episode ) def remove_subtitle(self, subtitle: Subtitle) -> None: """ Remove a subtitle from the library. Args: subtitle: Subtitle to remove Raises: SubtitleNotFound: If subtitle not found """ if not self.repository.delete(subtitle): raise SubtitleNotFound(f"Subtitle not found: {subtitle}") logger.info(f"Removed subtitle: {subtitle}") def detect_format_from_file(self, file_path: FilePath) -> SubtitleFormat: """ Detect subtitle format from file extension. Args: file_path: Path to subtitle file Returns: Detected subtitle format """ extension = file_path.value.suffix return SubtitleFormat.from_extension(extension) def validate_subtitle_file(self, file_path: FilePath) -> bool: """ Validate that a file is a valid subtitle file. Args: file_path: Path to the file Returns: True if valid subtitle file, False otherwise """ if not file_path.exists(): logger.warning(f"File does not exist: {file_path}") return False if not file_path.is_file(): logger.warning(f"Path is not a file: {file_path}") return False # Check file extension try: self.detect_format_from_file(file_path) return True except Exception as e: logger.warning(f"Invalid subtitle format: {e}") return False