From f17a88be9a833a578856eb249162256184b8ed9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 29 Aug 2026 07:48:07 +0100 Subject: [PATCH] slim-embedded-art: drop junk lyrics frames over 50 KB The 49 files the art pass left behind were fat with USLT, not APIC - the worst held the complete text of Angela's Ashes (1.5 MB of Frank McCourt) in an Eels track, some tagger malfunction. AVPlayer pages through the whole tag before the first audio frame regardless of what fills it, so absurd lyrics cost the same silence as absurd art. No song has 50 KB of lyrics; anything past it is junk and is removed. --- scripts/slim-embedded-art.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/scripts/slim-embedded-art.py b/scripts/slim-embedded-art.py index 1f43ea7..bafc363 100755 --- a/scripts/slim-embedded-art.py +++ b/scripts/slim-embedded-art.py @@ -39,6 +39,10 @@ from PIL import Image ROOT = os.path.expanduser("~/Music") EMBED_TARGET = 80_000 PROCESS_OVER = 100_000 +# No song has 50 KB of lyrics. The frame that set this limit held the complete text of Frank +# McCourt's "Angela's Ashes" - 1.5 MB of memoir in an Eels track's USLT, courtesy of some tagger +# malfunction - and AVPlayer pages through ALL of it before the first audio frame, art or not. +LYRICS_SANE = 50_000 def audio_region_hash(path, offset=None): @@ -120,22 +124,33 @@ def process(path, apply): return None pics = pictures(tag) total = sum(len(p.data) for p in pics) - if total <= PROCESS_OVER: + junk_lyrics = [f for f in tag.getall("USLT") if len(f.text or "") > LYRICS_SANE] + if total <= PROCESS_OVER and not junk_lyrics: return None - largest = max(pics, key=lambda p: len(p.data)) + largest = max(pics, key=lambda p: len(p.data)) if pics else None if not apply: - return f"would slim {total // 1024} KB of art" + parts = [] + if total > PROCESS_OVER: parts.append(f"{total // 1024} KB of art") + if junk_lyrics: parts.append(f"{sum(len(f.text) for f in junk_lyrics) // 1024} KB of junk lyrics") + return "would slim " + " + ".join(parts) before = audio_region_hash(path) tmp = path + ".slimtmp" shutil.copy2(path, tmp) try: - rescued = rescue_cover(os.path.dirname(path), largest.data) - jpeg, note = compress_cover(largest.data) + note = "art untouched" t = ID3(tmp) - t.delall("APIC") - t.delall("PIC") - t.add(APIC(encoding=0, mime="image/jpeg", type=3, desc="", data=jpeg)) + if largest is not None and total > PROCESS_OVER: + rescued = rescue_cover(os.path.dirname(path), largest.data) + jpeg, note = compress_cover(largest.data) + t.delall("APIC") + t.delall("PIC") + t.add(APIC(encoding=0, mime="image/jpeg", type=3, desc="", data=jpeg)) + else: + rescued = False + jpeg = b"" + for f in junk_lyrics: + t.delall(f.HashKey) v2 = 4 if tag.version >= (2, 4, 0) else 3 # v1=0 REMOVES any trailing ID3v1 tag: it is a 128-byte legacy appendix fully redundant # with the v2 frames, and keeping it in sync is what tripped the audio guard before the @@ -145,6 +160,8 @@ def process(path, apply): raise RuntimeError("audio bytes changed - keeping original") os.replace(tmp, path) note_out = f"{total // 1024} KB -> {len(jpeg) // 1024} KB ({note})" + if junk_lyrics: + note_out += f" -{sum(len(f.text) for f in junk_lyrics) // 1024} KB junk lyrics" return note_out + (" +rescued cover.jpg" if rescued else "") except Exception as e: if os.path.exists(tmp):