"""Metadata extraction service for media files.""" import logging from pathlib import Path from typing import Optional logger = logging.getLogger(__name__) class MetadataService: """Service for extracting metadata from media files.""" @staticmethod def extract_audio_metadata(file_path: Path) -> dict: """Extract metadata from an audio file. Args: file_path: Path to the audio file. Returns: Dictionary with audio metadata. """ try: import mutagen from mutagen import File as MutagenFile audio = MutagenFile(str(file_path), easy=True) if audio is None: return {"error": "Unable to read audio file"} metadata = { "type": "audio", "filename": file_path.name, "path": str(file_path), } # Extract duration if hasattr(audio.info, "length"): metadata["duration"] = round(audio.info.length, 2) # Extract bitrate if hasattr(audio.info, "bitrate"): metadata["bitrate"] = audio.info.bitrate # Extract sample rate if hasattr(audio.info, "sample_rate"): metadata["sample_rate"] = audio.info.sample_rate elif hasattr(audio.info, "samplerate"): metadata["sample_rate"] = audio.info.samplerate # Extract channels if hasattr(audio.info, "channels"): metadata["channels"] = audio.info.channels # Extract tags (use easy=True for consistent tag names) if audio is not None and hasattr(audio, "tags") and audio.tags: # Easy tags provide lists, so we take the first item tags = audio.tags if "title" in tags: metadata["title"] = tags["title"][0] if isinstance(tags["title"], list) else tags["title"] if "artist" in tags: metadata["artist"] = tags["artist"][0] if isinstance(tags["artist"], list) else tags["artist"] if "album" in tags: metadata["album"] = tags["album"][0] if isinstance(tags["album"], list) else tags["album"] if "albumartist" in tags: metadata["album_artist"] = tags["albumartist"][0] if isinstance(tags["albumartist"], list) else tags["albumartist"] if "date" in tags: metadata["date"] = tags["date"][0] if isinstance(tags["date"], list) else tags["date"] if "genre" in tags: metadata["genre"] = tags["genre"][0] if isinstance(tags["genre"], list) else tags["genre"] if "tracknumber" in tags: metadata["track_number"] = tags["tracknumber"][0] if isinstance(tags["tracknumber"], list) else tags["tracknumber"] # If no title tag, use filename if "title" not in metadata: metadata["title"] = file_path.stem return metadata except ImportError: logger.error("mutagen library not installed, cannot extract metadata") return {"error": "mutagen library not installed"} except Exception as e: logger.error(f"Error extracting audio metadata from {file_path}: {e}") return { "error": str(e), "filename": file_path.name, "title": file_path.stem, } @staticmethod def extract_video_metadata(file_path: Path) -> dict: """Extract basic metadata from a video file. Args: file_path: Path to the video file. Returns: Dictionary with video metadata. """ try: import mutagen from mutagen import File as MutagenFile video = MutagenFile(str(file_path)) if video is None: return { "type": "video", "filename": file_path.name, "title": file_path.stem, } metadata = { "type": "video", "filename": file_path.name, "path": str(file_path), } # Extract duration if hasattr(video.info, "length"): metadata["duration"] = round(video.info.length, 2) # Extract bitrate if hasattr(video.info, "bitrate"): metadata["bitrate"] = video.info.bitrate # Extract video-specific properties if available if hasattr(video.info, "width"): metadata["width"] = video.info.width if hasattr(video.info, "height"): metadata["height"] = video.info.height # Try to extract title from tags if hasattr(video, "tags") and video.tags: tags = video.tags if hasattr(tags, "get"): title = tags.get("title") or tags.get("TITLE") or tags.get("\xa9nam") if title: metadata["title"] = title[0] if isinstance(title, list) else str(title) # If no title tag, use filename if "title" not in metadata: metadata["title"] = file_path.stem return metadata except ImportError: logger.error("mutagen library not installed, cannot extract metadata") return { "error": "mutagen library not installed", "type": "video", "filename": file_path.name, "title": file_path.stem, } except Exception as e: logger.debug(f"Error extracting video metadata from {file_path}: {e}") # Return basic metadata return { "type": "video", "filename": file_path.name, "title": file_path.stem, } @staticmethod def extract_metadata(file_path: Path) -> dict: """Extract metadata from a media file (auto-detect type). Args: file_path: Path to the media file. Returns: Dictionary with media metadata. """ from .browser_service import AUDIO_EXTENSIONS, VIDEO_EXTENSIONS suffix = file_path.suffix.lower() if suffix in AUDIO_EXTENSIONS: return MetadataService.extract_audio_metadata(file_path) elif suffix in VIDEO_EXTENSIONS: return MetadataService.extract_video_metadata(file_path) else: return { "error": "Unsupported file type", "filename": file_path.name, }