~ubuntu-branches/ubuntu/gutsy/kid3/gutsy

« back to all changes in this revision

Viewing changes to kid3/taglibfile.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Mercatante
  • Date: 2007-07-01 00:31:03 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070701003103-2qownnv49a7jdqm3
Tags: 0.9-0ubuntu1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
#include "standardtags.h"
21
21
#include "taglibframelist.h"
22
22
#include "genres.h"
 
23
#include "dirinfo.h"
23
24
#include <sys/stat.h>
24
25
#ifdef WIN32
25
26
#include <sys/utime.h>
45
46
/**
46
47
 * Constructor.
47
48
 *
48
 
 * @param dn directory name
 
49
 * @param di directory information
49
50
 * @param fn filename
50
51
 */
51
 
TagLibFile::TagLibFile(const QString& dn, const QString& fn) :
52
 
        TaggedFile(dn, fn), m_tagV1(0), m_tagV2(0), m_fileRead(false)
 
52
TagLibFile::TagLibFile(const DirInfo* di, const QString& fn) :
 
53
        TaggedFile(di, fn), m_tagV1(0), m_tagV2(0), m_fileRead(false)
53
54
{
54
55
}
55
56
 
67
68
 */
68
69
void TagLibFile::readTags(bool force)
69
70
{
70
 
        Q3CString fn = QFile::encodeName(dirname + QDir::separator() + filename);
 
71
        Q3CString fn = QFile::encodeName(getDirInfo()->getDirname() + QDir::separator() + currentFilename());
71
72
 
72
73
        if (force || m_fileRef.isNull()) {
73
74
                m_fileRef = TagLib::FileRef(fn);
74
75
                m_tagV1 = 0;
75
76
                m_tagV2 = 0;
76
 
                changedV1 = false;
77
 
                changedV2 = false;
 
77
                markTag1Changed(false);
 
78
                markTag2Changed(false);
78
79
                m_fileRead = true;
79
80
        }
80
81
 
88
89
                if ((mpegFile = dynamic_cast<TagLib::MPEG::File*>(file)) != 0) {
89
90
                        if (!m_tagV1) {
90
91
                                m_tagV1 = mpegFile->ID3v1Tag();
91
 
                                changedV1 = false;
 
92
                                markTag1Changed(false);
92
93
                        }
93
94
                        if (!m_tagV2) {
94
95
                                m_tagV2 = mpegFile->ID3v2Tag();
95
 
                                changedV2 = false;
 
96
                                markTag2Changed(false);
96
97
                        }
97
98
                } else if ((flacFile = dynamic_cast<TagLib::FLAC::File*>(file)) != 0) {
98
99
                        if (!m_tagV1) {
99
100
                                m_tagV1 = flacFile->ID3v1Tag();
100
 
                                changedV1 = false;
 
101
                                markTag1Changed(false);
101
102
                        }
102
103
                        if (!m_tagV2) {
103
104
                                m_tagV2 = flacFile->xiphComment();
104
 
                                changedV2 = false;
 
105
                                markTag2Changed(false);
105
106
                        }
106
107
#ifdef MPC_ID3V1
107
108
                } else if ((mpcFile = dynamic_cast<TagLib::MPC::File*>(file)) != 0) {
108
109
                        if (!m_tagV1) {
109
110
                                m_tagV1 = mpcFile->ID3v1Tag();
110
 
                                changedV1 = false;
 
111
                                markTag1Changed(false);
111
112
                        }
112
113
                        if (!m_tagV2) {
113
114
                                m_tagV2 = mpcFile->APETag();
114
 
                                changedV2 = false;
 
115
                                markTag2Changed(false);
115
116
                        }
116
117
#endif
117
118
                } else {
118
119
                        m_tagV1 = 0;
119
 
                        changedV1 = false;
 
120
                        markTag1Changed(false);
120
121
                        if (!m_tagV2) {
121
122
                                m_tagV2 = m_fileRef.tag();
122
 
                                changedV2 = false;
 
123
                                markTag2Changed(false);
123
124
                        }
124
125
                }
125
126
        }
126
127
 
127
128
        if (force) {
128
 
                new_filename = filename;
 
129
                setFilename(currentFilename());
129
130
        }
130
131
}
131
132
 
140
141
 *
141
142
 * @return true if ok, false if the file could not be written or renamed.
142
143
 */
143
 
bool TagLibFile::writeTags(bool force, bool *renamed, bool preserve)
 
144
bool TagLibFile::writeTags(bool force, bool* renamed, bool preserve)
144
145
{
145
 
        QString fnStr(dirname + QDir::separator() + filename);
 
146
        QString fnStr(getDirInfo()->getDirname() + QDir::separator() + currentFilename());
146
147
        if (isChanged() && !QFileInfo(fnStr).isWritable()) {
147
148
                return false;
148
149
        }
166
167
        if (!m_fileRef.isNull() && (file = m_fileRef.file()) != 0) {
167
168
                TagLib::MPEG::File* mpegFile = dynamic_cast<TagLib::MPEG::File*>(file);
168
169
                if (mpegFile) {
169
 
                        if (m_tagV1 && (force || changedV1) && m_tagV1->isEmpty()) {
 
170
                        if (m_tagV1 && (force || isTag1Changed()) && m_tagV1->isEmpty()) {
170
171
                                mpegFile->strip(TagLib::MPEG::File::ID3v1);
171
172
                                fileChanged = true;
172
 
                                changedV1 = false;
 
173
                                markTag1Changed(false);
173
174
                                m_tagV1 = 0;
174
175
                        }
175
 
                        if (m_tagV2 && (force || changedV2) && m_tagV2->isEmpty()) {
 
176
                        if (m_tagV2 && (force || isTag2Changed()) && m_tagV2->isEmpty()) {
176
177
                                mpegFile->strip(TagLib::MPEG::File::ID3v2);
177
178
                                fileChanged = true;
178
 
                                changedV2 = false;
 
179
                                markTag2Changed(false);
179
180
                                m_tagV2 = 0;
180
181
                        }
181
182
                        int saveMask = 0;
182
 
                        if (m_tagV1 && (force || changedV1) && !m_tagV1->isEmpty()) {
 
183
                        if (m_tagV1 && (force || isTag1Changed()) && !m_tagV1->isEmpty()) {
183
184
                                saveMask |= TagLib::MPEG::File::ID3v1;
184
185
                        }
185
 
                        if (m_tagV2 && (force || changedV2) && !m_tagV2->isEmpty()) {
 
186
                        if (m_tagV2 && (force || isTag2Changed()) && !m_tagV2->isEmpty()) {
186
187
                                saveMask |= TagLib::MPEG::File::ID3v2;
187
188
                        }
188
189
                        if (saveMask != 0) {
189
190
                                if (mpegFile->save(saveMask, false)) {
190
191
                                        fileChanged = true;
191
192
                                        if (saveMask & TagLib::MPEG::File::ID3v1) {
192
 
                                                changedV1 = false;
 
193
                                                markTag1Changed(false);
193
194
                                        }
194
195
                                        if (saveMask & TagLib::MPEG::File::ID3v2) {
195
 
                                                changedV2 = false;
 
196
                                                markTag2Changed(false);
196
197
                                        }
197
198
                                }
198
199
                        }
199
200
                } else {
200
 
                        if ((m_tagV2 && (force || changedV2)) ||
201
 
                                        (m_tagV1 && (force || changedV1))) {
 
201
                        if ((m_tagV2 && (force || isTag2Changed())) ||
 
202
                                        (m_tagV1 && (force || isTag1Changed()))) {
202
203
                                TagLib::MPC::File* mpcFile = dynamic_cast<TagLib::MPC::File*>(file);
203
204
#ifndef MPC_ID3V1
204
205
                                // it does not work if there is also an ID3 tag (bug in TagLib?)
209
210
#endif
210
211
                                if (m_fileRef.save()) {
211
212
                                        fileChanged = true;
212
 
                                        changedV1 = false;
213
 
                                        changedV2 = false;
 
213
                                        markTag1Changed(false);
 
214
                                        markTag2Changed(false);
214
215
                                }
215
216
                        }
216
217
                }
222
223
        // file reference is assigned, later readTags() is called.
223
224
        // If the file is not properly closed, doubled tags can be
224
225
        // written if the file is finally closed!
225
 
        // This can be reproduced with an untag MP3 file, then add
 
226
        // This can be reproduced with an untagged MP3 file, then add
226
227
        // an ID3v2 title, save, add an ID3v2 artist, save, reload
227
 
        // => double ID3v2 tags. 
228
 
        if (fileChanged) {
 
228
        // => double ID3v2 tags.
 
229
        // On Windows it is necessary to close the file before renaming it,
 
230
        // so it is done even if the file is not changed.
 
231
#ifndef WIN32
 
232
        if (fileChanged)
 
233
#endif
229
234
                m_fileRef = TagLib::FileRef();
230
 
        }
231
235
 
232
236
        // restore time stamp
233
237
        if (setUtime) {
234
238
                ::utime(fn, &times);
235
239
        }
236
240
 
237
 
        if (new_filename != filename) {
238
 
                if (!renameFile(filename, new_filename)) {
 
241
        if (getFilename() != currentFilename()) {
 
242
                if (!renameFile(currentFilename(), getFilename())) {
239
243
                        return false;
240
244
                }
 
245
                updateCurrentFilename();
241
246
                *renamed = true;
242
247
        }
243
248
 
244
 
        if (fileChanged) {
 
249
#ifndef WIN32
 
250
        if (fileChanged)
 
251
#endif
245
252
                readTags(true);
246
 
        }
247
253
        return true;
248
254
}
249
255
 
277
283
                                                 it != frameList.end();) {
278
284
                                        id3v2Tag->removeFrame(*it++, true);
279
285
                                }
280
 
                                changedV2 = true;
 
286
                                markTag2Changed();
281
287
                        } else if ((oggTag = dynamic_cast<TagLib::Ogg::XiphComment*>(m_tagV2)) !=
282
288
                                                                 0) {
283
289
                                const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap();
285
291
                                                 it != fieldListMap.end();) {
286
292
                                        oggTag->removeField((*it++).first);
287
293
                                }
288
 
                                changedV2 = true;
 
294
                                markTag2Changed();
289
295
                        } else if ((apeTag = dynamic_cast<TagLib::APE::Tag*>(m_tagV2)) != 0) {
290
296
                                const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap();
291
297
                                for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin();
292
298
                                                 it != itemListMap.end();) {
293
299
                                        apeTag->removeItem((*it++).first);
294
300
                                }
295
 
                                changedV2 = true;
 
301
                                markTag2Changed();
296
302
                        } else {
297
303
                                removeStandardTagsV2(flt);
298
304
                        }
405
411
/**
406
412
 * Get ID3v1 genre.
407
413
 *
408
 
 * @return number,
409
 
 *         0xff if the field does not exist,
410
 
 *         -1 if the tags do not exist.
 
414
 * @return string,
 
415
 *         "" if the field does not exist,
 
416
 *         QString::null if the tags do not exist.
411
417
 */
412
 
int TagLibFile::getGenreNumV1()
 
418
QString TagLibFile::getGenreV1()
413
419
{
414
420
        if (m_tagV1) {
415
421
                TagLib::String str = m_tagV1->genre();
416
 
                if (!str.isNull()) {
417
 
                        return Genres::getNumber(TStringToQString(str));
418
 
                } else {
419
 
                        return 0xff;
420
 
                }
 
422
                return str.isNull() ? QString("") : TStringToQString(str);
421
423
        } else {
422
 
                return -1;
 
424
                return QString::null;
423
425
        }
424
426
}
425
427
 
524
526
}
525
527
 
526
528
/**
527
 
 * Get ID3v2 genre.
528
 
 *
529
 
 * @return number,
530
 
 *         0xff if the field does not exist,
531
 
 *         -1 if the tags do not exist.
 
529
 * Get a genre string from a string which can contain the genre itself,
 
530
 * or only the genre number or the genre number in parenthesis.
 
531
 *
 
532
 * @param str genre string
 
533
 *
 
534
 * @return genre.
532
535
 */
533
 
int TagLibFile::getGenreNumV2()
 
536
static QString getGenreString(const TagLib::String& str)
534
537
{
535
 
        if (m_tagV2) {
536
 
                TagLib::String str = m_tagV2->genre();
537
 
                if (!str.isNull()) {
538
 
                        QString qs = TStringToQString(str);
539
 
                        int cpPos = 0, n = 0xff;
540
 
                        bool ok = false;
541
 
                        if (qs[0] == '(' && (cpPos = qs.find(')', 2)) > 1) {
542
 
                                n = qs.mid(1, cpPos - 1).toInt(&ok);
543
 
                                if (!ok || n > 0xff) {
544
 
                                        n = 0xff;
545
 
                                }
546
 
                                return n;
547
 
                        } else if ((n = qs.toInt(&ok)) >= 0 && n <= 0xff && ok) {
548
 
                                return n;
549
 
                        } else {
550
 
                                return Genres::getNumber(qs);
 
538
        if (!str.isNull()) {
 
539
                QString qs = TStringToQString(str);
 
540
                int cpPos = 0, n = 0xff;
 
541
                bool ok = false;
 
542
                if (qs[0] == '(' && (cpPos = qs.find(')', 2)) > 1) {
 
543
                        n = qs.mid(1, cpPos - 1).toInt(&ok);
 
544
                        if (!ok || n > 0xff) {
 
545
                                n = 0xff;
551
546
                        }
 
547
                        return Genres::getName(n);
 
548
                } else if ((n = qs.toInt(&ok)) >= 0 && n <= 0xff && ok) {
 
549
                        return Genres::getName(n);
552
550
                } else {
553
 
                        return 0xff;
 
551
                        return qs;
554
552
                }
555
553
        } else {
556
 
                return -1;
 
554
                return "";
557
555
        }
558
556
}
559
557
 
567
565
QString TagLibFile::getGenreV2()
568
566
{
569
567
        if (m_tagV2) {
570
 
                TagLib::String str = m_tagV2->genre();
571
 
                return str.isNull() ? QString("") : TStringToQString(str);
 
568
                return getGenreString(m_tagV2->genre());
572
569
        } else {
573
570
                return QString::null;
574
571
        }
639
636
                TagLib::String tstr = str.isEmpty() ?
640
637
                        TagLib::String::null : QStringToTString(str);
641
638
                if (!(tstr == m_tagV1->title())) {
642
 
                        m_tagV1->setTitle(tstr);
643
 
                        changedV1 = true;
 
639
                        QString s = checkTruncation(str, StandardTags::TF_Title);
 
640
                        if (!s.isNull())
 
641
                                m_tagV1->setTitle(QStringToTString(s));
 
642
                        else
 
643
                                m_tagV1->setTitle(tstr);
 
644
                        markTag1Changed();
644
645
                }
645
646
        }
646
647
}
656
657
                TagLib::String tstr = str.isEmpty() ?
657
658
                        TagLib::String::null : QStringToTString(str);
658
659
                if (!(tstr == m_tagV1->artist())) {
659
 
                        m_tagV1->setArtist(tstr);
660
 
                        changedV1 = true;
 
660
                        QString s = checkTruncation(str, StandardTags::TF_Artist);
 
661
                        if (!s.isNull())
 
662
                                m_tagV1->setArtist(QStringToTString(s));
 
663
                        else
 
664
                                m_tagV1->setArtist(tstr);
 
665
                        markTag1Changed();
661
666
                }
662
667
        }
663
668
}
673
678
                TagLib::String tstr = str.isEmpty() ?
674
679
                        TagLib::String::null : QStringToTString(str);
675
680
                if (!(tstr == m_tagV1->album())) {
676
 
                        m_tagV1->setAlbum(tstr);
677
 
                        changedV1 = true;
 
681
                        QString s = checkTruncation(str, StandardTags::TF_Album);
 
682
                        if (!s.isNull())
 
683
                                m_tagV1->setAlbum(QStringToTString(s));
 
684
                        else
 
685
                                m_tagV1->setAlbum(tstr);
 
686
                        markTag1Changed();
678
687
                }
679
688
        }
680
689
}
690
699
                TagLib::String tstr = str.isEmpty() ?
691
700
                        TagLib::String::null : QStringToTString(str);
692
701
                if (!(tstr == m_tagV1->comment())) {
693
 
                        m_tagV1->setComment(tstr);
694
 
                        changedV1 = true;
 
702
                        QString s = checkTruncation(str, StandardTags::TF_Comment, 28);
 
703
                        if (!s.isNull())
 
704
                                m_tagV1->setComment(QStringToTString(s));
 
705
                        else
 
706
                                m_tagV1->setComment(tstr);
 
707
                        markTag1Changed();
695
708
                }
696
709
        }
697
710
}
706
719
        if (makeTagV1Settable() && num >= 0) {
707
720
                if (num != static_cast<int>(m_tagV1->year())) {
708
721
                        m_tagV1->setYear(num);
709
 
                        changedV1 = true;
 
722
                        markTag1Changed();
710
723
                }
711
724
        }
712
725
}
720
733
{
721
734
        if (makeTagV1Settable() && num >= 0) {
722
735
                if (num != static_cast<int>(m_tagV1->track())) {
723
 
                        m_tagV1->setTrack(num);
724
 
                        changedV1 = true;
 
736
                        int n = checkTruncation(num, StandardTags::TF_Track);
 
737
                        if (n != -1)
 
738
                                m_tagV1->setTrack(n);
 
739
                        else
 
740
                                m_tagV1->setTrack(num);
 
741
                        markTag1Changed();
725
742
                }
726
743
        }
727
744
}
728
745
 
729
746
/**
730
 
 * Set ID3v1 genre.
 
747
 * Set ID3v1 genre as text.
731
748
 *
732
 
 * @param num number to set, 0xff to remove field.
 
749
 * @param str string to set, "" to remove field, QString::null to ignore.
733
750
 */
734
 
void TagLibFile::setGenreNumV1(int num)
 
751
void TagLibFile::setGenreV1(const QString& str)
735
752
{
736
 
        if (makeTagV1Settable() && num >= 0) {
737
 
                const char* str = Genres::getName(num);
738
 
                TagLib::String tstr = str && *str ?
739
 
                        TagLib::String(str) : TagLib::String::null;
 
753
        if (makeTagV1Settable() && !str.isNull()) {
 
754
                TagLib::String tstr = str.isEmpty() ?
 
755
                        TagLib::String::null : QStringToTString(str);
740
756
                if (!(tstr == m_tagV1->genre())) {
741
757
                        m_tagV1->setGenre(tstr);
742
 
                        changedV1 = true;
 
758
                        markTag1Changed();
743
759
                }
 
760
                // if the string cannot be converted to a number, set the truncation flag
 
761
                checkTruncation(!str.isEmpty() && Genres::getNumber(str) == 0xff ? 1 : 0,
 
762
                                                                                StandardTags::TF_Genre, 0);
744
763
        }
745
764
}
746
765
 
749
768
 *
750
769
 * @param tag     tag
751
770
 * @param qstr    text as QString
752
 
 * @param tst     text as TagLib::String
 
771
 * @param tstr    text as TagLib::String
753
772
 * @param frameId ID3v2 frame ID
754
773
 *
755
774
 * @return true if an ID3v2 Unicode field was written.
814
833
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "TIT2")) {
815
834
                                m_tagV2->setTitle(tstr);
816
835
                        }
817
 
                        changedV2 = true;
 
836
                        markTag2Changed();
818
837
                }
819
838
        }
820
839
}
833
852
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "TPE1")) {
834
853
                        }
835
854
                        m_tagV2->setArtist(tstr);
836
 
                        changedV2 = true;
 
855
                        markTag2Changed();
837
856
                }
838
857
        }
839
858
}
852
871
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "TALB")) {
853
872
                        }
854
873
                        m_tagV2->setAlbum(tstr);
855
 
                        changedV2 = true;
 
874
                        markTag2Changed();
856
875
                }
857
876
        }
858
877
}
871
890
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "COMM")) {
872
891
                        }
873
892
                        m_tagV2->setComment(tstr);
874
 
                        changedV2 = true;
 
893
                        markTag2Changed();
875
894
                }
876
895
        }
877
896
}
886
905
        if (makeTagV2Settable() && num >= 0) {
887
906
                if (num != static_cast<int>(m_tagV2->year())) {
888
907
                        m_tagV2->setYear(num);
889
 
                        changedV2 = true;
 
908
                        markTag2Changed();
890
909
                }
891
910
        }
892
911
}
927
946
                        } else {
928
947
                                m_tagV2->setTrack(num);
929
948
                        }
930
 
                        changedV2 = true;
931
 
                }
932
 
        }
933
 
}
934
 
 
935
 
/**
936
 
 * Set ID3v2 genre.
937
 
 *
938
 
 * @param num number to set, 0xff to remove field.
939
 
 */
940
 
void TagLibFile::setGenreNumV2(int num)
941
 
{
942
 
        if (makeTagV2Settable() && num >= 0) {
943
 
                const char* str = Genres::getName(num);
944
 
                TagLib::String tstr = str && *str ?
945
 
                        TagLib::String(str) : TagLib::String::null;
946
 
                if (!(tstr == m_tagV2->genre())) {
947
 
                        m_tagV2->setGenre(tstr);
948
 
                        changedV2 = true;
 
949
                        markTag2Changed();
949
950
                }
950
951
        }
951
952
}
962
963
                        TagLib::String::null : QStringToTString(str);
963
964
                if (!(tstr == m_tagV2->genre())) {
964
965
                        m_tagV2->setGenre(tstr);
965
 
                        changedV2 = true;
 
966
                        markTag2Changed();
966
967
                }
967
968
        }
968
969
}