Simple ID3v2.4 => ID3v2.3 frame migration

This commit is contained in:
Lukáš Lalinský 2011-03-16 17:14:36 +01:00
parent 061b381ea8
commit e8d0551c9a
3 changed files with 112 additions and 1 deletions

View File

@ -334,6 +334,61 @@ ByteVector ID3v2::Tag::render() const
return render(4);
}
void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const
{
const char *unsupportedFrames[] = {
"ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG",
"TMOO", "TPRO", "TSOA", "TSOT", "TSST", "TSOP", "TIPL",
"TMCL", 0
};
ID3v2::TextIdentificationFrame *frameTDOR = 0;
ID3v2::TextIdentificationFrame *frameTDRC = 0;
for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) {
ID3v2::Frame *frame = *it;
ByteVector frameID = frame->header()->frameID();
for(int i = 0; unsupportedFrames[i]; i++) {
if(frameID == unsupportedFrames[i]) {
debug("A frame that is not supported in ID3v2.3 \'"
+ String(frameID) + "\' has been discarded");
frame = 0;
break;
}
}
if(frame && frameID == "TDOR") {
frameTDOR = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
frame = 0;
}
if(frame && frameID == "TDRC") {
frameTDRC = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
frame = 0;
}
if(frame) {
frames->append(frame);
}
}
if(frameTDOR) {
String content = frameTDOR->toString();
if(content.size() >= 4) {
ID3v2::TextIdentificationFrame *frameTORY = new ID3v2::TextIdentificationFrame("TORY", String::Latin1);
frameTORY->setText(content.substr(0, 4));
frames->append(frameTORY);
newFrames->append(frameTORY);
}
}
if(frameTDRC) {
// FIXME we can do better here, define also TDAT and TIME
String content = frameTDRC->toString();
if(content.size() >= 4) {
ID3v2::TextIdentificationFrame *frameTYER = new ID3v2::TextIdentificationFrame("TYER", String::Latin1);
frameTYER->setText(content.substr(0, 4));
frames->append(frameTYER);
newFrames->append(frameTYER);
}
}
// FIXME migrate TIPL and TMCL to IPLS
}
ByteVector ID3v2::Tag::render(int version) const
{
// We need to render the "tag data" first so that we have to correct size to
@ -347,7 +402,18 @@ ByteVector ID3v2::Tag::render(int version) const
// Loop through the frames rendering them and adding them to the tagData.
for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) {
FrameList newFrames;
newFrames.setAutoDelete(true);
FrameList frameList;
if(version == 4) {
frameList = d->frameList;
}
else {
downgradeFrames(&frameList, &newFrames);
}
for(FrameList::Iterator it = frameList.begin(); it != frameList.end(); it++) {
(*it)->header()->setVersion(version);
if((*it)->header()->frameID().size() != 4) {
debug("A frame of unsupported or unknown type \'"

View File

@ -295,6 +295,8 @@ namespace TagLib {
*/
void setTextFrame(const ByteVector &id, const String &value);
void downgradeFrames(FrameList *existingFrames, FrameList *newFrames) const;
private:
Tag(const Tag &);
Tag &operator=(const Tag &);

View File

@ -64,6 +64,7 @@ class TestID3v2 : public CppUnit::TestFixture
CPPUNIT_TEST(testUpdateGenre23_2);
CPPUNIT_TEST(testUpdateGenre24);
CPPUNIT_TEST(testUpdateDate22);
CPPUNIT_TEST(testDowngradeTo23);
// CPPUNIT_TEST(testUpdateFullDate22); TODO TYE+TDA should be upgraded to TDRC together
CPPUNIT_TEST(testCompressedFrameWithBrokenLength);
CPPUNIT_TEST_SUITE_END();
@ -474,6 +475,48 @@ public:
CPPUNIT_ASSERT_EQUAL(String("2010-04-03"), f.ID3v2Tag()->frameListMap()["TDRC"].front()->toString());
}
void testDowngradeTo23()
{
ScopedFileCopy copy("xing", ".mp3");
string newname = copy.fileName();
ID3v2::TextIdentificationFrame *tf;
MPEG::File foo(newname.c_str());
tf = new ID3v2::TextIdentificationFrame("TDOR", String::Latin1);
tf->setText("2011-03-16");
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TDRC", String::Latin1);
tf->setText("2012-04-17");
foo.ID3v2Tag()->addFrame(tf);
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDRL", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDTG", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TMOO", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TPRO", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOA", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOT", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSST", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOP", String::Latin1));
foo.save(MPEG::File::AllTags, true, 3);
MPEG::File bar(newname.c_str());
tf = static_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TDOR").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("2011"), tf->fieldList().front());
tf = static_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TDRC").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("2012"), tf->fieldList().front());
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDRL"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDTG"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TMOO"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TPRO"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOA"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOT"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSST"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOP"));
}
void testCompressedFrameWithBrokenLength()
{
MPEG::File f("data/compressed_id3_frame.mp3", false);