musikr: use uppercase tag names

This reduces the amount of string processing I need to do in
ktaglib.
This commit is contained in:
Alexander Capehart 2024-12-13 16:20:46 -07:00
parent 65151e006f
commit a2498db6e5
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
2 changed files with 87 additions and 88 deletions

View file

@ -27,31 +27,31 @@ import org.oxycblt.musikr.tag.util.parseXiphPositionField
// Song // Song
fun Metadata.musicBrainzId() = fun Metadata.musicBrainzId() =
(xiph["musicbrainz_releasetrackid"] (xiph["MUSICBRAINZ_RELEASETRACKID"]
?: xiph["musicbrainz release track id"] ?: xiph["MUSICBRAINZ RELEASE TRACK ID"]
?: id3v2["TXXX:musicbrainz release track id"] ?: id3v2["TXXX:MUSICBRAINZ RELEASE TRACK ID"]
?: id3v2["TXXX:musicbrainz_releasetrackid"]) ?: id3v2["TXXX:MUSICBRAINZ_RELEASETRACKID"])
?.first() ?.first()
fun Metadata.name() = (xiph["title"] ?: id3v2["TIT2"])?.first() fun Metadata.name() = (xiph["TITLE"] ?: id3v2["TIT2"])?.first()
fun Metadata.sortName() = (xiph["titlesort"] ?: id3v2["TSOT"])?.first() fun Metadata.sortName() = (xiph["TITLESORT"] ?: id3v2["TSOT"])?.first()
// Track. // Track.
fun Metadata.track() = fun Metadata.track() =
(parseXiphPositionField( (parseXiphPositionField(
xiph["tracknumber"]?.first(), xiph["TRACKNUMBER"]?.first(),
(xiph["totaltracks"] ?: xiph["tracktotal"] ?: xiph["trackc"])?.first()) (xiph["TOTALTRACKS"] ?: xiph["TRACKTOTAL"] ?: xiph["TRACKC"])?.first())
?: id3v2["TRCK"]?.run { first().parseId3v2PositionField() }) ?: id3v2["TRCK"]?.run { first().parseId3v2PositionField() })
// Disc and it's subtitle name. // Disc and it's subtitle name.
fun Metadata.disc() = fun Metadata.disc() =
(parseXiphPositionField( (parseXiphPositionField(
xiph["discnumber"]?.first(), xiph["DISCNUMBER"]?.first(),
(xiph["totaldiscs"] ?: xiph["disctotal"] ?: xiph["discc"])?.run { first() }) (xiph["TOTALDISCS"] ?: xiph["DISCTOTAL"] ?: xiph["DISCC"])?.run { first() })
?: id3v2["TPOS"]?.run { first().parseId3v2PositionField() }) ?: id3v2["TPOS"]?.run { first().parseId3v2PositionField() })
fun Metadata.subtitle() = (xiph["discsubtitle"] ?: id3v2["TSST"])?.first() fun Metadata.subtitle() = (xiph["DISCSUBTITLE"] ?: id3v2["TSST"])?.first()
// Dates are somewhat complicated, as not only did their semantics change from a flat year // Dates are somewhat complicated, as not only did their semantics change from a flat year
// value in ID3v2.3 to a full ISO-8601 date in ID3v2.4, but there are also a variety of // value in ID3v2.3 to a full ISO-8601 date in ID3v2.4, but there are also a variety of
@ -66,9 +66,9 @@ fun Metadata.subtitle() = (xiph["discsubtitle"] ?: id3v2["TSST"])?.first()
// TODO: Handle dates that are in "January" because the actual specific release date // TODO: Handle dates that are in "January" because the actual specific release date
// isn't known? // isn't known?
fun Metadata.date() = fun Metadata.date() =
(xiph["originaldate"]?.run { Date.from(first()) } (xiph["ORIGINALDATE"]?.run { Date.from(first()) }
?: xiph["date"]?.run { Date.from(first()) } ?: xiph["DATE"]?.run { Date.from(first()) }
?: xiph["year"]?.run { Date.from(first()) } ?: xiph["YEAR"]?.run { Date.from(first()) }
?: ?:
// xiph dates are less complicated, but there are still several types // xiph dates are less complicated, but there are still several types
@ -84,95 +84,95 @@ fun Metadata.date() =
// Album // Album
fun Metadata.albumMusicBrainzId() = fun Metadata.albumMusicBrainzId() =
(xiph["musicbrainz_albumid"] (xiph["MUSICBRAINZ_ALBUMID"]
?: xiph["musicbrainz album id"] ?: xiph["MUSICBRAINZ ALBUM ID"]
?: id3v2["TXXX:musicbrainz album id"] ?: id3v2["TXXX:MUSICBRAINZ ALBUM ID"]
?: id3v2["TXXX:musicbrainz_albumid"]) ?: id3v2["TXXX:MUSICBRAINZ_ALBUMID"])
?.first() ?.first()
fun Metadata.albumName() = (xiph["album"] ?: id3v2["TALB"])?.first() fun Metadata.albumName() = (xiph["ALBUM"] ?: id3v2["TALB"])?.first()
fun Metadata.albumSortName() = (xiph["albumsort"] ?: id3v2["TSOA"])?.first() fun Metadata.albumSortName() = (xiph["ALBUMSORT"] ?: id3v2["TSOA"])?.first()
fun Metadata.releaseTypes() = fun Metadata.releaseTypes() =
(xiph["releasetype"] (xiph["RELEASETYPE"]
?: xiph["musicbrainz album type"] ?: xiph["MUSICBRAINZ ALBUM TYPE"]
?: id3v2["TXXX:musicbrainz album type"] ?: id3v2["TXXX:MUSICBRAINZ ALBUM TYPE"]
?: id3v2["TXXX:releasetype"] ?: id3v2["TXXX:RELEASETYPE"]
?: ?:
// This is a non-standard iTunes extension // This is a non-standard iTunes extension
id3v2["GRP1"]) id3v2["GRP1"])
// Artist // Artist
fun Metadata.artistMusicBrainzIds() = fun Metadata.artistMusicBrainzIds() =
(xiph["musicbrainz_artistid"] (xiph["MUSICBRAINZ_ARTISTID"]
?: xiph["musicbrainz artist id"] ?: xiph["MUSICBRAINZ ARTIST ID"]
?: id3v2["TXXX:musicbrainz artist id"] ?: id3v2["TXXX:MUSICBRAINZ ARTIST ID"]
?: id3v2["TXXX:musicbrainz_artistid"]) ?: id3v2["TXXX:MUSICBRAINZ_ARTISTID"])
fun Metadata.artistNames() = fun Metadata.artistNames() =
(xiph["artists"] (xiph["ARTISTS"]
?: xiph["artist"] ?: xiph["ARTIST"]
?: id3v2["TXXX:artists"] ?: id3v2["TXXX:ARTISTS"]
?: id3v2["TPE1"] ?: id3v2["TPE1"]
?: id3v2["TXXX:artist"]) ?: id3v2["TXXX:ARTIST"])
fun Metadata.artistSortNames() = fun Metadata.artistSortNames() =
(xiph["artistssort"] (xiph["ARTISTSSORT"]
?: xiph["artists_sort"] ?: xiph["ARTISTS_SORT"]
?: xiph["artists sort"] ?: xiph["ARTISTS SORT"]
?: xiph["artistsort"] ?: xiph["ARTISTSORT"]
?: xiph["artist sort"] ?: xiph["ARTIST SORT"]
?: id3v2["TXXX:artistssort"] ?: id3v2["TXXX:ARTISTSSORT"]
?: id3v2["TXXX:artists_sort"] ?: id3v2["TXXX:ARTISTS_SORT"]
?: id3v2["TXXX:artists sort"] ?: id3v2["TXXX:ARTISTS SORT"]
?: id3v2["TSOP"] ?: id3v2["TSOP"]
?: id3v2["artistsort"] ?: id3v2["TXXX:ARTISTSORT"]
?: id3v2["TXXX:artist sort"]) ?: id3v2["TXXX:ARTIST SORT"])
fun Metadata.albumArtistMusicBrainzIds() = fun Metadata.albumArtistMusicBrainzIds() =
(xiph["musicbrainz_albumartistid"] (xiph["MUSICBRAINZ_ALBUMARTISTID"]
?: xiph["musicbrainz album artist id"] ?: xiph["MUSICBRAINZ ALBUM ARTIST ID"]
?: id3v2["TXXX:musicbrainz album artist id"] ?: id3v2["TXXX:MUSICBRAINZ ALBUM ARTIST ID"]
?: id3v2["TXXX:musicbrainz_albumartistid"]) ?: id3v2["TXXX:MUSICBRAINZ_ALBUMARTISTID"])
fun Metadata.albumArtistNames() = fun Metadata.albumArtistNames() =
(xiph["albumartists"] (xiph["ALBUMARTISTS"]
?: xiph["album_artists"] ?: xiph["ALBUM_ARTISTS"]
?: xiph["album artists"] ?: xiph["ALBUM ARTISTS"]
?: xiph["albumartist"] ?: xiph["ALBUMARTIST"]
?: xiph["album artist"] ?: xiph["ALBUM ARTIST"]
?: id3v2["TXXX:albumartists"] ?: id3v2["TXXX:ALBUMARTISTS"]
?: id3v2["TXXX:album_artists"] ?: id3v2["TXXX:ALBUM_ARTISTS"]
?: id3v2["TXXX:album artists"] ?: id3v2["TXXX:ALBUM ARTISTS"]
?: id3v2["TPE2"] ?: id3v2["TPE2"]
?: id3v2["TXXX:albumartist"] ?: id3v2["TXXX:ALBUMARTIST"]
?: id3v2["TXXX:album artist"]) ?: id3v2["TXXX:ALBUM ARTIST"])
fun Metadata.albumArtistSortNames() = fun Metadata.albumArtistSortNames() =
(xiph["albumartistssort"] (xiph["ALBUMARTISTSSORT"]
?: xiph["albumartists_sort"] ?: xiph["ALBUMARTISTS_SORT"]
?: xiph["albumartists sort"] ?: xiph["ALBUMARTISTS SORT"]
?: xiph["albumartistsort"] ?: xiph["ALBUMARTISTSORT"]
?: xiph["album artist sort"] ?: xiph["ALBUM ARTIST SORT"]
?: id3v2["TXXX:albumartistssort"] ?: id3v2["TXXX:ALBUMARTISTSSORT"]
?: id3v2["TXXX:albumartists_sort"] ?: id3v2["TXXX:ALBUMARTISTS_SORT"]
?: id3v2["TXXX:albumartists sort"] ?: id3v2["TXXX:ALBUMARTISTS SORT"]
?: id3v2["TXXX:albumartistsort"] ?: id3v2["TXXX:ALBUMARTISTSORT"]
// This is a non-standard iTunes extension // This is a non-standard iTunes extension
?: id3v2["TSO2"] ?: id3v2["TSO2"]
?: id3v2["TXXX:album artist sort"]) ?: id3v2["TXXX:ALBUM ARTIST SORT"])
// Genre // Genre
fun Metadata.genreNames() = xiph["genre"] ?: id3v2["TCON"] fun Metadata.genreNames() = xiph["GENRE"] ?: id3v2["TCON"]
// Compilation Flag // Compilation Flag
fun Metadata.isCompilation() = fun Metadata.isCompilation() =
(xiph["compilation"] (xiph["COMPILATION"]
?: xiph["itunescompilation"] ?: xiph["ITUNESCOMPILATION"]
?: id3v2["TCMP"] // This is a non-standard itunes extension ?: id3v2["TCMP"] // This is a non-standard itunes extension
?: id3v2["TXXX:compilation"] ?: id3v2["TXXX:COMPILATION"]
?: id3v2["TXXX:itunescompilation"]) ?: id3v2["TXXX:ITUNESCOMPILATION"])
?.let { ?.let {
// Ignore invalid instances of this tag // Ignore invalid instances of this tag
it == listOf("1") it == listOf("1")
@ -180,14 +180,14 @@ fun Metadata.isCompilation() =
// ReplayGain information // ReplayGain information
fun Metadata.replayGainTrackAdjustment() = fun Metadata.replayGainTrackAdjustment() =
(xiph["r128_track_gain"]?.parseR128Adjustment() (xiph["R128_TRACK_GAIN"]?.parseR128Adjustment()
?: xiph["replaygain_track_gain"]?.parseReplayGainAdjustment() ?: xiph["REPLAYGAIN_TRACK_GAIN"]?.parseReplayGainAdjustment()
?: id3v2["TXXX:replaygain_track_gain"]?.parseReplayGainAdjustment()) ?: id3v2["TXXX:REPLAYGAIN_TRACK_GAIN"]?.parseReplayGainAdjustment())
fun Metadata.replayGainAlbumAdjustment() = fun Metadata.replayGainAlbumAdjustment() =
(xiph["r128_album_gain"]?.parseR128Adjustment() (xiph["R128_ALBUM_GAIN"]?.parseR128Adjustment()
?: xiph["replaygain_album_gain"]?.parseReplayGainAdjustment() ?: xiph["REPLAYGAIN_ALBUM_GAIN"]?.parseReplayGainAdjustment()
?: id3v2["TXXX:replaygain_album_gain"]?.parseReplayGainAdjustment()) ?: id3v2["TXXX:REPLAYGAIN_ALBUM_GAIN"]?.parseReplayGainAdjustment())
private fun Metadata.parseId3v23Date(): Date? { private fun Metadata.parseId3v23Date(): Date? {
// Assume that TDAT/TIME can refer to TYER or TORY depending on if TORY // Assume that TDAT/TIME can refer to TYER or TORY depending on if TORY

View file

@ -17,12 +17,11 @@ void JVMMetadataBuilder::setMimeType(const std::string_view mimeType) {
void JVMMetadataBuilder::setId3v2(const TagLib::ID3v2::Tag &tag) { void JVMMetadataBuilder::setId3v2(const TagLib::ID3v2::Tag &tag) {
for (auto frame: tag.frameList()) { for (auto frame: tag.frameList()) {
if (auto txxxFrame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame *>(frame)) { if (auto txxxFrame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame *>(frame)) {
TagLib::String desc = std::string(txxxFrame->description().toCString(true));
// Make desc lowercase
std::transform(desc.begin(), desc.end(), desc.begin(), ::tolower);
TagLib::String key = TagLib::String(frame->frameID()) + ":" + desc;
TagLib::StringList frameText = txxxFrame->fieldList(); TagLib::StringList frameText = txxxFrame->fieldList();
frameText.erase(frameText.begin()); // Remove description that exists for some insane reason // Frame text starts with the description then the remaining values
auto begin = frameText.begin();
TagLib::String key = TagLib::String(frame->frameID()) + ":" + begin->upper();
frameText.erase(begin);
id3v2.add(key, frameText); id3v2.add(key, frameText);
} else if (auto textFrame = dynamic_cast<TagLib::ID3v2::TextIdentificationFrame *>(frame)) { } else if (auto textFrame = dynamic_cast<TagLib::ID3v2::TextIdentificationFrame *>(frame)) {
TagLib::String key = frame->frameID(); TagLib::String key = frame->frameID();
@ -36,11 +35,9 @@ void JVMMetadataBuilder::setId3v2(const TagLib::ID3v2::Tag &tag) {
void JVMMetadataBuilder::setXiph(const TagLib::Ogg::XiphComment &tag) { void JVMMetadataBuilder::setXiph(const TagLib::Ogg::XiphComment &tag) {
for (auto field: tag.fieldListMap()) { for (auto field: tag.fieldListMap()) {
auto fieldName = std::string(field.first.toCString(true)); auto key = field.first.upper();
std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), ::tolower); auto values = field.second;
auto taglibFieldName = TagLib::String(fieldName); xiph.add(key, values);
auto fieldValue = field.second;
xiph.add(taglibFieldName, fieldValue);
} }
} }
@ -50,6 +47,8 @@ void JVMMetadataBuilder::setMp4(const TagLib::MP4::Tag &tag) {
auto itemValue = item.second; auto itemValue = item.second;
auto type = itemValue.type(); auto type = itemValue.type();
// TODO: Handle internal atoms
// Only read out the atoms for the reasonable tags we are expecting. // Only read out the atoms for the reasonable tags we are expecting.
// None of the crazy binary atoms. // None of the crazy binary atoms.
if (type == TagLib::MP4::Item::Type::StringList) { if (type == TagLib::MP4::Item::Type::StringList) {
@ -125,7 +124,7 @@ jobject JVMMetadataBuilder::build() {
jobject mp4Map = mp4.getObject(); jobject mp4Map = mp4.getObject();
jbyteArray coverArray = nullptr; jbyteArray coverArray = nullptr;
if (cover.has_value()) { if (cover.has_value()) {
coverArray = env->NewByteArray(cover->size()); coverArray = env->NewByteArray(static_cast<jsize>(cover->size());
env->SetByteArrayRegion(coverArray, 0, cover->size(), reinterpret_cast<const jbyte *>(cover->data())); env->SetByteArrayRegion(coverArray, 0, cover->size(), reinterpret_cast<const jbyte *>(cover->data()));
} }
jobject metadataObj = env->NewObject(metadataClass, metadataInit, id3v2Map, xiphMap, mp4Map, coverArray, propertiesObj); jobject metadataObj = env->NewObject(metadataClass, metadataInit, id3v2Map, xiphMap, mp4Map, coverArray, propertiesObj);