Skip to content

Commit 88d6b18

Browse files
committed
Convert IPLS to TIPL and TMCL (#1274)
The involvement/involvee pairs which are supported for TIPL properties (ARRANGER, ENGINEER, PRODUCER, DJ-MIX, MIX) are left in the TIPL frame, other pairs are moved to a TMCL frame. This will result in a consistent behavior for both ID3v2.3 and ID3v2.4 tags produced by MusicBrainz Picard.
1 parent d61a333 commit 88d6b18

2 files changed

Lines changed: 56 additions & 10 deletions

File tree

taglib/mpeg/id3v2/id3v2framefactory.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,49 @@ void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const
357357
}
358358
}
359359
}
360+
if(tag->header()->majorVersion() < 4 &&
361+
tag->frameList("TIPL").size() == 1 &&
362+
tag->frameList("TMCL").size() == 0)
363+
{
364+
// FrameFactory::updateFrame() has mapped IPLS (ID3v2.3)/ IPL (ID3v2.2)
365+
// to TIPL (ID3v2.4). However, the musicians should be rather in TMCL.
366+
// Move all involvement/involvee pairs which are not supported by the
367+
// TIPL property map interface to a TMCL frame.
368+
if(auto tipl =
369+
dynamic_cast<TextIdentificationFrame *>(tag->frameList("TIPL").front())) {
370+
if(StringList tiplValues = tipl->toStringList(); tiplValues.size() % 2 == 0) {
371+
static StringList tiplKeys;
372+
if(tiplKeys.isEmpty()) {
373+
for(const auto &kv : TextIdentificationFrame::involvedPeopleMap()) {
374+
tiplKeys.append(kv.second);
375+
}
376+
}
377+
StringList tmclValues;
378+
for(auto it = tiplValues.begin(); it != tiplValues.end();) {
379+
const String involvement = *it;
380+
if(!tiplKeys.contains(involvement.upper())) {
381+
tmclValues.append(involvement);
382+
it = tiplValues.erase(it);
383+
tmclValues.append(*it);
384+
it = tiplValues.erase(it);
385+
} else {
386+
++it;
387+
++it;
388+
}
389+
}
390+
if(!tmclValues.isEmpty()) {
391+
auto tmcl = new TextIdentificationFrame("TMCL");
392+
tmcl->setText(tmclValues);
393+
tag->addFrame(tmcl);
394+
if(!tiplValues.isEmpty()) {
395+
tipl->setText(tiplValues);
396+
} else {
397+
tag->removeFrame(tipl);
398+
}
399+
}
400+
}
401+
}
402+
}
360403
}
361404

362405
String::Type FrameFactory::defaultTextEncoding() const

tests/test_id3v2.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ class TestID3v2 : public CppUnit::TestFixture
10351035
tf->setText(StringList().append("Guitar").append("Artist 1").append("Drums").append("Artist 2"));
10361036
foo.ID3v2Tag()->addFrame(tf);
10371037
tf = new ID3v2::TextIdentificationFrame("TIPL", String::Latin1);
1038-
tf->setText(StringList().append("Producer").append("Artist 3").append("Mastering").append("Artist 4"));
1038+
tf->setText(StringList().append("Producer").append("Artist 3").append("Engineer").append("Artist 4"));
10391039
foo.ID3v2Tag()->addFrame(tf);
10401040
tf = new ID3v2::TextIdentificationFrame("TCON", String::Latin1);
10411041
tf->setText(StringList().append("51").append("Noise").append("Power Noise"));
@@ -1062,15 +1062,18 @@ class TestID3v2 : public CppUnit::TestFixture
10621062
CPPUNIT_ASSERT_EQUAL(String("2012-04-17T12:01"), tf->fieldList().front());
10631063
tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TIPL").front());
10641064
CPPUNIT_ASSERT(tf);
1065-
CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(8), tf->fieldList().size());
1065+
CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), tf->fieldList().size());
1066+
CPPUNIT_ASSERT_EQUAL(String("Producer"), tf->fieldList()[0]);
1067+
CPPUNIT_ASSERT_EQUAL(String("Artist 3"), tf->fieldList()[1]);
1068+
CPPUNIT_ASSERT_EQUAL(String("Engineer"), tf->fieldList()[2]);
1069+
CPPUNIT_ASSERT_EQUAL(String("Artist 4"), tf->fieldList()[3]);
1070+
tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TMCL").front());
1071+
CPPUNIT_ASSERT(tf);
1072+
CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), tf->fieldList().size());
10661073
CPPUNIT_ASSERT_EQUAL(String("Guitar"), tf->fieldList()[0]);
10671074
CPPUNIT_ASSERT_EQUAL(String("Artist 1"), tf->fieldList()[1]);
10681075
CPPUNIT_ASSERT_EQUAL(String("Drums"), tf->fieldList()[2]);
10691076
CPPUNIT_ASSERT_EQUAL(String("Artist 2"), tf->fieldList()[3]);
1070-
CPPUNIT_ASSERT_EQUAL(String("Producer"), tf->fieldList()[4]);
1071-
CPPUNIT_ASSERT_EQUAL(String("Artist 3"), tf->fieldList()[5]);
1072-
CPPUNIT_ASSERT_EQUAL(String("Mastering"), tf->fieldList()[6]);
1073-
CPPUNIT_ASSERT_EQUAL(String("Artist 4"), tf->fieldList()[7]);
10741077
tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TCON").front());
10751078
CPPUNIT_ASSERT(tf);
10761079
CPPUNIT_ASSERT_EQUAL(3U, tf->fieldList().size());
@@ -1090,18 +1093,18 @@ class TestID3v2 : public CppUnit::TestFixture
10901093
}
10911094
{
10921095
const ByteVector expectedId3v23Data(
1093-
"ID3" "\x03\x00\x00\x00\x00\x09\x49"
1096+
"ID3" "\x03\x00\x00\x00\x00\x09\x48"
10941097
"TSOA" "\x00\x00\x00\x01\x00\x00\x00"
10951098
"TSOT" "\x00\x00\x00\x01\x00\x00\x00"
10961099
"TSOP" "\x00\x00\x00\x01\x00\x00\x00"
10971100
"TORY" "\x00\x00\x00\x05\x00\x00\x00" "2011"
10981101
"TYER" "\x00\x00\x00\x05\x00\x00\x00" "2012"
10991102
"TDAT" "\x00\x00\x00\x05\x00\x00\x00" "1704"
11001103
"TIME" "\x00\x00\x00\x05\x00\x00\x00" "1201"
1101-
"IPLS" "\x00\x00\x00\x44\x00\x00\x00" "Guitar" "\x00"
1104+
"IPLS" "\x00\x00\x00\x43\x00\x00\x00" "Guitar" "\x00"
11021105
"Artist 1" "\x00" "Drums" "\x00" "Artist 2" "\x00" "Producer" "\x00"
1103-
"Artist 3" "\x00" "Mastering" "\x00" "Artist 4"
1104-
"TCON" "\x00\x00\x00\x14\x00\x00\x00" "(51)(39)Power Noise", 211);
1106+
"Artist 3" "\x00" "Engineer" "\x00" "Artist 4"
1107+
"TCON" "\x00\x00\x00\x14\x00\x00\x00" "(51)(39)Power Noise", 210);
11051108
const ByteVector actualId3v23Data =
11061109
PlainFile(newname.c_str()).readBlock(expectedId3v23Data.size());
11071110
CPPUNIT_ASSERT_EQUAL(expectedId3v23Data, actualId3v23Data);

0 commit comments

Comments
 (0)