mirror of
https://github.com/shadoll/moma.git
synced 2026-08-28 03:27:34 +00:00
259 lines
9.5 KiB
Python
259 lines
9.5 KiB
Python
"""Media metadata extraction coordinator.
|
|
|
|
This module provides the MediaExtractor class which coordinates multiple
|
|
specialized extractors to gather comprehensive metadata about media files.
|
|
It implements a priority-based extraction system where data is retrieved
|
|
from the most appropriate source.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from .filename_extractor import FilenameExtractor
|
|
from .metadata_extractor import MetadataExtractor
|
|
from .mediainfo_extractor import MediaInfoExtractor
|
|
from .fileinfo_extractor import FileInfoExtractor
|
|
from .tmdb_extractor import TMDBExtractor
|
|
from .default_extractor import DefaultExtractor
|
|
|
|
|
|
class MediaExtractor:
|
|
"""Coordinator for extracting metadata from media files using multiple specialized extractors.
|
|
|
|
This class manages a collection of specialized extractors and provides a unified
|
|
interface for retrieving metadata. It implements a priority-based system where
|
|
each type of data is retrieved from the most appropriate source.
|
|
|
|
The extraction priority order varies by data type:
|
|
- Title: TMDB → Metadata → Filename → Default
|
|
- Year: Filename → Default
|
|
- Technical info: MediaInfo → Default
|
|
- File info: FileInfo → Default
|
|
|
|
Attributes:
|
|
file_path: Path to the media file
|
|
filename_extractor: Extracts metadata from filename patterns
|
|
metadata_extractor: Extracts embedded metadata tags
|
|
mediainfo_extractor: Extracts technical media information
|
|
fileinfo_extractor: Extracts basic file system information
|
|
tmdb_extractor: Fetches metadata from The Movie Database API
|
|
default_extractor: Provides fallback default values
|
|
|
|
Example:
|
|
>>> from pathlib import Path
|
|
>>> extractor = MediaExtractor(Path("Movie (2020) [1080p].mkv"))
|
|
>>> title = extractor.get("title")
|
|
>>> year = extractor.get("year")
|
|
>>> tracks = extractor.get("video_tracks")
|
|
"""
|
|
|
|
def __init__(self, file_path: Path):
|
|
self.file_path = file_path
|
|
|
|
self.filename_extractor = FilenameExtractor(file_path)
|
|
self.metadata_extractor = MetadataExtractor(file_path)
|
|
self.mediainfo_extractor = MediaInfoExtractor(file_path)
|
|
self.fileinfo_extractor = FileInfoExtractor(file_path)
|
|
self.tmdb_extractor = TMDBExtractor(file_path)
|
|
self.default_extractor = DefaultExtractor()
|
|
|
|
# Extractor mapping
|
|
self._extractors = {
|
|
"Metadata": self.metadata_extractor,
|
|
"Filename": self.filename_extractor,
|
|
"MediaInfo": self.mediainfo_extractor,
|
|
"FileInfo": self.fileinfo_extractor,
|
|
"TMDB": self.tmdb_extractor,
|
|
"Default": self.default_extractor,
|
|
}
|
|
|
|
# Define sources and conditions for each data type
|
|
self._data = {
|
|
"title": {
|
|
"sources": [
|
|
("TMDB", "extract_title"),
|
|
("Metadata", "extract_title"),
|
|
("Filename", "extract_title"),
|
|
("Default", "extract_title"),
|
|
],
|
|
},
|
|
"year": {
|
|
"sources": [
|
|
("Filename", "extract_year"),
|
|
("Default", "extract_year"),
|
|
],
|
|
},
|
|
"source": {
|
|
"sources": [
|
|
("Filename", "extract_source"),
|
|
("Default", "extract_source"),
|
|
],
|
|
},
|
|
"order": {
|
|
"sources": [
|
|
("Filename", "extract_order"),
|
|
("Default", "extract_order"),
|
|
],
|
|
},
|
|
"frame_class": {
|
|
"sources": [
|
|
("MediaInfo", "extract_frame_class"),
|
|
("Filename", "extract_frame_class"),
|
|
("Default", "extract_frame_class"),
|
|
],
|
|
},
|
|
"resolution": {
|
|
"sources": [
|
|
("MediaInfo", "extract_resolution"),
|
|
("Default", "extract_resolution"),
|
|
],
|
|
},
|
|
"hdr": {
|
|
"sources": [
|
|
("MediaInfo", "extract_hdr"),
|
|
("Filename", "extract_hdr"),
|
|
("Default", "extract_hdr"),
|
|
],
|
|
},
|
|
"anamorphic": {
|
|
"sources": [
|
|
("MediaInfo", "extract_anamorphic"),
|
|
("Default", "extract_anamorphic"),
|
|
],
|
|
},
|
|
"3d_layout": {
|
|
"sources": [
|
|
("MediaInfo", "extract_3d_layout"),
|
|
("Default", "extract_3d_layout"),
|
|
],
|
|
},
|
|
"movie_db": {
|
|
"sources": [
|
|
("TMDB", "extract_movie_db"),
|
|
("Filename", "extract_movie_db"),
|
|
("Default", "extract_movie_db"),
|
|
],
|
|
},
|
|
"special_info": {
|
|
"sources": [
|
|
("Filename", "extract_special_info"),
|
|
("Default", "extract_special_info"),
|
|
],
|
|
},
|
|
"audio_langs": {
|
|
"sources": [
|
|
("MediaInfo", "extract_audio_langs"),
|
|
("Filename", "extract_audio_langs"),
|
|
("Default", "extract_audio_langs"),
|
|
],
|
|
},
|
|
"meta_type": {
|
|
"sources": [
|
|
("Metadata", "extract_meta_type"),
|
|
("Default", "extract_meta_type"),
|
|
],
|
|
},
|
|
"file_size": {
|
|
"sources": [
|
|
("FileInfo", "extract_size"),
|
|
("Default", "extract_size"),
|
|
],
|
|
},
|
|
"modification_time": {
|
|
"sources": [
|
|
("FileInfo", "extract_modification_time"),
|
|
("Default", "extract_modification_time"),
|
|
],
|
|
},
|
|
"file_name": {
|
|
"sources": [
|
|
("FileInfo", "extract_file_name"),
|
|
("Default", "extract_file_name"),
|
|
],
|
|
},
|
|
"file_path": {
|
|
"sources": [
|
|
("FileInfo", "extract_file_path"),
|
|
("Default", "extract_file_path"),
|
|
],
|
|
},
|
|
"extension": {
|
|
"sources": [
|
|
("MediaInfo", "extract_extension"),
|
|
("FileInfo", "extract_extension"),
|
|
("Default", "extract_extension"),
|
|
],
|
|
},
|
|
"video_tracks": {
|
|
"sources": [
|
|
("MediaInfo", "extract_video_tracks"),
|
|
("Default", "extract_video_tracks"),
|
|
],
|
|
},
|
|
"audio_tracks": {
|
|
"sources": [
|
|
("MediaInfo", "extract_audio_tracks"),
|
|
("Default", "extract_audio_tracks"),
|
|
],
|
|
},
|
|
"subtitle_tracks": {
|
|
"sources": [
|
|
("MediaInfo", "extract_subtitle_tracks"),
|
|
("Default", "extract_subtitle_tracks"),
|
|
],
|
|
},
|
|
"genres": {
|
|
"sources": [
|
|
("TMDB", "extract_genres"),
|
|
("Default", "extract_genres"),
|
|
],
|
|
},
|
|
"production_countries": {
|
|
"sources": [
|
|
("TMDB", "extract_production_countries"),
|
|
],
|
|
},
|
|
}
|
|
|
|
def get(self, key: str, source: str | None = None):
|
|
"""Get metadata value by key, optionally from a specific source.
|
|
|
|
Retrieves metadata using a priority-based system. If a source is specified,
|
|
only that extractor is used. Otherwise, extractors are tried in priority
|
|
order until a non-None value is found.
|
|
|
|
Args:
|
|
key: The metadata key to retrieve (e.g., "title", "year", "resolution")
|
|
source: Optional specific extractor to use ("TMDB", "MediaInfo", "Filename", etc.)
|
|
|
|
Returns:
|
|
The extracted metadata value, or None if not found
|
|
|
|
Example:
|
|
>>> extractor = MediaExtractor(Path("movie.mkv"))
|
|
>>> title = extractor.get("title") # Try all sources in priority order
|
|
>>> year = extractor.get("year", source="Filename") # Use only filename
|
|
"""
|
|
if source:
|
|
# Specific source requested - find the extractor and call the method directly
|
|
for extractor_name, extractor in self._extractors.items():
|
|
if extractor_name.lower() == source.lower():
|
|
method = f"extract_{key}"
|
|
if hasattr(extractor, method):
|
|
val = getattr(extractor, method)()
|
|
return val if val is not None else None
|
|
return None
|
|
|
|
# Fallback mode - try sources in order
|
|
if key in self._data:
|
|
sources = self._data[key]["sources"]
|
|
else:
|
|
# Try extractors in order for unconfigured keys
|
|
sources = [(name, f"extract_{key}") for name in ["MediaInfo", "Metadata", "Filename", "FileInfo"]]
|
|
|
|
# Try each source in order until a valid value is found
|
|
for src, method in sources:
|
|
if src in self._extractors and hasattr(self._extractors[src], method):
|
|
val = getattr(self._extractors[src], method)()
|
|
if val is not None:
|
|
return val
|
|
return None
|