Skip to content

Commit

Permalink
audiotags fix
Browse files Browse the repository at this point in the history
  • Loading branch information
azuline committed Apr 26, 2024
1 parent d0083c1 commit 48a36c5
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 75 deletions.
2 changes: 1 addition & 1 deletion docs/TAGGING_CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ world, Rosé will support reading from additional fields.
| Release Artists | `aART` | |
| Release Type | `----:com.apple.iTunes:RELEASETYPE` | `----:com.apple.iTunes:MusicBrainz Album Type` |
| Release Year | `\xa9day` | |
| Original Year | `----:net.sunsetglow.rose:ORIGINALYEAR` | `----:com.apple.iTunes:ORIGINALYEAR` |
| Original Year | `----:net.sunsetglow.rose:ORIGINALDATE` | `----:com.apple.iTunes:ORIGINALDATE`, `----:com.apple.iTunes:ORIGINALYEAR` |
| Composition Year | `----:net.sunsetglow.rose:COMPOSITIONDATE` | |
| Genre | `\xa9gen` | |
| Secondary Genre | `----:net.sunsetglow.rose:SECONDARYGENRE` | |
Expand Down
86 changes: 66 additions & 20 deletions rose/audiotags.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,28 @@ class UnsupportedTagValueTypeError(RoseExpectedError):
class AudioTags:
id: str | None
release_id: str | None
title: str | None
releaseyear: int | None
compositionyear: int | None

tracktitle: str | None
tracknumber: str | None
tracktotal: int | None
discnumber: str | None
disctotal: int | None
release: str | None
trackartists: ArtistMapping

releasetitle: str | None
releasetype: str
releaseyear: int | None
originalyear: int | None
compositionyear: int | None
genre: list[str]
secondarygenre: list[str]
descriptor: list[str]
edition: str | None
label: list[str]
catalognumber: str | None
releasetype: str

releaseartists: ArtistMapping
trackartists: ArtistMapping

duration_sec: int

path: Path

@classmethod
Expand Down Expand Up @@ -144,17 +148,21 @@ def _get_paired_frame(x: str) -> str | None:
return AudioTags(
id=_get_tag(m.tags, ["TXXX:ROSEID"], first=True),
release_id=_get_tag(m.tags, ["TXXX:ROSERELEASEID"], first=True),
title=_get_tag(m.tags, ["TIT2"]),
tracktitle=_get_tag(m.tags, ["TIT2"]),
releaseyear=_parse_year(_get_tag(m.tags, ["TDRC", "TYER", "TDAT"])),
originalyear=_parse_year(_get_tag(m.tags, ["TDOR", "TORY"])),
compositionyear=_parse_year(_get_tag(m.tags, ["TXXX:COMPOSITIONDATE"], first=True)),
tracknumber=tracknumber,
tracktotal=tracktotal,
discnumber=discnumber,
disctotal=disctotal,
release=_get_tag(m.tags, ["TALB"]),
releasetitle=_get_tag(m.tags, ["TALB"]),
genre=_split_tag(_get_tag(m.tags, ["TCON"], split=True)),
secondarygenre=_split_tag(_get_tag(m.tags, ["TXXX:SECONDARYGENRE"], split=True)),
descriptor=_split_tag(_get_tag(m.tags, ["TXXX:DESCRIPTOR"], split=True)),
label=_split_tag(_get_tag(m.tags, ["TPUB"], split=True)),
catalognumber=_get_tag(m.tags, ["TXXX:CATALOGNUMBER"], first=True),
edition=_get_tag(m.tags, ["TXXX:EDITION"], first=True),
releasetype=_normalize_rtype(
_get_tag(
m.tags, ["TXXX:RELEASETYPE", "TXXX:MusicBrainz Album Type"], first=True
Expand Down Expand Up @@ -184,19 +192,36 @@ def _get_paired_frame(x: str) -> str | None:
return AudioTags(
id=_get_tag(m.tags, ["----:net.sunsetglow.rose:ID"]),
release_id=_get_tag(m.tags, ["----:net.sunsetglow.rose:RELEASEID"]),
title=_get_tag(m.tags, ["\xa9nam"]),
tracktitle=_get_tag(m.tags, ["\xa9nam"]),
releaseyear=_parse_year(_get_tag(m.tags, ["\xa9day"])),
originalyear=_parse_year(
_get_tag(
m.tags,
[
"----:net.sunsetglow.rose:ORIGINALDATE",
"----:com.apple.iTunes:ORIGINALDATE",
"----:com.apple.iTunes:ORIGINALYEAR",
],
)
),
compositionyear=_parse_year(
_get_tag(m.tags, ["----:net.sunsetglow.rose:COMPOSITIONDATE"])
),
tracknumber=str(tracknumber),
tracktotal=tracktotal,
discnumber=str(discnumber),
disctotal=disctotal,
release=_get_tag(m.tags, ["\xa9alb"]),
releasetitle=_get_tag(m.tags, ["\xa9alb"]),
genre=_split_tag(_get_tag(m.tags, ["\xa9gen"], split=True)),
secondarygenre=_split_tag(
_get_tag(m.tags, ["----:net.sunsetglow.rose:SECONDARYGENRE"], split=True)
),
descriptor=_split_tag(
_get_tag(m.tags, ["----:net.sunsetglow.rose:DESCRIPTOR"], split=True)
),
label=_split_tag(_get_tag(m.tags, ["----:com.apple.iTunes:LABEL"], split=True)),
catalognumber=_get_tag(m.tags, ["----:com.apple.iTunes:CATALOGNUMBER"]),
edition=_get_tag(m.tags, ["----:net.sunsetglow.rose:EDITION"]),
releasetype=_normalize_rtype(
_get_tag(
m.tags,
Expand All @@ -223,19 +248,23 @@ def _get_paired_frame(x: str) -> str | None:
return AudioTags(
id=_get_tag(m.tags, ["roseid"]),
release_id=_get_tag(m.tags, ["rosereleaseid"]),
title=_get_tag(m.tags, ["title"]),
tracktitle=_get_tag(m.tags, ["title"]),
releaseyear=_parse_year(_get_tag(m.tags, ["date", "year"])),
originalyear=_parse_year(_get_tag(m.tags, ["originaldate", "originalyear"])),
compositionyear=_parse_year(_get_tag(m.tags, ["compositiondate"])),
tracknumber=_get_tag(m.tags, ["tracknumber"], first=True),
tracktotal=_parse_int(_get_tag(m.tags, ["tracktotal"], first=True)),
discnumber=_get_tag(m.tags, ["discnumber"], first=True),
disctotal=_parse_int(_get_tag(m.tags, ["disctotal"], first=True)),
release=_get_tag(m.tags, ["album"]),
releasetitle=_get_tag(m.tags, ["album"]),
genre=_split_tag(_get_tag(m.tags, ["genre"], split=True)),
secondarygenre=_split_tag(_get_tag(m.tags, ["secondarygenre"], split=True)),
descriptor=_split_tag(_get_tag(m.tags, ["descriptor"], split=True)),
label=_split_tag(
_get_tag(m.tags, ["label", "organization", "recordlabel"], split=True)
),
catalognumber=_get_tag(m.tags, ["catalognumber"]),
edition=_get_tag(m.tags, ["edition"]),
releasetype=_normalize_rtype(_get_tag(m.tags, ["releasetype"], first=True)),
releaseartists=parse_artist_string(
main=_get_tag(m.tags, ["albumartist"], split=True)
Expand Down Expand Up @@ -291,15 +320,19 @@ def _write_tag_with_description(name: str, value: str | None) -> None:

_write_tag_with_description("TXXX:ROSEID", self.id)
_write_tag_with_description("TXXX:ROSERELEASEID", self.release_id)
_write_standard_tag("TIT2", self.title)
_write_standard_tag("TIT2", self.tracktitle)
_write_standard_tag("TDRC", str(self.releaseyear).zfill(4))
_write_standard_tag("TDOR", str(self.originalyear).zfill(4))
_write_tag_with_description("TXXX:COMPOSITIONDATE", self.compositionyear)
_write_standard_tag("TRCK", self.tracknumber)
_write_standard_tag("TPOS", self.discnumber)
_write_standard_tag("TALB", self.release)
_write_standard_tag("TALB", self.releasetitle)
_write_standard_tag("TCON", ";".join(self.genre))
_write_tag_with_description("TXXX:SECONDARYGENRE", ";".join(self.secondarygenre))
_write_tag_with_description("TXXX:DESCRIPTOR", ";".join(self.descriptor))
_write_standard_tag("TPUB", ";".join(self.label))
_write_tag_with_description("TXXX:CATALOGNUMBER", self.catalognumber)
_write_tag_with_description("TXXX:EDITION", self.edition)
_write_tag_with_description("TXXX:RELEASETYPE", self.releasetype)
_write_standard_tag("TPE2", format_artist_string(self.releaseartists))
_write_standard_tag("TPE1", format_artist_string(self.trackartists))
Expand All @@ -318,15 +351,24 @@ def _write_tag_with_description(name: str, value: str | None) -> None:
m.tags = mutagen.mp4.MP4Tags()
m.tags["----:net.sunsetglow.rose:ID"] = (self.id or "").encode()
m.tags["----:net.sunsetglow.rose:RELEASEID"] = (self.release_id or "").encode()
m.tags["\xa9nam"] = self.title or ""
m.tags["\xa9nam"] = self.tracktitle or ""
m.tags["\xa9day"] = str(self.releaseyear).zfill(4)
m.tags["----:net.sunsetglow.rose:ORIGINALDATE"] = (
str(self.originalyear).zfill(4).encode()
)
m.tags["----:net.sunsetglow.rose:COMPOSITIONDATE"] = (
str(self.compositionyear).zfill(4).encode()
)
m.tags["\xa9alb"] = self.release or ""
m.tags["\xa9alb"] = self.releasetitle or ""
m.tags["\xa9gen"] = ";".join(self.genre)
m.tags["----:net.sunsetglow.rose:SECONDARYGENRE"] = ";".join(
self.secondarygenre
).encode()
m.tags["----:net.sunsetglow.rose:DESCRIPTOR"] = ";".join(self.descriptor).encode()
m.tags["\xa9gen"] = ";".join(self.genre)
m.tags["----:com.apple.iTunes:LABEL"] = ";".join(self.label).encode()
m.tags["----:com.apple.iTunes:CATALOGNUMBER"] = (self.catalognumber or "").encode()
m.tags["----:net.sunsetglow.rose:EDITION"] = (self.edition or "").encode()
m.tags["----:com.apple.iTunes:RELEASETYPE"] = self.releasetype.encode()
m.tags["aART"] = format_artist_string(self.releaseartists)
m.tags["\xa9ART"] = format_artist_string(self.trackartists)
Expand Down Expand Up @@ -376,15 +418,19 @@ def _write_tag_with_description(name: str, value: str | None) -> None:
assert not isinstance(m.tags, mutagen.flac.MetadataBlock)
m.tags["roseid"] = self.id or ""
m.tags["rosereleaseid"] = self.release_id or ""
m.tags["title"] = self.title or ""
m.tags["title"] = self.tracktitle or ""
m.tags["date"] = str(self.releaseyear).zfill(4)
m.tags["originaldate"] = str(self.originalyear).zfill(4)
m.tags["compositiondate"] = str(self.compositionyear).zfill(4)
m.tags["tracknumber"] = self.tracknumber or ""
m.tags["discnumber"] = self.discnumber or ""
m.tags["album"] = self.release or ""
m.tags["album"] = self.releasetitle or ""
m.tags["genre"] = ";".join(self.genre)
m.tags["secondarygenre"] = ";".join(self.secondarygenre)
m.tags["descriptor"] = ";".join(self.descriptor)
m.tags["label"] = ";".join(self.label)
m.tags["catalognumber"] = self.catalognumber or ""
m.tags["edition"] = self.edition or ""
m.tags["releasetype"] = self.releasetype
m.tags["albumartist"] = format_artist_string(self.releaseartists)
m.tags["artist"] = format_artist_string(self.trackartists)
Expand Down
16 changes: 12 additions & 4 deletions rose/audiotags_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,25 @@
)
def test_getters(filename: str, track_num: str, duration: int) -> None:
af = AudioTags.from_file(TEST_TAGGER / filename)
assert af.release == "A Cool Album"
assert af.releasetitle == "A Cool Album"
assert af.releasetype == "album"
assert af.releaseyear == 1990
assert af.originalyear == 1990
assert af.compositionyear == 1984
assert af.genre == ["Electronic", "House"]
assert af.secondarygenre == ["Minimal", "Ambient"]
assert af.descriptor == ["Lush", "Warm"]
assert af.label == ["A Cool Label"]
assert af.catalognumber == "DN-420"
assert af.edition == "Japan"
assert af.releaseartists.main == [Artist("Artist A"), Artist("Artist B")]

assert af.tracknumber == track_num
assert af.tracktotal == 5
assert af.discnumber == "1"
assert af.disctotal == 1

assert af.title == f"Track {track_num}"
assert af.tracktitle == f"Track {track_num}"
assert af.trackartists == ArtistMapping(
main=[Artist("Artist A"), Artist("Artist B")],
guest=[Artist("Artist C"), Artist("Artist D")],
Expand Down Expand Up @@ -74,19 +78,23 @@ def test_flush(isolated_dir: Path, filename: str, track_num: str, duration: int)
af.flush()
af = AudioTags.from_file(fpath)

assert af.release == "A Cool Album"
assert af.releasetitle == "A Cool Album"
assert af.releasetype == "album"
assert af.releaseyear == 1990
assert af.originalyear == 1990
assert af.compositionyear == 1984
assert af.genre == ["Electronic", "House"]
assert af.secondarygenre == ["Minimal", "Ambient"]
assert af.descriptor == ["Lush", "Warm"]
assert af.label == ["A Cool Label"]
assert af.catalognumber == "DN-420"
assert af.edition == "Japan"
assert af.releaseartists.main == [Artist("Artist A"), Artist("Artist B")]

assert af.tracknumber == track_num
assert af.discnumber == "1"

assert af.title == f"Track {track_num}"
assert af.tracktitle == f"Track {track_num}"
assert af.trackartists == ArtistMapping(
main=[Artist("Artist A"), Artist("Artist B")],
guest=[Artist("Artist C"), Artist("Artist D")],
Expand Down
4 changes: 2 additions & 2 deletions rose/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ def _update_cache_for_releases_executor(
# formatted artist string.
if not pulled_release_tags:
pulled_release_tags = True
release_title = tags.release or "Unknown Release"
release_title = tags.releasetitle or "Unknown Release"
if release_title != release.releasetitle:
logger.debug(f"Release title change detected for {source_path}, updating")
release.releasetitle = release_title
Expand Down Expand Up @@ -862,7 +862,7 @@ def _update_cache_for_releases_executor(
id=track_id,
source_path=Path(f),
source_mtime=track_mtime,
tracktitle=tags.title or "Unknown Title",
tracktitle=tags.tracktitle or "Unknown Title",
# Remove `.` here because we use `.` to parse out discno/trackno in the virtual
# filesystem. It should almost never happen, but better to be safe. We set the
# totals on all tracks the end of the loop.
Expand Down
12 changes: 6 additions & 6 deletions rose/releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,8 @@ def edit_release(
tags.discnumber = track_meta.discnumber
dirty = True
logger.debug(f"Modified tag detected for {t.source_path}: discnumber")
if tags.title != track_meta.title:
tags.title = track_meta.title
if tags.tracktitle != track_meta.title:
tags.tracktitle = track_meta.title
dirty = True
logger.debug(f"Modified tag detected for {t.source_path}: title")
tart = MetadataArtist.to_mapping(track_meta.artists)
Expand All @@ -378,8 +378,8 @@ def edit_release(
logger.debug(f"Modified tag detected for {t.source_path}: artists")

# Album tags.
if tags.release != release_meta.title:
tags.release = release_meta.title
if tags.releasetitle != release_meta.title:
tags.releasetitle = release_meta.title
dirty = True
logger.debug(f"Modified tag detected for {t.source_path}: release")
if tags.releasetype != release_meta.releasetype:
Expand Down Expand Up @@ -468,7 +468,7 @@ def create_single_release(c: Config, track_path: Path) -> None:

# Step 1. Compute the new directory name for the single.
af = AudioTags.from_file(track_path)
title = (af.title or "Unknown Title").strip()
title = (af.tracktitle or "Unknown Title").strip()

dirname = f"{artistsfmt(af.trackartists)} - "
if af.releaseyear:
Expand All @@ -494,7 +494,7 @@ def create_single_release(c: Config, track_path: Path) -> None:
break
# Step 3. Update the tags of the new track. Clear the Rose IDs too: this is a brand new track.
af = AudioTags.from_file(new_track_path)
af.release = title
af.releasetitle = title
af.releasetype = "single"
af.releaseartists = af.trackartists
af.tracknumber = "1"
Expand Down
6 changes: 3 additions & 3 deletions rose/releases_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ def test_extract_single_release(config: Config) -> None:
assert (source_path / "01. Track 2.m4a").is_file()
assert (source_path / "cover.jpg").is_file()
af = AudioTags.from_file(source_path / "01. Track 2.m4a")
assert af.release == "Track 2"
assert af.releasetitle == "Track 2"
assert af.tracknumber == "1"
assert af.discnumber == "1"
assert af.releasetype == "single"
Expand All @@ -404,7 +404,7 @@ def test_extract_single_release_with_trailing_space(config: Config) -> None:
release_dir = config.music_source_dir / TEST_RELEASE_1.name
shutil.copytree(TEST_RELEASE_1, release_dir)
af = AudioTags.from_file(release_dir / "02.m4a")
af.title = "Trailing Space "
af.tracktitle = "Trailing Space "
af.flush()
update_cache(config)
create_single_release(config, release_dir / "02.m4a")
Expand Down Expand Up @@ -724,4 +724,4 @@ def test_run_action_on_release(config: Config, source_dir: Path) -> None:
action = MetadataAction.parse("tracktitle/replace:Bop")
run_actions_on_release(config, "ilovecarly", [action])
af = AudioTags.from_file(source_dir / "Test Release 2" / "01.m4a")
assert af.title == "Bop"
assert af.tracktitle == "Bop"
Loading

0 comments on commit 48a36c5

Please sign in to comment.