diff --git a/taglib/ape/apetag.cpp b/taglib/ape/apetag.cpp index 79e1d5cc..a2bdaeed 100644 --- a/taglib/ape/apetag.cpp +++ b/taglib/ape/apetag.cpp @@ -215,7 +215,9 @@ namespace {"DATE", "YEAR" }, {"ALBUMARTIST", "ALBUM ARTIST"}, {"DISCNUMBER", "DISC" }, - {"REMIXER", "MIXARTIST" }}; + {"REMIXER", "MIXARTIST" }, + {"RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS" }, + {"RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE" }}; const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]); } diff --git a/taglib/asf/asftag.cpp b/taglib/asf/asftag.cpp index 20a946f0..935db046 100644 --- a/taglib/asf/asftag.cpp +++ b/taglib/asf/asftag.cpp @@ -216,7 +216,7 @@ namespace { "WM/AlbumTitle", "ALBUM" }, { "WM/AlbumArtist", "ALBUMARTIST" }, { "WM/Composer", "COMPOSER" }, - { "WM/Writer", "WRITER" }, + { "WM/Writer", "LYRICIST" }, { "WM/Conductor", "CONDUCTOR" }, { "WM/ModifiedBy", "REMIXER" }, { "WM/Year", "DATE" }, @@ -243,11 +243,17 @@ namespace { "WM/TitleSortOrder", "TITLESORT" }, { "WM/Script", "SCRIPT" }, { "WM/Language", "LANGUAGE" }, + { "WM/ARTISTS", "ARTISTS" }, + { "ASIN", "ASIN" }, { "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" }, { "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" }, { "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" }, { "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, + { "MusicBrainz/Album Release Country", "RELEASECOUNTRY" }, + { "MusicBrainz/Album Status", "RELEASESTATUS" }, + { "MusicBrainz/Album Type", "RELEASETYPE" }, { "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID" }, { "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" }, { "MusicIP/PUID", "MUSICIP_PUID" }, { "Acoustid/Id", "ACOUSTID_ID" }, diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp index ba8f4f59..0f392ec7 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -366,6 +366,7 @@ StringList FileRef::defaultFileExtensions() l.append("ogg"); l.append("flac"); l.append("oga"); + l.append("opus"); l.append("mp3"); l.append("mpc"); l.append("wv"); @@ -382,6 +383,8 @@ StringList FileRef::defaultFileExtensions() l.append("asf"); l.append("aif"); l.append("aiff"); + l.append("afc"); + l.append("aifc"); l.append("wav"); l.append("ape"); l.append("mod"); diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index 87ac1852..a99b7101 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -895,6 +895,17 @@ namespace { "soco", "COMPOSERSORT" }, { "sosn", "SHOWSORT" }, { "shwm", "SHOWWORKMOVEMENT" }, + { "pgap", "GAPLESSPLAYBACK" }, + { "pcst", "PODCAST" }, + { "catg", "PODCASTCATEGORY" }, + { "desc", "PODCASTDESC" }, + { "egid", "PODCASTID" }, + { "purl", "PODCASTURL" }, + { "tves", "TVEPISODE" }, + { "tven", "TVEPISODEID" }, + { "tvnn", "TVNETWORK" }, + { "tvsn", "TVSEASON" }, + { "tvsh", "TVSHOW" }, { "\251wrk", "WORK" }, { "\251mvn", "MOVEMENTNAME" }, { "\251mvi", "MOVEMENTNUMBER" }, @@ -904,7 +915,13 @@ namespace { "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" }, { "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, { "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "----:com.apple.iTunes:MusicBrainz Release Track Id", "MUSICBRAINZ_RELEASETRACKID" }, { "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" }, + { "----:com.apple.iTunes:MusicBrainz Album Release Country", "RELEASECOUNTRY" }, + { "----:com.apple.iTunes:MusicBrainz Album Status", "RELEASESTATUS" }, + { "----:com.apple.iTunes:MusicBrainz Album Type", "RELEASETYPE" }, + { "----:com.apple.iTunes:ARTISTS", "ARTISTS" }, + { "----:com.apple.iTunes:originaldate", "ORIGINALDATE" }, { "----:com.apple.iTunes:ASIN", "ASIN" }, { "----:com.apple.iTunes:LABEL", "LABEL" }, { "----:com.apple.iTunes:LYRICIST", "LYRICIST" }, @@ -952,10 +969,12 @@ PropertyMap MP4::Tag::properties() const } props[key] = value; } - else if(key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT") { + else if(key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT" || + key == "TVEPISODE" || key == "TVSEASON") { props[key] = String::number(it->second.toInt()); } - else if(key == "COMPILATION" || key == "SHOWWORKMOVEMENT") { + else if(key == "COMPILATION" || key == "SHOWWORKMOVEMENT" || + key == "GAPLESSPLAYBACK" || key == "PODCAST") { props[key] = String::number(it->second.toBool()); } else { @@ -1007,11 +1026,15 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props) d->items[name] = MP4::Item(first, second); } } - else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" || it->first == "MOVEMENTCOUNT") && !it->second.isEmpty()) { + else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" || + it->first == "MOVEMENTCOUNT" || it->first == "TVEPISODE" || + it->first == "TVSEASON") && !it->second.isEmpty()) { int value = it->second.front().toInt(); d->items[name] = MP4::Item(value); } - else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT") && !it->second.isEmpty()) { + else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT" || + it->first == "GAPLESSPLAYBACK" || it->first == "PODCAST") && + !it->second.isEmpty()) { bool value = (it->second.front().toInt() != 0); d->items[name] = MP4::Item(value); } diff --git a/taglib/mpeg/id3v2/frames/podcastframe.cpp b/taglib/mpeg/id3v2/frames/podcastframe.cpp index 7285b968..7dfe471d 100644 --- a/taglib/mpeg/id3v2/frames/podcastframe.cpp +++ b/taglib/mpeg/id3v2/frames/podcastframe.cpp @@ -24,6 +24,7 @@ ***************************************************************************/ #include "podcastframe.h" +#include using namespace TagLib; using namespace ID3v2; @@ -55,6 +56,13 @@ String PodcastFrame::toString() const return String(); } +PropertyMap PodcastFrame::asProperties() const +{ + PropertyMap map; + map.insert("PODCAST", StringList()); + return map; +} + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/mpeg/id3v2/frames/podcastframe.h b/taglib/mpeg/id3v2/frames/podcastframe.h index 7bbc2138..a71278c3 100644 --- a/taglib/mpeg/id3v2/frames/podcastframe.h +++ b/taglib/mpeg/id3v2/frames/podcastframe.h @@ -57,6 +57,8 @@ namespace TagLib { */ virtual String toString() const; + PropertyMap asProperties() const; + protected: // Reimplementations. diff --git a/taglib/mpeg/id3v2/frames/privateframe.cpp b/taglib/mpeg/id3v2/frames/privateframe.cpp index be80c4df..4f55a3ca 100644 --- a/taglib/mpeg/id3v2/frames/privateframe.cpp +++ b/taglib/mpeg/id3v2/frames/privateframe.cpp @@ -55,7 +55,7 @@ PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data), d(new PrivateFramePrivate()) { - setData(data); + Frame::setData(data); } PrivateFrame::~PrivateFrame() diff --git a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp index 937cb4d2..36016c71 100644 --- a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp @@ -168,7 +168,8 @@ void TableOfContentsFrame::removeChildElement(const ByteVector &cE) if(it == d->childElements.end()) it = d->childElements.find(cE + ByteVector("\0")); - d->childElements.erase(it); + if(it != d->childElements.end()) + d->childElements.erase(it); } const FrameListMap &TableOfContentsFrame::embeddedFrameListMap() const @@ -196,11 +197,14 @@ void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del) { // remove the frame from the frame list FrameList::Iterator it = d->embeddedFrameList.find(frame); - d->embeddedFrameList.erase(it); + if(it != d->embeddedFrameList.end()) + d->embeddedFrameList.erase(it); // ...and from the frame list map - it = d->embeddedFrameListMap[frame->frameID()].find(frame); - d->embeddedFrameListMap[frame->frameID()].erase(it); + FrameList &mappedList = d->embeddedFrameListMap[frame->frameID()]; + it = mappedList.find(frame); + if(it != mappedList.end()) + mappedList.erase(it); // ...and delete as desired if(del) diff --git a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index 9b1eacd1..39019e61 100644 --- a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -63,7 +63,10 @@ TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const Property TextIdentificationFrame *frame = new TextIdentificationFrame("TIPL"); StringList l; for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it){ - l.append(it->first); + const String role = involvedPeopleMap()[it->first]; + if(role.isEmpty()) // should not happen + continue; + l.append(role); l.append(it->second.toString(",")); // comma-separated list of names } frame->setText(l); diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index f2414b16..81f351ef 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -40,6 +40,7 @@ #include "frames/commentsframe.h" #include "frames/uniquefileidentifierframe.h" #include "frames/unknownframe.h" +#include "frames/podcastframe.h" using namespace TagLib; using namespace ID3v2; @@ -120,6 +121,8 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) // UrlLinkFrame* frame = new UrlLinkFrame(frameID); frame->setUrl(values.front()); return frame; + } else if(frameID == "PCST") { + return new PodcastFrame(); } } if(key == "MUSICBRAINZ_TRACKID" && values.size() == 1) { @@ -344,7 +347,7 @@ namespace { "TEXT", "LYRICIST" }, { "TFLT", "FILETYPE" }, //{ "TIPL", "INVOLVEDPEOPLE" }, handled separately - { "TIT1", "CONTENTGROUP" }, + { "TIT1", "CONTENTGROUP" }, // 'Work' in iTunes { "TIT2", "TITLE"}, { "TIT3", "SUBTITLE" }, { "TKEY", "INITIALKEY" }, @@ -369,6 +372,7 @@ namespace { "TRSN", "RADIOSTATION" }, { "TRSO", "RADIOSTATIONOWNER" }, { "TSOA", "ALBUMSORT" }, + { "TSOC", "COMPOSERSORT" }, { "TSOP", "ARTISTSORT" }, { "TSOT", "TITLESORT" }, { "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes @@ -402,7 +406,11 @@ namespace { "MUSICBRAINZ ALBUM ID", "MUSICBRAINZ_ALBUMID" }, { "MUSICBRAINZ ARTIST ID", "MUSICBRAINZ_ARTISTID" }, { "MUSICBRAINZ ALBUM ARTIST ID", "MUSICBRAINZ_ALBUMARTISTID" }, + { "MUSICBRAINZ ALBUM RELEASE COUNTRY", "RELEASECOUNTRY" }, + { "MUSICBRAINZ ALBUM STATUS", "RELEASESTATUS" }, + { "MUSICBRAINZ ALBUM TYPE", "RELEASETYPE" }, { "MUSICBRAINZ RELEASE GROUP ID", "MUSICBRAINZ_RELEASEGROUPID" }, + { "MUSICBRAINZ RELEASE TRACK ID", "MUSICBRAINZ_RELEASETRACKID" }, { "MUSICBRAINZ WORK ID", "MUSICBRAINZ_WORKID" }, { "ACOUSTID ID", "ACOUSTID_ID" }, { "ACOUSTID FINGERPRINT", "ACOUSTID_FINGERPRINT" }, @@ -490,6 +498,8 @@ PropertyMap Frame::asProperties() const return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties(); else if(id == "UFID") return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties(); + else if(id == "PCST") + return dynamic_cast< const PodcastFrame* >(this)->asProperties(); PropertyMap m; m.unsupportedData().append(id); return m; diff --git a/taglib/toolkit/tpropertymap.h b/taglib/toolkit/tpropertymap.h index 5933b89b..d491efe8 100644 --- a/taglib/toolkit/tpropertymap.h +++ b/taglib/toolkit/tpropertymap.h @@ -68,6 +68,7 @@ namespace TagLib { * - ALBUMSORT * - ARTISTSORT * - ALBUMARTISTSORT + * - COMPOSERSORT * * Credits: * @@ -90,12 +91,16 @@ namespace TagLib { * - LABEL * - CATALOGNUMBER * - BARCODE + * - RELEASECOUNTRY + * - RELEASESTATUS + * - RELEASETYPE * * MusicBrainz identifiers: * * - MUSICBRAINZ_TRACKID * - MUSICBRAINZ_ALBUMID * - MUSICBRAINZ_RELEASEGROUPID + * - MUSICBRAINZ_RELEASETRACKID * - MUSICBRAINZ_WORKID * - MUSICBRAINZ_ARTISTID * - MUSICBRAINZ_ALBUMARTISTID diff --git a/taglib/wavpack/wavpackfile.cpp b/taglib/wavpack/wavpackfile.cpp index 01bdba36..56b99393 100644 --- a/taglib/wavpack/wavpackfile.cpp +++ b/taglib/wavpack/wavpackfile.cpp @@ -263,7 +263,7 @@ void WavPack::File::read(bool readProperties) d->APELocation = d->APELocation + APE::Footer::size() - d->APESize; } - if(d->ID3v1Location >= 0) + if(d->ID3v1Location < 0) APETag(true); // Look for WavPack audio properties diff --git a/tests/test_ape.cpp b/tests/test_ape.cpp index 8c120483..81b8510f 100644 --- a/tests/test_ape.cpp +++ b/tests/test_ape.cpp @@ -155,6 +155,58 @@ public: } } + void testProperties() + { + PropertyMap tags; + tags["ALBUM"] = StringList("Album"); + tags["ALBUMARTIST"] = StringList("Album Artist"); + tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); + tags["ALBUMSORT"] = StringList("Album Sort"); + tags["ARTIST"] = StringList("Artist"); + tags["ARTISTS"] = StringList("Artists"); + tags["ARTISTSORT"] = StringList("Artist Sort"); + tags["ASIN"] = StringList("ASIN"); + tags["BARCODE"] = StringList("Barcode"); + tags["CATALOGNUMBER"] = StringList("Catalog Number 1").append("Catalog Number 2"); + tags["COMMENT"] = StringList("Comment"); + tags["DATE"] = StringList("2021-01-10"); + tags["DISCNUMBER"] = StringList("3/5"); + tags["GENRE"] = StringList("Genre"); + tags["ISRC"] = StringList("UKAAA0500001"); + tags["LABEL"] = StringList("Label 1").append("Label 2"); + tags["MEDIA"] = StringList("Media"); + tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); + tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); + tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); + tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); + tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); + tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); + tags["ORIGINALDATE"] = StringList("2021-01-09"); + tags["RELEASECOUNTRY"] = StringList("Release Country"); + tags["RELEASESTATUS"] = StringList("Release Status"); + tags["RELEASETYPE"] = StringList("Release Type"); + tags["SCRIPT"] = StringList("Script"); + tags["TITLE"] = StringList("Title"); + tags["TRACKNUMBER"] = StringList("2/3"); + + ScopedFileCopy copy("mac-399", ".ape"); + { + APE::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + CPPUNIT_ASSERT(properties.isEmpty()); + f.setProperties(tags); + f.save(); + } + { + const APE::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + if (tags != properties) { + CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); + } + CPPUNIT_ASSERT(tags == properties); + } + } + void testRepeatedSave() { ScopedFileCopy copy("mac-399", ".ape"); diff --git a/tests/test_apetag.cpp b/tests/test_apetag.cpp index 577ec4b0..2225ccbd 100644 --- a/tests/test_apetag.cpp +++ b/tests/test_apetag.cpp @@ -79,6 +79,10 @@ public: CPPUNIT_ASSERT_EQUAL(2u, tag.itemListMap()["ARTIST"].values().size()); CPPUNIT_ASSERT_EQUAL(String("artist 1 artist 2"), tag.artist()); CPPUNIT_ASSERT_EQUAL(17u, tag.track()); + const APE::Item &textItem = tag.itemListMap()["TRACK"]; + CPPUNIT_ASSERT_EQUAL(APE::Item::Text, textItem.type()); + CPPUNIT_ASSERT(!textItem.isEmpty()); + CPPUNIT_ASSERT_EQUAL(9 + 5 + 2, textItem.size()); } void testPropertyInterface2() @@ -89,6 +93,8 @@ public: APE::Item item2 = APE::Item(); item2.setType(APE::Item::Binary); + ByteVector binaryData1("first"); + item2.setBinaryData(binaryData1); tag.setItem("TESTBINARY", item2); PropertyMap properties = tag.properties(); @@ -96,6 +102,16 @@ public: CPPUNIT_ASSERT(properties.contains("TRACKNUMBER")); CPPUNIT_ASSERT(!properties.contains("TRACK")); CPPUNIT_ASSERT(tag.itemListMap().contains("TESTBINARY")); + CPPUNIT_ASSERT_EQUAL(binaryData1, + tag.itemListMap()["TESTBINARY"].binaryData()); + ByteVector binaryData2("second"); + tag.setData("TESTBINARY", binaryData2); + const APE::Item &binaryItem = tag.itemListMap()["TESTBINARY"]; + CPPUNIT_ASSERT_EQUAL(APE::Item::Binary, binaryItem.type()); + CPPUNIT_ASSERT(!binaryItem.isEmpty()); + CPPUNIT_ASSERT_EQUAL(9 + 10 + static_cast(binaryData2.size()), + binaryItem.size()); + CPPUNIT_ASSERT_EQUAL(binaryData2, binaryItem.binaryData()); tag.removeUnsupportedProperties(properties.unsupportedData()); CPPUNIT_ASSERT(!tag.itemListMap().contains("TESTBINARY")); diff --git a/tests/test_asf.cpp b/tests/test_asf.cpp index e9b8dab7..2abe9fe5 100644 --- a/tests/test_asf.cpp +++ b/tests/test_asf.cpp @@ -50,6 +50,7 @@ class TestASF : public CppUnit::TestFixture CPPUNIT_TEST(testSavePicture); CPPUNIT_TEST(testSaveMultiplePictures); CPPUNIT_TEST(testProperties); + CPPUNIT_TEST(testPropertiesAllSupported); CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST_SUITE_END(); @@ -302,6 +303,84 @@ public: CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["DISCNUMBER"]); } + void testPropertiesAllSupported() + { + PropertyMap tags; + tags["ACOUSTID_ID"] = StringList("Acoustid ID"); + tags["ACOUSTID_FINGERPRINT"] = StringList("Acoustid Fingerprint"); + tags["ALBUM"] = StringList("Album"); + tags["ALBUMARTIST"] = StringList("Album Artist"); + tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); + tags["ALBUMSORT"] = StringList("Album Sort"); + tags["ARTIST"] = StringList("Artist"); + tags["ARTISTS"] = StringList("Artists"); + tags["ARTISTSORT"] = StringList("Artist Sort"); + tags["ASIN"] = StringList("ASIN"); + tags["BARCODE"] = StringList("Barcode"); + tags["BPM"] = StringList("123"); + tags["CATALOGNUMBER"] = StringList("Catalog Number"); + tags["COMMENT"] = StringList("Comment"); + tags["COMPOSER"] = StringList("Composer"); + tags["CONDUCTOR"] = StringList("Conductor"); + tags["COPYRIGHT"] = StringList("2021 Copyright"); + tags["DATE"] = StringList("2021-01-03 12:29:23"); + tags["DISCNUMBER"] = StringList("3/5"); + tags["DISCSUBTITLE"] = StringList("Disc Subtitle"); + tags["ENCODEDBY"] = StringList("Encoded by"); + tags["GENRE"] = StringList("Genre"); + tags["GROUPING"] = StringList("Grouping"); + tags["ISRC"] = StringList("UKAAA0500001"); + tags["LABEL"] = StringList("Label"); + tags["LANGUAGE"] = StringList("eng"); + tags["LYRICIST"] = StringList("Lyricist"); + tags["LYRICS"] = StringList("Lyrics"); + tags["MEDIA"] = StringList("Media"); + tags["MOOD"] = StringList("Mood"); + tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); + tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); + tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); + tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); + tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); + tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); + tags["MUSICBRAINZ_WORKID"] = StringList("MusicBrainz_WorkID"); + tags["MUSICIP_PUID"] = StringList("MusicIP PUID"); + tags["ORIGINALDATE"] = StringList("2021-01-03 13:52:19"); + tags["PRODUCER"] = StringList("Producer"); + tags["RELEASECOUNTRY"] = StringList("Release Country"); + tags["RELEASESTATUS"] = StringList("Release Status"); + tags["RELEASETYPE"] = StringList("Release Type"); + tags["REMIXER"] = StringList("Remixer"); + tags["SCRIPT"] = StringList("Script"); + tags["SUBTITLE"] = StringList("Subtitle"); + tags["TITLE"] = StringList("Title"); + tags["TITLESORT"] = StringList("Title Sort"); + tags["TRACKNUMBER"] = StringList("2/4"); + + ScopedFileCopy copy("silence-1", ".wma"); + { + ASF::File f(copy.fileName().c_str()); + ASF::Tag *asfTag = f.tag(); + asfTag->setTitle(""); + asfTag->attributeListMap().clear(); + f.save(); + } + { + ASF::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + CPPUNIT_ASSERT(properties.isEmpty()); + f.setProperties(tags); + f.save(); + } + { + const ASF::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + if (tags != properties) { + CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); + } + CPPUNIT_ASSERT(tags == properties); + } + } + void testRepeatedSave() { ScopedFileCopy copy("silence-1", ".wma"); diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp index 77f5f887..1fc5def9 100644 --- a/tests/test_fileref.cpp +++ b/tests/test_fileref.cpp @@ -39,6 +39,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -79,8 +82,12 @@ class TestFileRef : public CppUnit::TestFixture CPPUNIT_TEST(testWav); CPPUNIT_TEST(testAIFF_1); CPPUNIT_TEST(testAIFF_2); + CPPUNIT_TEST(testWavPack); + CPPUNIT_TEST(testOpus); CPPUNIT_TEST(testUnsupported); CPPUNIT_TEST(testCreate); + CPPUNIT_TEST(testAudioProperties); + CPPUNIT_TEST(testDefaultFileExtensions); CPPUNIT_TEST(testFileResolver); CPPUNIT_TEST_SUITE_END(); @@ -100,6 +107,7 @@ public: f.tag()->setTitle("test title"); f.tag()->setGenre("Test!"); f.tag()->setAlbum("albummmm"); + f.tag()->setComment("a comment"); f.tag()->setTrack(5); f.tag()->setYear(2020); f.save(); @@ -111,12 +119,14 @@ public: CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); + CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("a comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020); f.tag()->setArtist("ttest artist"); f.tag()->setTitle("ytest title"); f.tag()->setGenre("uTest!"); f.tag()->setAlbum("ialbummmm"); + f.tag()->setComment("another comment"); f.tag()->setTrack(7); f.tag()->setYear(2080); f.save(); @@ -128,6 +138,7 @@ public: CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); + CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("another comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080); } @@ -141,12 +152,14 @@ public: CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); + CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("another comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080); f.tag()->setArtist("test artist"); f.tag()->setTitle("test title"); f.tag()->setGenre("Test!"); f.tag()->setAlbum("albummmm"); + f.tag()->setComment("a comment"); f.tag()->setTrack(5); f.tag()->setYear(2020); f.save(); @@ -162,6 +175,7 @@ public: CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); + CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("a comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020); @@ -178,12 +192,14 @@ public: CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); + CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("a comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020); f.tag()->setArtist("ttest artist"); f.tag()->setTitle("ytest title"); f.tag()->setGenre("uTest!"); f.tag()->setAlbum("ialbummmm"); + f.tag()->setComment("another comment"); f.tag()->setTrack(7); f.tag()->setYear(2080); f.save(); @@ -199,6 +215,7 @@ public: CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); + CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("another comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080); } @@ -289,6 +306,16 @@ public: fileRefSave("alaw", ".aifc"); } + void testWavPack() + { + fileRefSave("click", ".wv"); + } + + void testOpus() + { + fileRefSave("correctness_gain_silent_output", ".opus"); + } + void testUnsupported() { FileRef f1(TEST_FILE_PATH_C("no-extension")); @@ -309,6 +336,41 @@ public: f = FileRef::create(TEST_FILE_PATH_C("xing.mp3")); CPPUNIT_ASSERT(dynamic_cast(f)); delete f; + + f = FileRef::create(TEST_FILE_PATH_C("test.xm")); + CPPUNIT_ASSERT(dynamic_cast(f)); + delete f; + } + + void testAudioProperties() + { + FileRef f(TEST_FILE_PATH_C("xing.mp3")); + const AudioProperties *audioProperties = f.audioProperties(); + CPPUNIT_ASSERT_EQUAL(2, audioProperties->lengthInSeconds()); + CPPUNIT_ASSERT_EQUAL(2064, audioProperties->lengthInMilliseconds()); + } + + void testDefaultFileExtensions() + { + const StringList extensions = FileRef::defaultFileExtensions(); + CPPUNIT_ASSERT(extensions.contains("mpc")); + CPPUNIT_ASSERT(extensions.contains("wma")); + CPPUNIT_ASSERT(extensions.contains("ogg")); + CPPUNIT_ASSERT(extensions.contains("spx")); + CPPUNIT_ASSERT(extensions.contains("flac")); + CPPUNIT_ASSERT(extensions.contains("mp3")); + CPPUNIT_ASSERT(extensions.contains("tta")); + CPPUNIT_ASSERT(extensions.contains("m4a")); + CPPUNIT_ASSERT(extensions.contains("3g2")); + CPPUNIT_ASSERT(extensions.contains("m4v")); + CPPUNIT_ASSERT(extensions.contains("wav")); + CPPUNIT_ASSERT(extensions.contains("oga")); + CPPUNIT_ASSERT(extensions.contains("ape")); + CPPUNIT_ASSERT(extensions.contains("aiff")); + CPPUNIT_ASSERT(extensions.contains("aifc")); + CPPUNIT_ASSERT(extensions.contains("wv")); + CPPUNIT_ASSERT(extensions.contains("opus")); + CPPUNIT_ASSERT(extensions.contains("xm")); } void testFileResolver() diff --git a/tests/test_flac.cpp b/tests/test_flac.cpp index 7c25a83b..c83f1e90 100644 --- a/tests/test_flac.cpp +++ b/tests/test_flac.cpp @@ -54,6 +54,7 @@ class TestFLAC : public CppUnit::TestFixture CPPUNIT_TEST(testRepeatedSave3); CPPUNIT_TEST(testSaveMultipleValues); CPPUNIT_TEST(testDict); + CPPUNIT_TEST(testProperties); CPPUNIT_TEST(testInvalid); CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testZeroSizedPadding1); @@ -313,6 +314,60 @@ public: } } + void testProperties() + { + PropertyMap tags; + tags["ALBUM"] = StringList("Album"); + tags["ALBUMARTIST"] = StringList("Album Artist"); + tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); + tags["ALBUMSORT"] = StringList("Album Sort"); + tags["ARTIST"] = StringList("Artist"); + tags["ARTISTS"] = StringList("Artists"); + tags["ARTISTSORT"] = StringList("Artist Sort"); + tags["ASIN"] = StringList("ASIN"); + tags["BARCODE"] = StringList("Barcode"); + tags["CATALOGNUMBER"] = StringList("Catalog Number 1").append("Catalog Number 2"); + tags["COMMENT"] = StringList("Comment"); + tags["DATE"] = StringList("2021-01-10"); + tags["DISCNUMBER"] = StringList("3"); + tags["DISCTOTAL"] = StringList("5"); + tags["GENRE"] = StringList("Genre"); + tags["ISRC"] = StringList("UKAAA0500001"); + tags["LABEL"] = StringList("Label 1").append("Label 2"); + tags["MEDIA"] = StringList("Media"); + tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); + tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); + tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); + tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); + tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); + tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); + tags["ORIGINALDATE"] = StringList("2021-01-09"); + tags["RELEASECOUNTRY"] = StringList("Release Country"); + tags["RELEASESTATUS"] = StringList("Release Status"); + tags["RELEASETYPE"] = StringList("Release Type"); + tags["SCRIPT"] = StringList("Script"); + tags["TITLE"] = StringList("Title"); + tags["TRACKNUMBER"] = StringList("2"); + tags["TRACKTOTAL"] = StringList("4"); + + ScopedFileCopy copy("no-tags", ".flac"); + { + FLAC::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + CPPUNIT_ASSERT(properties.isEmpty()); + f.setProperties(tags); + f.save(); + } + { + const FLAC::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + if (tags != properties) { + CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); + } + CPPUNIT_ASSERT(tags == properties); + } + } + void testInvalid() { ScopedFileCopy copy("silence-44-s", ".flac"); diff --git a/tests/test_id3v1.cpp b/tests/test_id3v1.cpp index c50c3428..d3f037aa 100644 --- a/tests/test_id3v1.cpp +++ b/tests/test_id3v1.cpp @@ -67,6 +67,8 @@ public: { CPPUNIT_ASSERT_EQUAL(String("Darkwave"), ID3v1::genre(50)); CPPUNIT_ASSERT_EQUAL(100, ID3v1::genreIndex("Humour")); + CPPUNIT_ASSERT(ID3v1::genreList().contains("Heavy Metal")); + CPPUNIT_ASSERT_EQUAL(79, ID3v1::genreMap()["Hard Rock"]); } void testRenamedGenres() diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index 243e96ca..f03c82cc 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -42,6 +42,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -76,16 +79,20 @@ class TestID3v2 : public CppUnit::TestFixture CPPUNIT_TEST(testParseAPIC); CPPUNIT_TEST(testParseAPIC_UTF16_BOM); CPPUNIT_TEST(testParseAPICv22); + CPPUNIT_TEST(testRenderAPIC); CPPUNIT_TEST(testDontRender22); CPPUNIT_TEST(testParseGEOB); + CPPUNIT_TEST(testRenderGEOB); CPPUNIT_TEST(testPOPMtoString); CPPUNIT_TEST(testParsePOPM); CPPUNIT_TEST(testParsePOPMWithoutCounter); CPPUNIT_TEST(testRenderPOPM); CPPUNIT_TEST(testPOPMFromFile); CPPUNIT_TEST(testParseRelativeVolumeFrame); + CPPUNIT_TEST(testRenderRelativeVolumeFrame); CPPUNIT_TEST(testParseUniqueFileIdentifierFrame); CPPUNIT_TEST(testParseEmptyUniqueFileIdentifierFrame); + CPPUNIT_TEST(testRenderUniqueFileIdentifierFrame); CPPUNIT_TEST(testBrokenFrame1); CPPUNIT_TEST(testItunes24FrameSize); CPPUNIT_TEST(testParseUrlLinkFrame); @@ -99,6 +106,12 @@ class TestID3v2 : public CppUnit::TestFixture CPPUNIT_TEST(testRenderSynchronizedLyricsFrame); CPPUNIT_TEST(testParseEventTimingCodesFrame); CPPUNIT_TEST(testRenderEventTimingCodesFrame); + CPPUNIT_TEST(testParseCommentsFrame); + CPPUNIT_TEST(testRenderCommentsFrame); + CPPUNIT_TEST(testParsePodcastFrame); + CPPUNIT_TEST(testRenderPodcastFrame); + CPPUNIT_TEST(testParsePrivateFrame); + CPPUNIT_TEST(testRenderPrivateFrame); CPPUNIT_TEST(testSaveUTF16Comment); CPPUNIT_TEST(testUpdateGenre23_1); CPPUNIT_TEST(testUpdateGenre23_2); @@ -266,6 +279,26 @@ public: delete frame; } + void testRenderAPIC() + { + ID3v2::AttachedPictureFrame f; + f.setTextEncoding(String::UTF8); + f.setMimeType("image/png"); + f.setType(ID3v2::AttachedPictureFrame::BackCover); + f.setDescription("Description"); + f.setPicture("PNG data"); + CPPUNIT_ASSERT_EQUAL( + ByteVector("APIC" + "\x00\x00\x00\x20" + "\x00\x00" + "\x03" + "image/png\x00" + "\x04" + "Description\x00" + "PNG data", 42), + f.render()); + } + void testDontRender22() { ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); @@ -304,6 +337,26 @@ public: CPPUNIT_ASSERT_EQUAL(String("d"), f.description()); } + void testRenderGEOB() + { + ID3v2::GeneralEncapsulatedObjectFrame f; + f.setTextEncoding(String::Latin1); + f.setMimeType("application/octet-stream"); + f.setFileName("test.bin"); + f.setDescription("Description"); + f.setObject(ByteVector(3, '\x01')); + CPPUNIT_ASSERT_EQUAL( + ByteVector("GEOB" + "\x00\x00\x00\x32" + "\x00\x00" + "\x00" + "application/octet-stream\x00" + "test.bin\x00" + "Description\x00" + "\x01\x01\x01", 60), + f.render()); + } + void testParsePOPM() { ID3v2::PopularimeterFrame f(ByteVector("POPM" @@ -392,10 +445,36 @@ public: CPPUNIT_ASSERT_EQUAL(String("ident"), f.identification()); CPPUNIT_ASSERT_EQUAL(15.0f / 512.0f, f.volumeAdjustment(ID3v2::RelativeVolumeFrame::FrontRight)); + CPPUNIT_ASSERT_EQUAL(static_cast(15), + f.volumeAdjustmentIndex(ID3v2::RelativeVolumeFrame::FrontRight)); CPPUNIT_ASSERT_EQUAL((unsigned char)8, f.peakVolume(ID3v2::RelativeVolumeFrame::FrontRight).bitsRepresentingPeak); CPPUNIT_ASSERT_EQUAL(ByteVector("\x45"), f.peakVolume(ID3v2::RelativeVolumeFrame::FrontRight).peakVolume); + const List channels = f.channels(); + CPPUNIT_ASSERT_EQUAL(1U, channels.size()); + CPPUNIT_ASSERT_EQUAL(ID3v2::RelativeVolumeFrame::FrontRight, channels[0]); + } + + void testRenderRelativeVolumeFrame() + { + ID3v2::RelativeVolumeFrame f; + f.setIdentification("ident"); + f.setVolumeAdjustment(15.0f / 512.0f, ID3v2::RelativeVolumeFrame::FrontRight); + ID3v2::RelativeVolumeFrame::PeakVolume peakVolume; + peakVolume.bitsRepresentingPeak = 8; + peakVolume.peakVolume.setData("\x45"); + f.setPeakVolume(peakVolume, ID3v2::RelativeVolumeFrame::FrontRight); + CPPUNIT_ASSERT_EQUAL( + ByteVector("RVA2" + "\x00\x00\x00\x0B" + "\x00\x00" + "ident\x00" + "\x02" + "\x00\x0F" + "\x08" + "\x45", 21), + f.render()); } void testParseUniqueFileIdentifierFrame() @@ -426,6 +505,18 @@ public: f.identifier()); } + void testRenderUniqueFileIdentifierFrame() + { + ID3v2::UniqueFileIdentifierFrame f("owner", "\x01\x02\x03"); + CPPUNIT_ASSERT_EQUAL( + ByteVector("UFID" + "\x00\x00\x00\x09" + "\x00\x00" + "owner\x00" + "\x01\x02\x03", 19), + f.render()); + } + void testParseUrlLinkFrame() { ID3v2::UrlLinkFrame f( @@ -635,6 +726,89 @@ public: f.render()); } + void testParseCommentsFrame() + { + ID3v2::CommentsFrame f( + ByteVector("COMM" + "\x00\x00\x00\x14" + "\x00\x00" + "\x03" + "deu" + "Description\x00" + "Text", 30)); + CPPUNIT_ASSERT_EQUAL(String::UTF8, f.textEncoding()); + CPPUNIT_ASSERT_EQUAL(ByteVector("deu"), f.language()); + CPPUNIT_ASSERT_EQUAL(String("Description"), f.description()); + CPPUNIT_ASSERT_EQUAL(String("Text"), f.text()); + } + + void testRenderCommentsFrame() + { + ID3v2::CommentsFrame f; + f.setTextEncoding(String::UTF16); + f.setLanguage("eng"); + f.setDescription("Description"); + f.setText("Text"); + CPPUNIT_ASSERT_EQUAL( + ByteVector("COMM" + "\x00\x00\x00\x28" + "\x00\x00" + "\x01" + "eng" + "\xff\xfe" "D\0e\0s\0c\0r\0i\0p\0t\0i\0o\0n\0" "\x00\x00" + "\xff\xfe" "T\0e\0x\0t\0", 50), + f.render()); + } + + void testParsePodcastFrame() + { + ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); + ByteVector data = ByteVector("PCST" + "\x00\x00\x00\x04" + "\x00\x00" + "\x00\x00\x00\x00", 14); + const ID3v2::Header header; + CPPUNIT_ASSERT(dynamic_cast( + factory->createFrame(data, &header))); + } + + void testRenderPodcastFrame() + { + ID3v2::PodcastFrame f; + CPPUNIT_ASSERT_EQUAL( + ByteVector("PCST" + "\x00\x00\x00\x04" + "\x00\x00" + "\x00\x00\x00\x00", 14), + f.render()); + } + + void testParsePrivateFrame() + { + ID3v2::PrivateFrame f( + ByteVector("PRIV" + "\x00\x00\x00\x0e" + "\x00\x00" + "WM/Provider\x00" + "TL", 24)); + CPPUNIT_ASSERT_EQUAL(String("WM/Provider"), f.owner()); + CPPUNIT_ASSERT_EQUAL(ByteVector("TL"), f.data()); + } + + void testRenderPrivateFrame() + { + ID3v2::PrivateFrame f; + f.setOwner("WM/Provider"); + f.setData("TL"); + CPPUNIT_ASSERT_EQUAL( + ByteVector("PRIV" + "\x00\x00\x00\x0e" + "\x00\x00" + "WM/Provider\x00" + "TL", 24), + f.render()); + } + void testItunes24FrameSize() { MPEG::File f(TEST_FILE_PATH_C("005411.id3"), false); @@ -1246,6 +1420,16 @@ public: CPPUNIT_ASSERT((unsigned int)0x01 == f.embeddedFrameList().size()); CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").size() == 1); CPPUNIT_ASSERT(f.embeddedFrameList("TIT2")[0]->toString() == "TC1"); + + f.removeChildElement("E"); // not existing + CPPUNIT_ASSERT_EQUAL(2U, f.entryCount()); + f.removeChildElement("C"); + CPPUNIT_ASSERT_EQUAL(1U, f.entryCount()); + CPPUNIT_ASSERT_EQUAL(ByteVector("D"), f.childElements()[0]); + + ID3v2::Frame *frame = f.embeddedFrameList("TIT2")[0]; + f.removeEmbeddedFrame(frame); + CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").isEmpty()); } void testRenderTableOfContentsFrame() diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp index ba38c0f0..5f96c9c0 100644 --- a/tests/test_mp4.cpp +++ b/tests/test_mp4.cpp @@ -59,6 +59,7 @@ class TestMP4 : public CppUnit::TestFixture CPPUNIT_TEST(testCovrWrite); CPPUNIT_TEST(testCovrRead2); CPPUNIT_TEST(testProperties); + CPPUNIT_TEST(testPropertiesAllSupported); CPPUNIT_TEST(testPropertiesMovement); CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST(testRepeatedSave); @@ -426,6 +427,97 @@ public: f.setProperties(tags); } + void testPropertiesAllSupported() + { + PropertyMap tags; + tags["ALBUM"] = StringList("Album"); + tags["ALBUMARTIST"] = StringList("Album Artist"); + tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); + tags["ALBUMSORT"] = StringList("Album Sort"); + tags["ARTIST"] = StringList("Artist"); + tags["ARTISTS"] = StringList("Artists"); + tags["ARTISTSORT"] = StringList("Artist Sort"); + tags["ASIN"] = StringList("ASIN"); + tags["BARCODE"] = StringList("Barcode"); + tags["BPM"] = StringList("123"); + tags["CATALOGNUMBER"] = StringList("Catalog Number"); + tags["COMMENT"] = StringList("Comment"); + tags["COMPILATION"] = StringList("1"); + tags["COMPOSER"] = StringList("Composer"); + tags["COMPOSERSORT"] = StringList("Composer Sort"); + tags["CONDUCTOR"] = StringList("Conductor"); + tags["COPYRIGHT"] = StringList("2021 Copyright"); + tags["DATE"] = StringList("2021-01-03 12:29:23"); + tags["DISCNUMBER"] = StringList("3/5"); + tags["DISCSUBTITLE"] = StringList("Disc Subtitle"); + tags["DJMIXER"] = StringList("DJ Mixer"); + tags["ENCODEDBY"] = StringList("Encoded by"); + tags["ENGINEER"] = StringList("Engineer"); + tags["GAPLESSPLAYBACK"] = StringList("1"); + tags["GENRE"] = StringList("Genre"); + tags["GROUPING"] = StringList("Grouping"); + tags["ISRC"] = StringList("UKAAA0500001"); + tags["LABEL"] = StringList("Label"); + tags["LANGUAGE"] = StringList("eng"); + tags["LICENSE"] = StringList("License"); + tags["LYRICIST"] = StringList("Lyricist"); + tags["LYRICS"] = StringList("Lyrics"); + tags["MEDIA"] = StringList("Media"); + tags["MIXER"] = StringList("Mixer"); + tags["MOOD"] = StringList("Mood"); + tags["MOVEMENTCOUNT"] = StringList("3"); + tags["MOVEMENTNAME"] = StringList("Movement Name"); + tags["MOVEMENTNUMBER"] = StringList("2"); + tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); + tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); + tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); + tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); + tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); + tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); + tags["MUSICBRAINZ_WORKID"] = StringList("MusicBrainz_WorkID"); + tags["ORIGINALDATE"] = StringList("2021-01-03 13:52:19"); + tags["PODCAST"] = StringList("1"); + tags["PODCASTCATEGORY"] = StringList("Podcast Category"); + tags["PODCASTDESC"] = StringList("Podcast Description"); + tags["PODCASTID"] = StringList("Podcast ID"); + tags["PODCASTURL"] = StringList("Podcast URL"); + tags["PRODUCER"] = StringList("Producer"); + tags["RELEASECOUNTRY"] = StringList("Release Country"); + tags["RELEASESTATUS"] = StringList("Release Status"); + tags["RELEASETYPE"] = StringList("Release Type"); + tags["REMIXER"] = StringList("Remixer"); + tags["SCRIPT"] = StringList("Script"); + tags["SHOWSORT"] = StringList("Show Sort"); + tags["SHOWWORKMOVEMENT"] = StringList("1"); + tags["SUBTITLE"] = StringList("Subtitle"); + tags["TITLE"] = StringList("Title"); + tags["TITLESORT"] = StringList("Title Sort"); + tags["TRACKNUMBER"] = StringList("2/4"); + tags["TVEPISODE"] = StringList("3"); + tags["TVEPISODEID"] = StringList("TV Episode ID"); + tags["TVNETWORK"] = StringList("TV Network"); + tags["TVSEASON"] = StringList("2"); + tags["TVSHOW"] = StringList("TV Show"); + tags["WORK"] = StringList("Work"); + + ScopedFileCopy copy("no-tags", ".m4a"); + { + MP4::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + CPPUNIT_ASSERT(properties.isEmpty()); + f.setProperties(tags); + f.save(); + } + { + const MP4::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + if (tags != properties) { + CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); + } + CPPUNIT_ASSERT(tags == properties); + } + } + void testPropertiesMovement() { MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index 240231a4..1db1c9d4 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -58,6 +58,7 @@ class TestMPEG : public CppUnit::TestFixture CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST(testFrameOffset); CPPUNIT_TEST(testStripAndProperties); + CPPUNIT_TEST(testProperties); CPPUNIT_TEST(testRepeatedSave1); CPPUNIT_TEST(testRepeatedSave2); CPPUNIT_TEST(testRepeatedSave3); @@ -295,6 +296,109 @@ public: } } + void testProperties() + { + PropertyMap tags; + tags["ALBUM"] = StringList("Album"); + tags["ALBUMARTIST"] = StringList("Album Artist"); + tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); + tags["ALBUMSORT"] = StringList("Album Sort"); + tags["ARRANGER"] = StringList("Arranger"); + tags["ARTIST"] = StringList("Artist"); + tags["ARTISTSORT"] = StringList("Artist Sort"); + tags["ARTISTWEBPAGE"] = StringList("Artist Web Page"); + tags["ASIN"] = StringList("ASIN"); + tags["AUDIOSOURCEWEBPAGE"] = StringList("Audio Source Web Page"); + tags["BARCODE"] = StringList("Barcode"); + tags["BPM"] = StringList("123"); + tags["CATALOGNUMBER"] = StringList("Catalog Number"); + tags["COMMENT"] = StringList("Comment"); + tags["COMMENT:CDESC"] = StringList("Comment with Description"); + tags["COMPOSER"] = StringList("Composer"); + tags["COMPOSERSORT"] = StringList("Composer Sort"); + tags["CONDUCTOR"] = StringList("Conductor"); + tags["CONTENTGROUP"] = StringList("Content Group"); + tags["COPYRIGHT"] = StringList("2021 Copyright"); + tags["COPYRIGHTURL"] = StringList("Copyright URL"); + tags["DATE"] = StringList("2021-01-03 12:29:23"); + tags["DISCNUMBER"] = StringList("3/5"); + tags["DJMIXER"] = StringList("DJ Mixer"); + tags["ENCODEDBY"] = StringList("Encoded by"); + tags["ENCODING"] = StringList("Encoding"); + tags["ENCODINGTIME"] = StringList("2021-01-03 13:48:44"); + tags["ENGINEER"] = StringList("Engineer"); + tags["FILETYPE"] = StringList("File Type"); + tags["FILEWEBPAGE"] = StringList("File Web Page"); + tags["GENRE"] = StringList("Genre"); + tags["GROUPING"] = StringList("Grouping"); + tags["INITIALKEY"] = StringList("Dbm"); + tags["ISRC"] = StringList("UKAAA0500001"); + tags["LABEL"] = StringList("Label"); + tags["LANGUAGE"] = StringList("eng"); + tags["LENGTH"] = StringList("1234"); + tags["LYRICIST"] = StringList("Lyricist"); + tags["LYRICS:LDESC"] = StringList("Lyrics"); + tags["MEDIA"] = StringList("Media"); + tags["MIXER"] = StringList("Mixer"); + tags["MOOD"] = StringList("Mood"); + tags["MOVEMENTNAME"] = StringList("Movement Name"); + tags["MOVEMENTNUMBER"] = StringList("2"); + tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); + tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); + tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); + tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); + tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); + tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); + tags["MUSICBRAINZ_WORKID"] = StringList("MusicBrainz_WorkID"); + tags["ORIGINALALBUM"] = StringList("Original Album"); + tags["ORIGINALARTIST"] = StringList("Original Artist"); + tags["ORIGINALDATE"] = StringList("2021-01-03 13:52:19"); + tags["ORIGINALFILENAME"] = StringList("Original Filename"); + tags["ORIGINALLYRICIST"] = StringList("Original Lyricist"); + tags["OWNER"] = StringList("Owner"); + tags["PAYMENTWEBPAGE"] = StringList("Payment Web Page"); + tags["PERFORMER:DRUMS"] = StringList("Drummer"); + tags["PERFORMER:GUITAR"] = StringList("Guitarist"); + tags["PLAYLISTDELAY"] = StringList("10"); + tags["PODCAST"] = StringList(); + tags["PODCASTCATEGORY"] = StringList("Podcast Category"); + tags["PODCASTDESC"] = StringList("Podcast Description"); + tags["PODCASTID"] = StringList("Podcast ID"); + tags["PODCASTURL"] = StringList("Podcast URL"); + tags["PRODUCEDNOTICE"] = StringList("2021 Produced Notice"); + tags["PRODUCER"] = StringList("Producer"); + tags["PUBLISHERWEBPAGE"] = StringList("Publisher Web Page"); + tags["RADIOSTATION"] = StringList("Radio Station"); + tags["RADIOSTATIONOWNER"] = StringList("Radio Station Owner"); + tags["RELEASECOUNTRY"] = StringList("Release Country"); + tags["RELEASESTATUS"] = StringList("Release Status"); + tags["RELEASETYPE"] = StringList("Release Type"); + tags["REMIXER"] = StringList("Remixer"); + tags["SCRIPT"] = StringList("Script"); + tags["SUBTITLE"] = StringList("Subtitle"); + tags["TITLE"] = StringList("Title"); + tags["TITLESORT"] = StringList("Title Sort"); + tags["TRACKNUMBER"] = StringList("2/4"); + tags["URL:UDESC"] = StringList("URL"); + + ScopedFileCopy copy("xing", ".mp3"); + { + MPEG::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + CPPUNIT_ASSERT(properties.isEmpty()); + f.setProperties(tags); + f.save(); + } + { + const MPEG::File f(copy.fileName().c_str()); + PropertyMap properties = f.properties(); + if (tags != properties) { + CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); + } + CPPUNIT_ASSERT(tags == properties); + } + } + void testRepeatedSave1() { ScopedFileCopy copy("xing", ".mp3");