"""Tests for re-embedding lyrics from an existing sidecar file. Discord report (Netti93): retag was clearing the LYRICS / USLT tag without rewriting it. Cause was two-fold: 1. `core/library/retag.py:execute_retag` never called `generate_lrc_file` after `enhance_file_metadata`. The download pipeline does — retag was inconsistent. 2. Even with the call added, `lyrics_client.create_lrc_file` used to short-circuit when an .lrc / .txt sidecar already existed (the typical retag case — sidecar moved alongside the audio file). Pre-fix: returned True without re-embedding USLT. Post-fix: reads the existing sidecar and re-embeds the USLT tag. """ from __future__ import annotations import os import tempfile from unittest.mock import patch, MagicMock import pytest @pytest.fixture def fake_audio_file(tmp_path): """Build a minimal FLAC file with no LYRICS tag.""" fd, path = tempfile.mkstemp(suffix='.flac', dir=str(tmp_path)) os.close(fd) minimal = ( b'fLaC' + b'\x80\x00\x00\x22' + b'\x00\x10\x00\x10' + b'\x00\x00\x00\x00\x00\x00' + b'\x0a\xc4\x42\xf0\x00\x00\x00\x00' + b'\x00' * 16 ) with open(path, 'wb') as f: f.write(minimal) yield path # ────────────────────────────────────────────────────────────────────── # create_lrc_file — re-embed when sidecar present # ────────────────────────────────────────────────────────────────────── def test_existing_lrc_sidecar_triggers_reembed(fake_audio_file): """The exact retag scenario — sidecar already exists alongside the audio file (moved during retag), USLT got cleared by enrichment. Helper should read the sidecar and re-embed without hitting LRClib.""" from core.lyrics_client import LyricsClient sidecar_path = os.path.splitext(fake_audio_file)[0] + '.lrc' with open(sidecar_path, 'w', encoding='utf-8') as f: f.write('[00:01.00]Test lyric line\n[00:05.00]Second line') client = LyricsClient() client.api = MagicMock() # API stub — should NOT be called client._embed_lyrics = MagicMock() result = client.create_lrc_file( audio_file_path=fake_audio_file, track_name='Test', artist_name='Artist', ) assert result is True # API never hit — sidecar shortcut client.api.get_lyrics.assert_not_called() client.api.search_lyrics.assert_not_called() # USLT was re-embedded client._embed_lyrics.assert_called_once() call_args = client._embed_lyrics.call_args assert call_args.args[0] == fake_audio_file assert 'Test lyric line' in call_args.args[1] def test_existing_txt_sidecar_also_triggers_reembed(fake_audio_file): """Same shape with .txt sidecar (plain lyrics, no timestamps).""" from core.lyrics_client import LyricsClient sidecar_path = os.path.splitext(fake_audio_file)[0] + '.txt' with open(sidecar_path, 'w', encoding='utf-8') as f: f.write('Just plain lyrics no timestamps') client = LyricsClient() client.api = MagicMock() client._embed_lyrics = MagicMock() result = client.create_lrc_file( audio_file_path=fake_audio_file, track_name='T', artist_name='A', ) assert result is True client._embed_lyrics.assert_called_once_with( fake_audio_file, 'Just plain lyrics no timestamps' ) def test_empty_sidecar_does_not_embed(fake_audio_file): """Defensive — if the sidecar exists but is empty, don't write an empty USLT tag.""" from core.lyrics_client import LyricsClient sidecar_path = os.path.splitext(fake_audio_file)[0] + '.lrc' with open(sidecar_path, 'w', encoding='utf-8') as f: f.write(' \n ') # whitespace only client = LyricsClient() client.api = MagicMock() client._embed_lyrics = MagicMock() result = client.create_lrc_file( audio_file_path=fake_audio_file, track_name='T', artist_name='A', ) assert result is True client._embed_lyrics.assert_not_called() def test_unreadable_sidecar_swallows_error_returns_true(fake_audio_file): """If the sidecar is somehow unreadable, return True (don't try LRClib again — the early-return contract holds), just skip the embed silently.""" from core.lyrics_client import LyricsClient sidecar_path = os.path.splitext(fake_audio_file)[0] + '.lrc' with open(sidecar_path, 'wb') as f: f.write(b'\xff\xfe\x00\x00') # invalid UTF-8 client = LyricsClient() client.api = MagicMock() client._embed_lyrics = MagicMock() result = client.create_lrc_file( audio_file_path=fake_audio_file, track_name='T', artist_name='A', ) assert result is True client.api.get_lyrics.assert_not_called() def test_no_sidecar_falls_through_to_lrclib(fake_audio_file): """No sidecar → original LRClib fetch path runs (download flow).""" from core.lyrics_client import LyricsClient client = LyricsClient() fake_lyrics = MagicMock() fake_lyrics.synced_lyrics = '[00:01.00]synced from api' fake_lyrics.plain_lyrics = None client.api = MagicMock() client.api.get_lyrics.return_value = None client.api.search_lyrics.return_value = [fake_lyrics] client._embed_lyrics = MagicMock() result = client.create_lrc_file( audio_file_path=fake_audio_file, track_name='T', artist_name='A', ) assert result is True client.api.search_lyrics.assert_called_once() # Sidecar was created lrc = os.path.splitext(fake_audio_file)[0] + '.lrc' assert os.path.exists(lrc) # And USLT was embedded client._embed_lyrics.assert_called_once()