52
* @param dn directory name
53
* @param di directory information
53
54
* @param fn filename
56
Mp3File::Mp3File(const QString& dn, const QString& fn) :
56
Mp3File::Mp3File(const DirInfo* di, const QString& fn) :
57
TaggedFile(di, fn), m_tagV1(0), m_tagV2(0)
67
Mp3File::~Mp3File(void)
78
75
* Read tags from file.
80
* @param force TRUE to force reading even if tags were already read.
77
* @param force true to force reading even if tags were already read.
83
79
void Mp3File::readTags(bool force)
85
Q3CString fn = QFile::encodeName(dirname + QDir::separator() + filename);
81
Q3CString fn = QFile::encodeName(getDirInfo()->getDirname() + QDir::separator() + currentFilename());
89
tagV1->Link(fn, ID3TT_ID3V1);
83
if (force && m_tagV1) {
85
m_tagV1->Link(fn, ID3TT_ID3V1);
86
markTag1Changed(false);
95
tagV1->Link(fn, ID3TT_ID3V1);
89
m_tagV1 = new ID3_Tag;
91
m_tagV1->Link(fn, ID3TT_ID3V1);
92
markTag1Changed(false);
100
if (force && tagV2) {
102
tagV2->Link(fn, ID3TT_ID3V2);
96
if (force && m_tagV2) {
98
m_tagV2->Link(fn, ID3TT_ID3V2);
99
markTag2Changed(false);
108
tagV2->Link(fn, ID3TT_ID3V2);
102
m_tagV2 = new ID3_Tag;
104
m_tagV2->Link(fn, ID3TT_ID3V2);
105
markTag2Changed(false);
114
new_filename = filename;
110
setFilename(currentFilename());
119
115
* Write tags to file and rename it if necessary.
121
* @param force TRUE to force writing even if file was not changed.
122
* @param renamed will be set to TRUE if the file was renamed,
117
* @param force true to force writing even if file was not changed.
118
* @param renamed will be set to true if the file was renamed,
123
119
* i.e. the file name is no longer valid, else *renamed
124
120
* is left unchanged
125
121
* @param preserve true to preserve file time stamps
127
* @return TRUE if ok, FALSE if the file could not be written or renamed.
123
* @return true if ok, false if the file could not be written or renamed.
130
bool Mp3File::writeTags(bool force, bool *renamed, bool preserve)
125
bool Mp3File::writeTags(bool force, bool* renamed, bool preserve)
132
QString fnStr(dirname + QDir::separator() + filename);
127
QString fnStr(getDirInfo()->getDirname() + QDir::separator() + currentFilename());
133
128
if (isChanged() && !QFileInfo(fnStr).isWritable()) {
137
132
// store time stamp if it has to be preserved
151
146
// There seems to be a bug in id3lib: The V1 genre is not
152
147
// removed. So we check here and strip the whole header
153
148
// if there are no frames.
154
if (tagV1 && (force || changedV1) && (tagV1->NumFrames() == 0)) {
155
tagV1->Strip(ID3TT_ID3V1);
149
if (m_tagV1 && (force || isTag1Changed()) && (m_tagV1->NumFrames() == 0)) {
150
m_tagV1->Strip(ID3TT_ID3V1);
151
markTag1Changed(false);
158
153
// Even after removing all frames, HasV2Tag() still returns true,
159
154
// so we strip the whole header.
160
if (tagV2 && (force || changedV2) && (tagV2->NumFrames() == 0)) {
161
tagV2->Strip(ID3TT_ID3V2);
155
if (m_tagV2 && (force || isTag2Changed()) && (m_tagV2->NumFrames() == 0)) {
156
m_tagV2->Strip(ID3TT_ID3V2);
157
markTag2Changed(false);
164
159
// There seems to be a bug in id3lib: If I update an ID3v1 and then
165
160
// strip the ID3v2 the ID3v1 is removed too and vice versa, so I
166
161
// first make any stripping and then the updating.
167
if (tagV1 && (force || changedV1) && (tagV1->NumFrames() > 0)) {
168
tagV1->Update(ID3TT_ID3V1);
162
if (m_tagV1 && (force || isTag1Changed()) && (m_tagV1->NumFrames() > 0)) {
163
m_tagV1->Update(ID3TT_ID3V1);
164
markTag1Changed(false);
171
if (tagV2 && (force || changedV2) && (tagV2->NumFrames() > 0)) {
172
tagV2->Update(ID3TT_ID3V2);
166
if (m_tagV2 && (force || isTag2Changed()) && (m_tagV2->NumFrames() > 0)) {
167
m_tagV2->Update(ID3TT_ID3V2);
168
markTag2Changed(false);
176
171
// restore time stamp
178
173
::utime(fn, ×);
181
if (new_filename != filename) {
182
if (!renameFile(filename, new_filename)) {
176
if (getFilename() != currentFilename()) {
177
if (!renameFile(currentFilename(), getFilename())) {
180
updateCurrentFilename();
181
// link tags to new file name
193
191
* @param flt filter specifying which fields to remove
196
193
void Mp3File::removeTagsV1(const StandardTagsFilter& flt)
199
196
if (flt.areAllTrue()) {
200
ID3_Tag::Iterator* iter = tagV1->CreateIterator();
197
ID3_Tag::Iterator* iter = m_tagV1->CreateIterator();
201
198
ID3_Frame* frame;
202
199
while ((frame = iter->GetNext()) != NULL) {
203
tagV1->RemoveFrame(frame);
200
m_tagV1->RemoveFrame(frame);
206
203
/* allocated in Windows DLL => must be freed in the same DLL */
221
219
* @param flt filter specifying which fields to remove
224
221
void Mp3File::removeTagsV2(const StandardTagsFilter& flt)
227
224
if (flt.areAllTrue()) {
228
ID3_Tag::Iterator* iter = tagV2->CreateIterator();
225
ID3_Tag::Iterator* iter = m_tagV2->CreateIterator();
229
226
ID3_Frame* frame;
230
227
while ((frame = iter->GetNext()) != NULL) {
231
tagV2->RemoveFrame(frame);
228
m_tagV2->RemoveFrame(frame);
234
231
/* allocated in Windows DLL => must be freed in the same DLL */
251
248
* @return string,
252
249
* "" if the field does not exist.
255
251
QString Mp3File::getString(ID3_Field* field)
257
253
QString text("");
258
254
if (field != NULL) {
259
255
ID3_TextEnc enc = field->GetEncoding();
260
256
if (enc == ID3TE_UTF16 || enc == ID3TE_UTF16BE) {
261
const unicode_t *txt = field->GetRawUnicodeText();
257
const unicode_t* txt = field->GetRawUnicodeText();
262
258
uint unicode_size = field->Size() / sizeof(unicode_t);
263
259
if (unicode_size && txt) {
264
QChar *qcarray = new QChar[unicode_size];
260
QChar* qcarray = new QChar[unicode_size];
266
262
// Unfortunately, Unicode support in id3lib is rather buggy
267
263
// in the current version: The codes are mirrored.
303
299
* "" if the field does not exist,
304
300
* QString::null if the tags do not exist.
307
QString Mp3File::getTextField(const ID3_Tag *tag, ID3_FrameID id)
302
QString Mp3File::getTextField(const ID3_Tag* tag, ID3_FrameID id)
310
305
return QString::null;
314
ID3_Frame *frame = tag->Find(id);
309
ID3_Frame* frame = tag->Find(id);
315
310
if (frame && ((fld = frame->GetField(ID3FN_TEXT)) != NULL)) {
316
311
str = getString(fld);
326
321
* 0 if the field does not exist,
327
322
* -1 if the tags do not exist.
330
int Mp3File::getYear(const ID3_Tag *tag)
324
int Mp3File::getYear(const ID3_Tag* tag)
332
326
QString str = getTextField(tag, ID3FID_YEAR);
333
327
if (str.isNull()) return -1;
343
337
* 0 if the field does not exist,
344
338
* -1 if the tags do not exist.
347
int Mp3File::getTrackNum(const ID3_Tag *tag)
340
int Mp3File::getTrackNum(const ID3_Tag* tag)
349
342
QString str = getTextField(tag, ID3FID_TRACKNUM);
350
343
if (str.isNull()) return -1;
365
358
* 0xff if the field does not exist,
366
359
* -1 if the tags do not exist.
369
int Mp3File::getGenreNum(const ID3_Tag *tag)
361
int Mp3File::getGenreNum(const ID3_Tag* tag)
371
363
QString str = getTextField(tag, ID3FID_CONTENTTYPE);
372
364
if (str.isNull()) return -1;
393
385
* @param field field
394
386
* @param text text to set
397
void Mp3File::setString(ID3_Field* field, const QString &text)
388
void Mp3File::setString(ID3_Field* field, const QString& text)
399
390
ID3_TextEnc enc = field->GetEncoding();
400
391
// (ID3TE_IS_DOUBLE_BYTE_ENC(enc))
408
399
// In the hope that my patches will be included, I try here to
409
400
// work around these bugs, but there is no solution for the
410
401
// LSB >= 0x80 bug.
411
const QChar *qcarray = text.unicode();
402
const QChar* qcarray = text.unicode();
412
403
uint unicode_size = text.length();
413
unicode_t *unicode = new unicode_t[unicode_size + 1];
404
unicode_t* unicode = new unicode_t[unicode_size + 1];
416
407
for (i = 0; i < unicode_size; i++) {
445
436
* @return true if the field was changed.
448
bool Mp3File::setTextField(ID3_Tag *tag, ID3_FrameID id, const QString &text,
438
bool Mp3File::setTextField(ID3_Tag* tag, ID3_FrameID id, const QString& text,
449
439
bool allowUnicode, bool replace, bool removeEmpty)
451
441
bool changed = false;
466
456
if (allowUnicode && fld->GetEncoding() == ID3TE_ISO8859_1) {
467
457
// check if information is lost if the string is not unicode
468
458
uint i, unicode_size = text.length();
469
const QChar *qcarray = text.unicode();
459
const QChar* qcarray = text.unicode();
470
460
for (i = 0; i < unicode_size; i++) {
471
461
if (qcarray[i].latin1() == 0) {
472
ID3_Field *encfld = frame->GetField(ID3FN_TEXTENC);
462
ID3_Field* encfld = frame->GetField(ID3FN_TEXTENC);
474
464
encfld->Set(ID3TE_UTF16);
572
559
* "" if the field does not exist,
573
560
* QString::null if the tags do not exist.
576
QString Mp3File::getTitleV1(void)
562
QString Mp3File::getTitleV1()
578
return getTextField(tagV1, ID3FID_TITLE);
564
return getTextField(m_tagV1, ID3FID_TITLE);
585
571
* "" if the field does not exist,
586
572
* QString::null if the tags do not exist.
589
QString Mp3File::getArtistV1(void)
574
QString Mp3File::getArtistV1()
591
return getTextField(tagV1, ID3FID_LEADARTIST);
576
return getTextField(m_tagV1, ID3FID_LEADARTIST);
598
583
* "" if the field does not exist,
599
584
* QString::null if the tags do not exist.
602
QString Mp3File::getAlbumV1(void)
586
QString Mp3File::getAlbumV1()
604
return getTextField(tagV1, ID3FID_ALBUM);
588
return getTextField(m_tagV1, ID3FID_ALBUM);
611
595
* "" if the field does not exist,
612
596
* QString::null if the tags do not exist.
615
QString Mp3File::getCommentV1(void)
598
QString Mp3File::getCommentV1()
617
return getTextField(tagV1, ID3FID_COMMENT);
600
return getTextField(m_tagV1, ID3FID_COMMENT);
637
619
* 0 if the field does not exist,
638
620
* -1 if the tags do not exist.
641
int Mp3File::getTrackNumV1(void)
622
int Mp3File::getTrackNumV1()
643
return getTrackNum(tagV1);
624
return getTrackNum(m_tagV1);
647
628
* Get ID3v1 genre.
650
* 0xff if the field does not exist,
651
* -1 if the tags do not exist.
631
* "" if the field does not exist,
632
* QString::null if the tags do not exist.
654
int Mp3File::getGenreNumV1(void)
634
QString Mp3File::getGenreV1()
656
return getGenreNum(tagV1);
636
int num = getGenreNum(m_tagV1);
638
return QString::null;
639
} else if (num == 0xff) {
642
return Genres::getName(num);
663
650
* "" if the field does not exist,
664
651
* QString::null if the tags do not exist.
667
QString Mp3File::getTitleV2(void)
653
QString Mp3File::getTitleV2()
669
return getTextField(tagV2, ID3FID_TITLE);
655
return getTextField(m_tagV2, ID3FID_TITLE);
676
662
* "" if the field does not exist,
677
663
* QString::null if the tags do not exist.
680
QString Mp3File::getArtistV2(void)
665
QString Mp3File::getArtistV2()
682
return getTextField(tagV2, ID3FID_LEADARTIST);
667
return getTextField(m_tagV2, ID3FID_LEADARTIST);
689
674
* "" if the field does not exist,
690
675
* QString::null if the tags do not exist.
693
QString Mp3File::getAlbumV2(void)
677
QString Mp3File::getAlbumV2()
695
return getTextField(tagV2, ID3FID_ALBUM);
679
return getTextField(m_tagV2, ID3FID_ALBUM);
702
686
* "" if the field does not exist,
703
687
* QString::null if the tags do not exist.
706
QString Mp3File::getCommentV2(void)
689
QString Mp3File::getCommentV2()
708
return getTextField(tagV2, ID3FID_COMMENT);
691
return getTextField(m_tagV2, ID3FID_COMMENT);
728
710
* 0 if the field does not exist,
729
711
* -1 if the tags do not exist.
732
int Mp3File::getTrackNumV2(void)
734
return getTrackNum(tagV2);
741
* 0xff if the field does not exist,
742
* -1 if the tags do not exist.
745
int Mp3File::getGenreNumV2(void)
747
return getGenreNum(tagV2);
713
int Mp3File::getTrackNumV2()
715
return getTrackNum(m_tagV2);
757
725
QString Mp3File::getGenreV2()
759
int num = getGenreNumV2();
727
int num = getGenreNum(m_tagV2);
760
728
if (num != 0xff && num != -1) {
761
729
return Genres::getName(num);
763
return getTextField(tagV2, ID3FID_CONTENTTYPE);
731
return getTextField(m_tagV2, ID3FID_CONTENTTYPE);
770
738
* @param str string to set, "" to remove field.
773
740
void Mp3File::setTitleV1(const QString& str)
775
if (setTextField(tagV1, ID3FID_TITLE, str)) {
742
if (setTextField(m_tagV1, ID3FID_TITLE, str)) {
744
QString s = checkTruncation(str, StandardTags::TF_Title);
745
if (!s.isNull()) setTextField(m_tagV1, ID3FID_TITLE, s);
783
752
* @param str string to set, "" to remove field.
786
754
void Mp3File::setArtistV1(const QString& str)
788
if (setTextField(tagV1, ID3FID_LEADARTIST, str)) {
756
if (setTextField(m_tagV1, ID3FID_LEADARTIST, str)) {
758
QString s = checkTruncation(str, StandardTags::TF_Artist);
759
if (!s.isNull()) setTextField(m_tagV1, ID3FID_LEADARTIST, s);
796
766
* @param str string to set, "" to remove field.
799
768
void Mp3File::setAlbumV1(const QString& str)
801
if (setTextField(tagV1, ID3FID_ALBUM, str)) {
770
if (setTextField(m_tagV1, ID3FID_ALBUM, str)) {
772
QString s = checkTruncation(str, StandardTags::TF_Album);
773
if (!s.isNull()) setTextField(m_tagV1, ID3FID_ALBUM, s);
809
780
* @param str string to set, "" to remove field.
812
782
void Mp3File::setCommentV1(const QString& str)
814
if (setTextField(tagV1, ID3FID_COMMENT, str)) {
784
if (setTextField(m_tagV1, ID3FID_COMMENT, str)) {
786
QString s = checkTruncation(str, StandardTags::TF_Comment, 28);
787
if (!s.isNull()) setTextField(m_tagV1, ID3FID_COMMENT, s);
835
806
* @param num number to set, 0 to remove field.
838
808
void Mp3File::setTrackNumV1(int num)
840
if (setTrackNum(tagV1, num)) {
810
if (setTrackNum(m_tagV1, num)) {
812
int n = checkTruncation(num, StandardTags::TF_Track);
813
if (n != -1) setTrackNum(m_tagV1, n);
818
* Set ID3v1 genre as text.
848
* @param num number to set, 0xff to remove field.
820
* @param str string to set, "" to remove field, QString::null to ignore.
851
void Mp3File::setGenreNumV1(int num)
822
void Mp3File::setGenreV1(const QString& str)
853
if (setGenreNum(tagV1, num)) {
825
int num = Genres::getNumber(str);
826
if (setGenreNum(m_tagV1, num)) {
829
// if the string cannot be converted to a number, set the truncation flag
830
checkTruncation(num == 0xff && !str.isEmpty() ? 1 : 0,
831
StandardTags::TF_Genre, 0);
861
838
* @param str string to set, "" to remove field.
864
840
void Mp3File::setTitleV2(const QString& str)
866
if (setTextField(tagV2, ID3FID_TITLE, str, true)) {
842
if (setTextField(m_tagV2, ID3FID_TITLE, str, true)) {
874
850
* @param str string to set, "" to remove field.
877
852
void Mp3File::setArtistV2(const QString& str)
879
if (setTextField(tagV2, ID3FID_LEADARTIST, str, true)) {
854
if (setTextField(m_tagV2, ID3FID_LEADARTIST, str, true)) {
887
862
* @param str string to set, "" to remove field.
890
864
void Mp3File::setAlbumV2(const QString& str)
892
if (setTextField(tagV2, ID3FID_ALBUM, str, true)) {
866
if (setTextField(m_tagV2, ID3FID_ALBUM, str, true)) {
900
874
* @param str string to set, "" to remove field.
903
876
void Mp3File::setCommentV2(const QString& str)
905
if (setTextField(tagV2, ID3FID_COMMENT, str, true)) {
878
if (setTextField(m_tagV2, ID3FID_COMMENT, str, true)) {
926
898
* @param num number to set, 0 to remove field.
929
900
void Mp3File::setTrackNumV2(int num)
931
902
int numTracks = getTotalNumberOfTracksIfEnabled();
932
if (setTrackNum(tagV2, num, numTracks)) {
940
* @param num number to set, 0xff to remove field.
943
void Mp3File::setGenreNumV2(int num)
945
if (setGenreNum(tagV2, num)) {
903
if (setTrackNum(m_tagV2, num, numTracks)) {
955
913
void Mp3File::setGenreV2(const QString& str)
957
if (setTextField(tagV2, ID3FID_CONTENTTYPE, str, true)) {
916
int num = Genres::getNumber(str);
917
if (num >= 0 && num != 0xff) {
918
if (setGenreNum(m_tagV2, num)) {
922
if (setTextField(m_tagV2, ID3FID_CONTENTTYPE, str, true)) {
1012
979
QString Mp3File::getDetailInfo() const {
1013
980
QString str("");
1014
981
const Mp3_Headerinfo* info = NULL;
1016
info = tagV1->GetMp3HeaderInfo();
1018
info = tagV2->GetMp3HeaderInfo();
983
info = m_tagV1->GetMp3HeaderInfo();
984
} else if (m_tagV2) {
985
info = m_tagV2->GetMp3HeaderInfo();
1021
988
switch (info->version) {
1099
1066
unsigned duration = 0;
1100
1067
const Mp3_Headerinfo* info = NULL;
1102
info = tagV1->GetMp3HeaderInfo();
1104
info = tagV2->GetMp3HeaderInfo();
1069
info = m_tagV1->GetMp3HeaderInfo();
1070
} else if (m_tagV2) {
1071
info = m_tagV2->GetMp3HeaderInfo();
1106
1073
if (info && info->time > 0) {
1107
1074
duration = info->time;