Skip to content

Commit 196b47b

Browse files
chuckyutanclaude
andcommitted
Tag AIFF (and re-tag converted files) with full metadata
Conversion runs after tagging the source file, but ffmpeg does not reliably carry metadata into the converted container (AIFF was left completely untagged). Add an AIFF tagging path (ID3 via mutagen) and re-tag the converted file for formats tag_file understands. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9d124e4 commit 196b47b

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

streamrip/media/track.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ async def _convert(self):
9393
)
9494
await engine.convert()
9595
self.download_path = engine.final_fn # because the extension changed
96+
# Re-tag the converted file: ffmpeg does not reliably carry over all
97+
# metadata (and some containers, e.g. AIFF, are not tagged at all).
98+
# Only formats that tag_file understands are re-tagged; others (opus,
99+
# ogg, ...) keep the metadata ffmpeg copied during conversion.
100+
ext = self.download_path.split(".")[-1].lower()
101+
if ext in ("flac", "m4a", "mp3", "aiff", "aif"):
102+
await tag_file(self.download_path, self.meta, self.cover_path)
96103

97104
def _set_download_path(self):
98105
c = self.config.session.filepaths

streamrip/metadata/tagger.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import aiofiles
66
from mutagen import id3
7+
from mutagen.aiff import AIFF
78
from mutagen.flac import FLAC, Picture
89
from mutagen.id3 import (
910
APIC, # type: ignore
@@ -100,12 +101,18 @@ class Container(Enum):
100101
FLAC = 1
101102
AAC = 2
102103
MP3 = 3
104+
AIFF = 4
103105

104106
def get_mutagen_class(self, path: str):
105107
if self == Container.FLAC:
106108
return FLAC(path)
107109
elif self == Container.AAC:
108110
return MP4(path)
111+
elif self == Container.AIFF:
112+
audio = AIFF(path)
113+
if audio.tags is None:
114+
audio.add_tags()
115+
return audio.tags
109116
elif self == Container.MP3:
110117
try:
111118
return ID3(path)
@@ -117,7 +124,7 @@ def get_mutagen_class(self, path: str):
117124
def get_tag_pairs(self, meta) -> list[tuple]:
118125
if self == Container.FLAC:
119126
return self._tag_flac(meta)
120-
elif self == Container.MP3:
127+
elif self == Container.MP3 or self == Container.AIFF:
121128
return self._tag_mp3(meta)
122129
elif self == Container.AAC:
123130
return self._tag_mp4(meta)
@@ -217,7 +224,7 @@ async def embed_cover(self, audio, cover_path):
217224
async with aiofiles.open(cover_path, "rb") as img:
218225
cover.data = await img.read()
219226
audio.add_picture(cover)
220-
elif self == Container.MP3:
227+
elif self == Container.MP3 or self == Container.AIFF:
221228
cover = APIC()
222229
cover.type = 3
223230
cover.mime = "image/jpeg"
@@ -236,6 +243,10 @@ def save_audio(self, audio, path):
236243
audio.save()
237244
elif self == Container.MP3:
238245
audio.save(path, "v2_version=3")
246+
elif self == Container.AIFF:
247+
# AIFF uses an _IFFID3 tag object whose save() signature differs
248+
# from MP3's ID3.save(); call it with defaults (v2_version=4).
249+
audio.save(path)
239250

240251

241252
async def tag_file(path: str, meta: TrackMetadata, cover_path: str | None):
@@ -246,6 +257,8 @@ async def tag_file(path: str, meta: TrackMetadata, cover_path: str | None):
246257
container = Container.AAC
247258
elif ext == "mp3":
248259
container = Container.MP3
260+
elif ext in ("aiff", "aif"):
261+
container = Container.AIFF
249262
else:
250263
raise Exception(f"Invalid extension {ext}")
251264

0 commit comments

Comments
 (0)