155 lines
4.5 KiB
Python
155 lines
4.5 KiB
Python
"""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
|