File indexing completed on 2021-12-21 13:27:54

0001 /**
0002  * Copyright (C) 2002-2004 Scott Wheeler <wheeler@kde.org>
0003  * Copyright (C) 2009 Michael Pyne <mpyne@kde.org>
0004  *
0005  * This program is free software; you can redistribute it and/or modify it under
0006  * the terms of the GNU General Public License as published by the Free Software
0007  * Foundation; either version 2 of the License, or (at your option) any later
0008  * version.
0009  *
0010  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0011  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0012  * PARTICULAR PURPOSE. See the GNU General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU General Public License along with
0015  * this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 #include "juktag.h"
0019 
0020 #include <KLocalizedString>
0021 
0022 #include <QFile>
0023 
0024 #include <tag.h> //taglib
0025 #include <tfile.h>
0026 #include <audioproperties.h>
0027 #include <id3v2framefactory.h>
0028 
0029 #include "cache.h"
0030 #include "mediafiles.h"
0031 #include "stringshare.h"
0032 #include "juk_debug.h"
0033 
0034 ////////////////////////////////////////////////////////////////////////////////
0035 // public members
0036 ////////////////////////////////////////////////////////////////////////////////
0037 
0038 
0039 Tag::Tag(const QString &fileName) :
0040     m_fileName(fileName),
0041     m_track(0),
0042     m_year(0),
0043     m_seconds(0),
0044     m_bitrate(0),
0045     m_isValid(false)
0046 {
0047     if(fileName.isEmpty()) {
0048         qCCritical(JUK_LOG) << "Trying to add empty file";
0049         return;
0050     }
0051 
0052     TagLib::File *file = MediaFiles::fileFactoryByType(fileName);
0053     if(file && file->isValid()) {
0054         setup(file);
0055         delete file;
0056     }
0057     else {
0058         qCCritical(JUK_LOG) << "Couldn't resolve the mime type of \"" <<
0059             fileName << "\" -- this shouldn't happen.";
0060     }
0061 }
0062 
0063 bool Tag::save() const
0064 {
0065     bool result;
0066     TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);
0067     TagLib::File *file = MediaFiles::fileFactoryByType(m_fileName);
0068 
0069     if(file && !file->readOnly() && file->isValid() && file->tag()) {
0070         file->tag()->setTitle(TagLib::String(m_title.toUtf8().constData(), TagLib::String::UTF8));
0071         file->tag()->setArtist(TagLib::String(m_artist.toUtf8().constData(), TagLib::String::UTF8));
0072         file->tag()->setAlbum(TagLib::String(m_album.toUtf8().constData(), TagLib::String::UTF8));
0073         file->tag()->setGenre(TagLib::String(m_genre.toUtf8().constData(), TagLib::String::UTF8));
0074         file->tag()->setComment(TagLib::String(m_comment.toUtf8().constData(), TagLib::String::UTF8));
0075         file->tag()->setTrack(m_track);
0076         file->tag()->setYear(m_year);
0077         result = file->save();
0078     }
0079     else {
0080         qCCritical(JUK_LOG) << "Couldn't save file.";
0081         result = false;
0082     }
0083 
0084     delete file;
0085     return result;
0086 }
0087 
0088 QString Tag::playingString() const
0089 {
0090     QString str;
0091     if(artist().isEmpty())
0092         str = title();
0093     else {
0094         str = i18nc("a playing track, %1 is artist, %2 is song title",
0095                     "%1 - <i>%2</i>", artist(), title());
0096     }
0097 
0098     return str;
0099 }
0100 
0101 CacheDataStream &Tag::read(CacheDataStream &s)
0102 {
0103     switch(s.cacheVersion()) {
0104     case 1: {
0105         qint32 track;
0106         qint32 year;
0107         qint32 bitrate;
0108         qint32 seconds;
0109 
0110         s >> m_title
0111           >> m_artist
0112           >> m_album
0113           >> m_genre
0114           >> track
0115           >> year
0116           >> m_comment
0117           >> bitrate
0118           >> m_lengthString
0119           >> seconds;
0120 
0121         m_track = track;
0122         m_year = year;
0123         m_bitrate = bitrate;
0124         m_seconds = seconds;
0125         break;
0126     }
0127     default: {
0128         static QString dummyString;
0129         static int dummyInt;
0130         QString bitrateString;
0131 
0132         s >> dummyInt
0133           >> m_title
0134           >> m_artist
0135           >> m_album
0136           >> m_genre
0137           >> dummyInt
0138           >> m_track
0139           >> dummyString
0140           >> m_year
0141           >> dummyString
0142           >> m_comment
0143           >> bitrateString
0144           >> m_lengthString
0145           >> m_seconds
0146           >> dummyString;
0147 
0148         bool ok;
0149         m_bitrate = bitrateString.toInt(&ok);
0150         if(!ok)
0151             m_bitrate = 0;
0152         break;
0153     }
0154     }
0155 
0156     minimizeMemoryUsage();
0157     return s;
0158 }
0159 
0160 ////////////////////////////////////////////////////////////////////////////////
0161 // private methods
0162 ////////////////////////////////////////////////////////////////////////////////
0163 
0164 Tag::Tag(const QString &fileName, bool) :
0165     m_fileName(fileName),
0166     m_track(0),
0167     m_year(0),
0168     m_seconds(0),
0169     m_bitrate(0),
0170     m_isValid(true)
0171 {
0172 
0173 }
0174 
0175 void Tag::setup(TagLib::File *file)
0176 {
0177     if(!file || !file->tag()) {
0178         qCWarning(JUK_LOG) << "Can't setup invalid file" << m_fileName;
0179         return;
0180     }
0181 
0182     m_title   = TStringToQString(file->tag()->title()).trimmed();
0183     m_artist  = TStringToQString(file->tag()->artist()).trimmed();
0184     m_album   = TStringToQString(file->tag()->album()).trimmed();
0185     m_genre   = TStringToQString(file->tag()->genre()).trimmed();
0186     m_comment = TStringToQString(file->tag()->comment()).trimmed();
0187 
0188     m_track = file->tag()->track();
0189     m_year  = file->tag()->year();
0190 
0191     m_seconds = file->audioProperties()->length();
0192     m_bitrate = file->audioProperties()->bitrate();
0193 
0194     const int seconds = m_seconds % 60;
0195     const int minutes = (m_seconds - seconds) / 60;
0196 
0197     m_lengthString = QString::number(minutes) + (seconds >= 10 ? ":" : ":0") + QString::number(seconds);
0198 
0199     if(m_title.isEmpty()) {
0200         int i = m_fileName.lastIndexOf('/');
0201         int j = m_fileName.lastIndexOf('.');
0202         m_title = i > 0 ? m_fileName.mid(i + 1, j - i - 1) : m_fileName;
0203     }
0204 
0205     minimizeMemoryUsage();
0206     m_isValid = true;
0207 }
0208 
0209 void Tag::minimizeMemoryUsage()
0210 {
0211     // Try to reduce memory usage: share tags that frequently repeat, squeeze others
0212 
0213     m_title.squeeze();
0214     m_lengthString.squeeze();
0215 
0216     m_comment = StringShare::tryShare(m_comment);
0217     m_artist  = StringShare::tryShare(m_artist);
0218     m_album   = StringShare::tryShare(m_album);
0219     m_genre   = StringShare::tryShare(m_genre);
0220 }
0221 
0222 ////////////////////////////////////////////////////////////////////////////////
0223 // related functions
0224 ////////////////////////////////////////////////////////////////////////////////
0225 
0226 QDataStream &operator<<(QDataStream &s, const Tag &t)
0227 {
0228     s << t.title()
0229       << t.artist()
0230       << t.album()
0231       << t.genre()
0232       << qint32(t.track())
0233       << qint32(t.year())
0234       << t.comment()
0235       << qint32(t.bitrate())
0236       << t.lengthString()
0237       << qint32(t.seconds());
0238 
0239     return s;
0240 }
0241 
0242 CacheDataStream &operator>>(CacheDataStream &s, Tag &t)
0243 {
0244     return t.read(s);
0245 }
0246 
0247 // vim: set et sw=4 tw=0 sta: