Skip to content

Commit 9423c2f

Browse files
author
david.algis
committed
🐛 Fix mutagen tag changes
1 parent dc91a52 commit 9423c2f

1 file changed

Lines changed: 47 additions & 34 deletions

File tree

auto_tag/audio_recognize.py

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# auto_tag/audio_recognize.py
22
"""
33
Recognise audio files with Shazam, optionally rename or copy them,
4-
and update metadata (tags, cover art) using `eyed3` and `mutagen`.
4+
and update metadata (tags, cover art).
55
66
OGG files are converted to WAV via soundfile/libsndfile (no ffmpeg),
7-
but if conversion or recognition on WAV fails, we fall back to using
8-
the original OGG for recognition—so tests with DummyShazam still work.
7+
but if conversion or recognition on WAV fails, we fall back to the
8+
original OGG for recognition—so tests with DummyShazam still work.
99
"""
1010

1111
from __future__ import annotations
@@ -41,20 +41,27 @@ async def find_and_recognize_audio_files(
4141
plex_structure: bool = False,
4242
copy_to: str | None = None,
4343
) -> None:
44+
"""
45+
Walk folder_path, recognise each file, then move or copy/tag it.
46+
copy_to, if given, is the base dir to copy files into (instead of moving).
47+
"""
4448
exts = {e.lower().lstrip(".") for e in extensions}
4549
audio_files: list[str] = []
50+
4651
for root, _, files in os.walk(folder_path):
4752
if "test" in os.path.basename(root).lower():
4853
continue
4954
for fn in files:
5055
if os.path.splitext(fn)[1].lower().lstrip(".") in exts:
5156
audio_files.append(os.path.join(root, fn))
57+
5258
if not audio_files:
5359
print(f"No files with extensions {exts} found in {folder_path}.")
5460
return
5561

5662
shazam = Shazam()
5763
ok = 0
64+
5865
for path in tqdm(audio_files, desc="Recognising and renaming"):
5966
res = await recognize_and_rename_file(
6067
file_path=path,
@@ -87,10 +94,16 @@ async def recognize_and_rename_file(
8794
plex_structure: bool,
8895
copy_to: str | None = None,
8996
) -> dict:
97+
"""
98+
Recognise file_path with Shazam, then move or copy & tag it.
99+
- If copy_to is set, the file is **copied** to that directory (with or
100+
without Plex subfolders) and the original remains untouched.
101+
- Otherwise it is **moved** (renamed) in place or under the output_dir.
102+
"""
90103
ext = os.path.splitext(file_path)[1].lower()
91104
tmp_wav: str | None = None
92105

93-
# Prepare for recognition: try WAV conversion for OGG
106+
# 1) For OGG, try to convert to WAV first
94107
input_path = file_path
95108
if ext == ".ogg":
96109
fd, tmp_wav = tempfile.mkstemp(suffix=".wav")
@@ -102,16 +115,15 @@ async def recognize_and_rename_file(
102115
except Exception as exc:
103116
if trace:
104117
print(f"[{os.path.basename(file_path)}] OGG→WAV failed: {exc}")
105-
input_path = file_path # fallback to original
118+
input_path = file_path # fallback
106119

107-
# Attempt recognition (first on input_path, then if OGG and failed, on
108-
# original)
120+
# 2) Recognise with retries
109121
out = None
110122
for attempt in range(1, nbr_retry + 1):
111123
try:
112-
res = await shazam.recognize(input_path)
113-
if res:
114-
out = res
124+
candidate = await shazam.recognize(input_path)
125+
if candidate:
126+
out = candidate
115127
break
116128
except Exception as exc:
117129
if trace:
@@ -121,24 +133,24 @@ async def recognize_and_rename_file(
121133
if attempt < nbr_retry:
122134
await asyncio.sleep(delay)
123135

124-
# If OGG and first recognition on WAV failed, try on original OGG
136+
# Fallback for OGG if WAV recognition failed
125137
if ext == ".ogg" and out is None and input_path != file_path:
126138
for attempt in range(1, nbr_retry + 1):
127139
try:
128-
res = await shazam.recognize(file_path)
129-
if res:
130-
out = res
140+
candidate = await shazam.recognize(file_path)
141+
if candidate:
142+
out = candidate
131143
break
132144
except Exception as exc:
133145
if trace:
134146
print(
135-
f"[{os.path.basename(file_path)}] OGG original attempt"
136-
f"{attempt}: {exc}"
147+
f"[{os.path.basename(file_path)}] OGG fallback"
148+
f" attempt {attempt}: {exc}"
137149
)
138150
if attempt < nbr_retry:
139151
await asyncio.sleep(delay)
140152

141-
# Clean up temp WAV
153+
# cleanup
142154
if tmp_wav and os.path.exists(tmp_wav):
143155
os.remove(tmp_wav)
144156

@@ -147,7 +159,7 @@ async def recognize_and_rename_file(
147159
print(f"Shazam failed: {file_path}")
148160
return {"file_path": file_path, "error": "Recognition failed"}
149161

150-
# Extract metadata
162+
# 3) Extract metadata
151163
track = out["track"]
152164
title = track.get("title", "Unknown Title")
153165
artist = track.get("subtitle", "Unknown Artist")
@@ -158,27 +170,27 @@ async def recognize_and_rename_file(
158170
s_artist = sanitize(artist, trace)
159171
s_album = sanitize(album, trace)
160172

161-
# Build new filename
173+
# 4) Build filename
162174
if plex_structure:
163175
new_name = f"{s_title}{ext}"
164176
else:
165177
new_name = f"{s_title} - {s_artist} - {s_album}{ext}"
166178

167-
# Determine target directory
168-
base_dir = copy_to or output_dir or os.path.dirname(file_path)
179+
# 5) Determine the root folder for the new file
180+
root_dir = copy_to or output_dir or os.path.dirname(file_path)
169181
if plex_structure:
170-
base_dir = os.path.join(base_dir, s_artist, s_album)
171-
os.makedirs(base_dir, exist_ok=True)
182+
root_dir = os.path.join(root_dir, s_artist, s_album)
183+
os.makedirs(root_dir, exist_ok=True)
172184

173-
# Ensure unique filename
174-
new_path = os.path.join(base_dir, new_name)
185+
# 6) Ensure uniqueness
186+
new_path = os.path.join(root_dir, new_name)
175187
count = 1
176188
while os.path.exists(new_path) and new_path != file_path:
177-
stem, ext2 = os.path.splitext(new_path)
178-
new_path = f"{stem} ({count}){ext2}"
189+
stem, e2 = os.path.splitext(new_path)
190+
new_path = f"{stem} ({count}){e2}"
179191
count += 1
180192

181-
# Move or copy & tag
193+
# 7) Move or copy & tag
182194
if modify:
183195
try:
184196
if copy_to:
@@ -243,6 +255,7 @@ def update_ogg_tags(
243255
cover_url: str,
244256
trace: bool,
245257
) -> None:
258+
# Try Vorbis, then Opus, then generic
246259
try:
247260
audio = OggVorbis(file_path)
248261
except Exception:
@@ -253,9 +266,10 @@ def update_ogg_tags(
253266
if audio is None:
254267
raise RuntimeError("Unsupported OGG type for tagging")
255268

256-
audio["TITLE"] = title
257-
audio["ARTIST"] = artist
258-
audio["ALBUM"] = album
269+
# Mutagen expects tag values as lists
270+
audio["TITLE"] = [title]
271+
audio["ARTIST"] = [artist]
272+
audio["ALBUM"] = [album]
259273

260274
if cover_url:
261275
try:
@@ -265,9 +279,8 @@ def update_ogg_tags(
265279
pic.type = 3
266280
pic.mime = "image/jpeg"
267281
pic.width = pic.height = pic.depth = pic.colors = 0
268-
audio["METADATA_BLOCK_PICTURE"] = [
269-
base64.b64encode(pic.write()).decode()
270-
]
282+
b64 = base64.b64encode(pic.write()).decode("ascii")
283+
audio["METADATA_BLOCK_PICTURE"] = [b64]
271284
except Exception as exc:
272285
if trace:
273286
print("Cover art error:", exc)

0 commit comments

Comments
 (0)