Fix bug 134312 (empty flac file crashes JuK) in KDE 4.
[kdemultimedia.git] / juk / tag.cpp
blob7b3483b807e337c0cc7edd7f817f57aad278177f
1 /***************************************************************************
2 begin : Sun Feb 17 2002
3 copyright : (C) 2002 - 2004 by Scott Wheeler
4 email : wheeler@kde.org
5 ***************************************************************************/
7 /***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
16 #include "tag.h"
18 #include <kdebug.h>
20 #include <QRegExp>
21 #include <QFile>
23 #include <taglib/tag.h>
24 #include <mpegfile.h>
25 #include <vorbisfile.h>
26 #include <flacfile.h>
27 #include <xiphcomment.h>
28 #include <id3v2framefactory.h>
30 #if (TAGLIB_MAJOR_VERSION > 1) || \
31 ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 2))
32 #include <oggflacfile.h>
33 #define TAGLIB_1_2
34 #endif
35 #if (TAGLIB_MAJOR_VERSION > 1) || \
36 ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 3))
37 #include <mpcfile.h>
38 #define TAGLIB_1_3
39 #endif
41 #include "cache.h"
42 #include "mediafiles.h"
43 #include "stringshare.h"
45 ////////////////////////////////////////////////////////////////////////////////
46 // public members
47 ////////////////////////////////////////////////////////////////////////////////
50 Tag::Tag(const QString &fileName) :
51 m_fileName(fileName),
52 m_track(0),
53 m_year(0),
54 m_seconds(0),
55 m_bitrate(0),
56 m_isValid(false)
58 // using qDebug here since we want this to show up in non-debug builds as well
60 qDebug("Reading tag for %s", fileName.toLocal8Bit().data());
62 if(MediaFiles::isMP3(fileName)) {
63 TagLib::MPEG::File file(QFile::encodeName(fileName).data());
64 if(file.isValid())
65 setup(&file);
68 else if(MediaFiles::isFLAC(fileName)) {
69 TagLib::FLAC::File file(QFile::encodeName(fileName).data());
70 if(file.isValid())
71 setup(&file);
73 #ifdef TAGLIB_1_3
74 else if(MediaFiles::isMPC(fileName)) {
75 kDebug(65432) << "Trying to resolve Musepack file" << endl;
76 TagLib::MPC::File file(QFile::encodeName(fileName).data());
77 if(file.isValid())
78 setup(&file);
80 #endif
81 #ifdef TAGLIB_1_2
82 else if(MediaFiles::isOggFLAC(fileName)) {
83 TagLib::Ogg::FLAC::File file(QFile::encodeName(fileName).data());
84 if(file.isValid())
85 setup(&file);
87 #endif
88 else if(MediaFiles::isVorbis(fileName)) {
89 TagLib::Vorbis::File file(QFile::encodeName(fileName).data());
90 if(file.isValid())
91 setup(&file);
94 else {
95 kError(65432) << "Couldn't resolve the mime type of \"" <<
96 fileName << "\" -- this shouldn't happen." << endl;
100 bool Tag::save()
102 bool result;
103 TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);
105 TagLib::File *file = 0;
107 if(MediaFiles::isMP3(m_fileName))
108 file = new TagLib::MPEG::File(QFile::encodeName(m_fileName).data());
109 else if(MediaFiles::isFLAC(m_fileName))
110 file = new TagLib::FLAC::File(QFile::encodeName(m_fileName).data());
111 #ifdef TAGLIB_1_3
112 else if(MediaFiles::isMPC(m_fileName))
113 file = new TagLib::MPC::File(QFile::encodeName(m_fileName).data());
114 #endif
115 #ifdef TAGLIB_1_2
116 else if(MediaFiles::isOggFLAC(m_fileName))
117 file = new TagLib::Ogg::FLAC::File(QFile::encodeName(m_fileName).data());
118 #endif
119 else if(MediaFiles::isVorbis(m_fileName))
120 file = new TagLib::Vorbis::File(QFile::encodeName(m_fileName).data());
122 if(file && file->isValid() && file->tag() && !file->readOnly()) {
123 file->tag()->setTitle(TagLib::String(m_title.toUtf8().data(), TagLib::String::UTF8));
124 file->tag()->setArtist(TagLib::String(m_artist.toUtf8().data(), TagLib::String::UTF8));
125 file->tag()->setAlbum(TagLib::String(m_album.toUtf8().data(), TagLib::String::UTF8));
126 file->tag()->setGenre(TagLib::String(m_genre.toUtf8().data(), TagLib::String::UTF8));
127 file->tag()->setComment(TagLib::String(m_comment.toUtf8().data(), TagLib::String::UTF8));
128 file->tag()->setTrack(m_track);
129 file->tag()->setYear(m_year);
130 #ifdef TAGLIB_1_2
131 result = file->save();
132 #else
133 file->save();
134 result = true;
135 #endif
137 else {
138 kError(65432) << "Couldn't save file." << endl;
139 result = false;
142 delete file;
143 return result;
146 CacheDataStream &Tag::read(CacheDataStream &s)
148 switch(s.cacheVersion()) {
149 case 1: {
150 qint32 track;
151 qint32 year;
152 qint32 bitrate;
153 qint32 seconds;
155 s >> m_title
156 >> m_artist
157 >> m_album
158 >> m_genre
159 >> track
160 >> year
161 >> m_comment
162 >> bitrate
163 >> m_lengthString
164 >> seconds;
166 m_track = track;
167 m_year = year;
168 m_bitrate = bitrate;
169 m_seconds = seconds;
170 break;
172 default: {
173 static QString dummyString;
174 static int dummyInt;
175 QString bitrateString;
177 s >> dummyInt
178 >> m_title
179 >> m_artist
180 >> m_album
181 >> m_genre
182 >> dummyInt
183 >> m_track
184 >> dummyString
185 >> m_year
186 >> dummyString
187 >> m_comment
188 >> bitrateString
189 >> m_lengthString
190 >> m_seconds
191 >> dummyString;
193 bool ok;
194 m_bitrate = bitrateString.toInt(&ok);
195 if(!ok)
196 m_bitrate = 0;
197 break;
201 // Try to reduce memory usage: share tags that frequently repeat, squeeze others
203 m_title.squeeze();
204 m_lengthString.squeeze();
206 m_comment = StringShare::tryShare(m_comment);
207 m_artist = StringShare::tryShare(m_artist);
208 m_album = StringShare::tryShare(m_album);
209 m_genre = StringShare::tryShare(m_genre);
211 return s;
214 ////////////////////////////////////////////////////////////////////////////////
215 // private methods
216 ////////////////////////////////////////////////////////////////////////////////
218 Tag::Tag(const QString &fileName, bool) :
219 m_fileName(fileName),
220 m_track(0),
221 m_year(0),
222 m_seconds(0),
223 m_bitrate(0),
224 m_isValid(true)
229 void Tag::setup(TagLib::File *file)
231 m_title = TStringToQString(file->tag()->title()).trimmed();
232 m_artist = TStringToQString(file->tag()->artist()).trimmed();
233 m_album = TStringToQString(file->tag()->album()).trimmed();
234 m_genre = TStringToQString(file->tag()->genre()).trimmed();
235 m_comment = TStringToQString(file->tag()->comment()).trimmed();
237 m_track = file->tag()->track();
238 m_year = file->tag()->year();
240 m_seconds = file->audioProperties()->length();
241 m_bitrate = file->audioProperties()->bitrate();
243 const int seconds = m_seconds % 60;
244 const int minutes = (m_seconds - seconds) / 60;
246 m_lengthString = QString::number(minutes) + (seconds >= 10 ? ":" : ":0") + QString::number(seconds);
248 if(m_title.isEmpty()) {
249 int i = m_fileName.lastIndexOf('/');
250 int j = m_fileName.lastIndexOf('.');
251 m_title = i > 0 ? m_fileName.mid(i + 1, j - i - 1) : m_fileName;
254 m_isValid = true;
257 ////////////////////////////////////////////////////////////////////////////////
258 // related functions
259 ////////////////////////////////////////////////////////////////////////////////
261 QDataStream &operator<<(QDataStream &s, const Tag &t)
263 s << t.title()
264 << t.artist()
265 << t.album()
266 << t.genre()
267 << qint32(t.track())
268 << qint32(t.year())
269 << t.comment()
270 << qint32(t.bitrate())
271 << t.lengthString()
272 << qint32(t.seconds());
274 return s;
277 CacheDataStream &operator>>(CacheDataStream &s, Tag &t)
279 return t.read(s);
282 // vim: set et sw=4 tw=0 sta: