from __future__ import annotations from pathlib import Path from openmusic_analysis.domain import LyricsSourceError from openmusic_analysis.lyrics_resolution import ( LyricsLookupInput, LyricsResolver, _ProviderResult, _metadata_from_filename, ) from openmusic_analysis.settings import LyricsFallbackConfig class PipelineResolver(LyricsResolver): def __init__(self) -> None: super().__init__( LyricsFallbackConfig( lrclib_enabled=True, musixmatch_api_key="test-key", acoustid_api_key="test-key", asr_provider="command", local_asr_command="fake {input}", ) ) def _embedded(self, source_path): return ( None, LyricsLookupInput(duration_seconds=180), LyricsSourceError( source="embedded", code="LYRICS_NOT_FOUND", message="No embedded lyrics.", ), ) def _lrclib(self, metadata, *, source): if metadata.title == "Identified title": return _ProviderResult("Resolved lyrics", None) return _ProviderResult( None, LyricsSourceError( source=source, code="METADATA_REQUIRED", message="Metadata missing.", ), ) def _musixmatch(self, metadata, *, source): return _ProviderResult( None, LyricsSourceError( source=source, code="LYRICS_NOT_FOUND", message="No Musixmatch result.", ), ) def _fingerprint(self, source_path, errors): return LyricsLookupInput(title="Identified title", artist="Identified artist") def _transcribe(self, source_path): raise AssertionError("ASR must not run after provider resolution") def test_provided_lyrics_short_circuit_every_fallback(tmp_path: Path): source = tmp_path / "track.mp3" source.write_bytes(b"audio") resolver = PipelineResolver() result = resolver._resolve_sync( source, "Provided lyrics", LyricsLookupInput(title="Title", artist="Artist"), ) assert result.text == "Provided lyrics" assert result.source == "request" assert result.errors == [] def test_fingerprint_enriches_metadata_and_provider_errors_are_preserved(tmp_path: Path): source = tmp_path / "track.mp3" source.write_bytes(b"audio") result = PipelineResolver()._resolve_sync(source, None, LyricsLookupInput()) assert result.text == "Resolved lyrics" assert result.source == "lrclib" assert result.metadata.title == "Identified title" assert result.metadata.artist == "Identified artist" assert [error.source for error in result.errors] == [ "embedded", "lrclib", "musixmatch", ] def test_filename_metadata_removes_music_video_suffix(): metadata = _metadata_from_filename(Path("AQUAKEY - ALONE (MUSIC VIDEO).mp3")) assert metadata.artist == "AQUAKEY" assert metadata.title == "ALONE"