Music_Metadata_Enricher/models.py
dschlueter b6abfae16c Add YouTube ID detection and metadata lookup via yt-dlp
- Extract 11-char YouTube video IDs from audio filenames
- Fetch title, uploader, chapters via yt-dlp (--dump-json)
- Use chapters as tracklist when no .txt tracklist is available
- Store yt_title / yt_uploader in AlbumHints for LLM prompt context
- Fall back to YouTube video title as track title for single-file albums

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 05:42:03 +02:00

84 lines
2.6 KiB
Python
Executable file

from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional, List, Dict
AUDIO_EXTENSIONS = {
".mp3", ".flac", ".m4a", ".aac", ".ogg", ".opus",
".wav", ".wma", ".aiff", ".ape",
}
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".gif"}
TRACKLIST_EXTENSIONS = {".txt", ".htm", ".html", ".nfo"}
PLAYLIST_EXTENSIONS = {".m3u", ".m3u8", ".pls"}
@dataclass
class ScannedFile:
path: Path
kind: str # "audio" | "image" | "tracklist" | "playlist" | "other"
@dataclass
class AlbumScan:
album_dir: Path
audio_files: List[Path] = field(default_factory=list)
image_files: List[Path] = field(default_factory=list)
tracklist_files: List[Path] = field(default_factory=list)
playlist_files: List[Path] = field(default_factory=list) # .m3u / .m3u8 / .pls
other_files: List[Path] = field(default_factory=list)
@dataclass
class TrackHints:
path: Path
track_number: Optional[int] = None
disc_number: Optional[int] = None
title: Optional[str] = None
artist: Optional[str] = None
duration: Optional[float] = None
existing_tags: Dict[str, str] = field(default_factory=dict)
@dataclass
class AlbumHints:
album_dir: Path
dir_artist: Optional[str] = None
dir_album: Optional[str] = None
dir_year: Optional[str] = None
tracklist_text: Optional[str] = None # merged text from all tracklist files
cover_images: List[Path] = field(default_factory=list)
tracks: List[TrackHints] = field(default_factory=list)
yt_title: Optional[str] = None # YouTube video title (if found)
yt_uploader: Optional[str] = None # YouTube channel/uploader name
@dataclass
class TrackProposal:
path: Path
title: str
artist: str
track_number: Optional[int]
disc_number: Optional[int]
new_filename: Optional[str] = None # only set when --rename is active
mbid: Optional[str] = None
conductor: Optional[str] = None # classical: Dirigent
orchestra: Optional[str] = None # classical: Orchester / Ensemble
@dataclass
class AlbumProposal:
album_dir: Path
album: str
albumartist: str
date: Optional[str]
genre: Optional[str]
label: Optional[str]
mbid: Optional[str] # MusicBrainz release ID
cover_path: Optional[Path] # resolved local or downloaded cover
cover_source: Optional[str] # "local" | "musicbrainz" | "discogs"
tracks: List[TrackProposal]
confidence: float
sources: List[str] = field(default_factory=list)
notes: List[str] = field(default_factory=list)