File indexing completed on 2023-05-30 11:30:47

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 <tag.h> //taglib
0023 #include <tfile.h>
0024 #include <audioproperties.h>
0025 #include <id3v2framefactory.h>
0026 
0027 #include "cache.h"
0028 #include "mediafiles.h"
0029 #include "stringshare.h"
0030 #include "juk_debug.h"
0031 
0032 ////////////////////////////////////////////////////////////////////////////////
0033 // public members
0034 ////////////////////////////////////////////////////////////////////////////////
0035 
0036 
0037 Tag::Tag(const QString &fileName) :
0038     m_fileName(fileName),
0039     m_track(0),
0040     m_year(0),
0041     m_seconds(0),
0042     m_bitrate(0),
0043     m_isValid(false)
0044 {
0045     if(fileName.isEmpty()) {
0046         qCCritical(JUK_LOG) << "Trying to add empty file";
0047         return;
0048     }
0049 
0050     TagLib::File *file = MediaFiles::fileFactoryByType(fileName);
0051     if(file && file->isValid()) {
0052         setup(file);
0053         delete file;
0054     }
0055     else {
0056         qCCritical(JUK_LOG) << "Couldn't resolve the mime type of \"" <<
0057             fileName << "\" -- this shouldn't happen.";
0058     }
0059 }
0060 
0061 bool Tag::save() const
0062 {
0063     bool result;
0064     TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);
0065     TagLib::File *file = MediaFiles::fileFactoryByType(m_fileName);
0066 
0067     if(file && !file->readOnly() && file->isValid() && file->tag()) {
0068         file->tag()->setTitle(TagLib::String(m_title.toUtf8().constData(), TagLib::String::UTF8));
0069         file->tag()->setArtist(TagLib::String(m_artist.toUtf8().constData(), TagLib::String::UTF8));
0070         file->tag()->setAlbum(TagLib::String(m_album.toUtf8().constData(), TagLib::String::UTF8));
0071         file->tag()->setGenre(TagLib::String(m_genre.toUtf8().constData(), TagLib::String::UTF8));
0072         file->tag()->setComment(TagLib::String(m_comment.toUtf8().constData(), TagLib::String::UTF8));
0073         file->tag()->setTrack(m_track);
0074         file->tag()->setYear(m_year);
0075         result = file->save();
0076     }
0077     else {
0078         qCCritical(JUK_LOG) << "Couldn't save file.";
0079         result = false;
0080     }
0081 
0082     delete file;
0083     return result;
0084 }
0085 
0086 QString Tag::playingString() const
0087 {
0088     QString str;
0089     if(artist().isEmpty())
0090         str = title();
0091     else {
0092         str = i18nc("a playing track, %1 is artist, %2 is song title",
0093                     "%1 - <i>%2</i>", artist(), title());
0094     }
0095 
0096     return str;
0097 }
0098 
0099 CacheDataStream &Tag::read(CacheDataStream &s)
0100 {
0101     switch(s.cacheVersion()) {
0102     case 1: {
0103         qint32 track;
0104         qint32 year;
0105         qint32 bitrate;
0106         qint32 seconds;
0107 
0108         s >> m_title
0109           >> m_artist
0110           >> m_album
0111           >> m_genre
0112           >> track
0113           >> year
0114           >> m_comment
0115           >> bitrate
0116           >> m_lengthString
0117           >> seconds;
0118 
0119         m_track = track;
0120         m_year = year;
0121         m_bitrate = bitrate;
0122         m_seconds = seconds;
0123         break;
0124     }
0125     default: {
0126         static QString dummyString;
0127         static int dummyInt;
0128         QString bitrateString;
0129 
0130         s >> dummyInt
0131           >> m_title
0132           >> m_artist
0133           >> m_album
0134           >> m_genre
0135           >> dummyInt
0136           >> m_track
0137           >> dummyString
0138           >> m_year
0139           >> dummyString
0140           >> m_comment
0141           >> bitrateString
0142           >> m_lengthString
0143           >> m_seconds
0144           >> dummyString;
0145 
0146         bool ok;
0147         m_bitrate = bitrateString.toInt(&ok);
0148         if(!ok)
0149             m_bitrate = 0;
0150         break;
0151     }
0152     }
0153 
0154     minimizeMemoryUsage();
0155     return s;
0156 }
0157 
0158 ////////////////////////////////////////////////////////////////////////////////
0159 // private methods
0160 ////////////////////////////////////////////////////////////////////////////////
0161 
0162 Tag::Tag(const QString &fileName, bool) :
0163     m_fileName(fileName),
0164     m_track(0),
0165     m_year(0),
0166     m_seconds(0),
0167     m_bitrate(0),
0168     m_isValid(true)
0169 {
0170 
0171 }
0172 
0173 void Tag::setup(TagLib::File *file)
0174 {
0175     if(!file || !file->tag()) {
0176         qCWarning(JUK_LOG) << "Can't setup invalid file" << m_fileName;
0177         return;
0178     }
0179 
0180     m_title   = TStringToQString(file->tag()->title()).trimmed();
0181     m_artist  = TStringToQString(file->tag()->artist()).trimmed();
0182     m_album   = TStringToQString(file->tag()->album()).trimmed();
0183     m_genre   = TStringToQString(file->tag()->genre()).trimmed();
0184     m_comment = TStringToQString(file->tag()->comment()).trimmed();
0185 
0186     m_track = file->tag()->track();
0187     m_year  = file->tag()->year();
0188 
0189     m_seconds = file->audioProperties()->length();
0190     m_bitrate = file->audioProperties()->bitrate();
0191 
0192     const int seconds = m_seconds % 60;
0193     const int minutes = (m_seconds - seconds) / 60;
0194 
0195     m_lengthString = QString::number(minutes) + (seconds >= 10 ? ":" : ":0") + QString::number(seconds);
0196 
0197     if(m_title.isEmpty()) {
0198         int i = m_fileName.lastIndexOf('/');
0199         int j = m_fileName.lastIndexOf('.');
0200         m_title = i > 0 ? m_fileName.mid(i + 1, j - i - 1) : m_fileName;
0201     }
0202 
0203     minimizeMemoryUsage();
0204     m_isValid = true;
0205 }
0206 
0207 void Tag::minimizeMemoryUsage()
0208 {
0209     // Try to reduce memory usage: share tags that frequently repeat, squeeze others
0210 
0211     m_title.squeeze();
0212     m_lengthString.squeeze();
0213 
0214     m_comment = StringShare::tryShare(m_comment);
0215     m_artist  = StringShare::tryShare(m_artist);
0216     m_album   = StringShare::tryShare(m_album);
0217     m_genre   = StringShare::tryShare(m_genre);
0218 }
0219 
0220 ////////////////////////////////////////////////////////////////////////////////
0221 // related functions
0222 ////////////////////////////////////////////////////////////////////////////////
0223 
0224 QDataStream &operator<<(QDataStream &s, const Tag &t)
0225 {
0226     s << t.title()
0227       << t.artist()
0228       << t.album()
0229       << t.genre()
0230       << qint32(t.track())
0231       << qint32(t.year())
0232       << t.comment()
0233       << qint32(t.bitrate())
0234       << t.lengthString()
0235       << qint32(t.seconds());
0236 
0237     return s;
0238 }
0239 
0240 CacheDataStream &operator>>(CacheDataStream &s, Tag &t)
0241 {
0242     return t.read(s);
0243 }
0244 
0245 // vim: set et sw=4 tw=0 sta: