defuze.me  Client
audiotrack.cpp
00001 #include "library/audiotrack.hpp"
00002 #include "parser.hpp"
00003 #include "exception.hpp"
00004 #include <QSqlQuery>
00005 #include <QFile>
00006 #include <QDebug>
00007 #include <QSqlError>
00008 #include <QFileInfo>
00009 #include <QMutexLocker>
00010 
00011 // TagLib
00012 #include <audioproperties.h>
00013 #include <mpegfile.h>
00014 #include <fileref.h>
00015 #include <id3v2tag.h>
00016 #include <id3v2frame.h>
00017 #include <id3v2header.h>
00018 #include <id3v1tag.h>
00019 #include <apetag.h>
00020 #include <attachedpictureframe.h>
00021 #include <unistd.h>
00022 
00023 using namespace Library;
00024 
00025 QMap<int, AudioTrack*>          AudioTrack::tracksMap;
00026 QMap<int, AudioTrackLoader*>    AudioTrack::trackLoaders;
00027 QImage                          AudioTrack::defaultImage;
00028 QImage                          AudioTrack::artistImage;
00029 QMutex                          AudioTrack::taglibMutex;
00030 QMutex                          AudioTrack::trackMapMutex;
00031 QHash<int, QPixmap>             AudioTrack::resizedArtist;
00032 
00033 AudioTrack::AudioTrack(const QString &path, Source *source)
00034     : source(source), path(path)
00035 {
00036     uid = 0;
00037     attributes["extension"] = QFileInfo(getAbsolutePath()).suffix();
00038     extractTags();
00039 }
00040 
00041 AudioTrack::AudioTrack(int id): source(NULL), uid(0)
00042 {
00043     QSqlQuery query;
00044     query.prepare("SELECT title, artist, album_artist, album, year, track, genre, duration, attributes, path, source "
00045                   "FROM audio_tracks WHERE id = :id LIMIT 1");
00046     query.bindValue(":id", id);
00047     query.exec();
00048     if (query.first())
00049     {
00050         uid = id;
00051         title = query.value(0).toString();
00052         artist = query.value(1).toString();
00053         albumArtist = query.value(2).toString();
00054         album = query.value(3).toString();
00055         year = query.value(4).toInt();
00056         track = query.value(5).toInt();
00057         genre = query.value(6).toString();
00058         duration = query.value(7).toInt();
00059         attributes = Network::JsonParser().parse(query.value(8).toByteArray()).toMap();
00060         path = query.value(9).toString();
00061         source = Source::getSource(query.value(10).toInt());
00062         if (attributes.contains("image"))
00063             image.loadFromData(QByteArray::fromHex(attributes["image"].toByteArray()));
00064         else if (attributes["image-fetched-from-file"] != true)
00065         {
00066             extractCover();
00067             save();
00068         }
00069     }
00070     else
00071         qDebug() << "No audio track with id " << id;
00072 }
00073 
00074 void    AudioTrack::extractTags()
00075 {
00076     TagLib::FileRef file(getAbsolutePath().toLocal8Bit().data());
00077     if(!file.isNull() && file.tag()) {
00078 
00079         TagLib::Tag *tag = file.tag();
00080         title = tag->title().toCString();
00081         artist = tag->artist().toCString();
00082         album = tag->album().toCString();
00083         year = tag->year();
00084         attributes["comment"] = tag->comment().toCString();
00085         track = tag->track();
00086         genre = tag->genre().toCString();
00087     }
00088     if (!file.isNull() && file.audioProperties()) {
00089         TagLib::AudioProperties *properties = file.audioProperties();
00090         attributes["bitrate"] = properties->bitrate();
00091         attributes["sampleRate"] = properties->sampleRate();
00092         attributes["channels"] = properties->channels();
00093         duration = properties->length();
00094     }
00095     if (!attributes["extension"].toString().compare("mp3", Qt::CaseInsensitive))
00096     {
00097         TagLib::MPEG::File mp3File(getAbsolutePath().toUtf8().data());
00098         if (mp3File.ID3v2Tag())
00099         {
00100             TagLib::ID3v2::FrameList frames = mp3File.ID3v2Tag()->frameListMap()["TPE2"];
00101             if(!frames.isEmpty())
00102                 albumArtist = frames.front()->toString().toCString();
00103          }
00104     }
00105     if (artist.isNull() || artist.isEmpty())
00106         artist = "Unknown Artist";
00107     if (album.isNull() || album.isEmpty())
00108         album = "Unknown Album";
00109     if (albumArtist.isNull() || albumArtist.isEmpty())
00110         albumArtist = artist;
00111     if (title.isNull() || title.isEmpty())
00112         title = QFileInfo(getAbsolutePath()).fileName();
00113     extractCover();
00114 }
00115 
00116 void    AudioTrack::extractCover()
00117 {
00118     if (image.isNull())
00119     {
00120         QMutexLocker    locker(&taglibMutex);
00121         TagLib::FileRef fileref(getAbsolutePath().toLocal8Bit().data());
00122         TagLib::ID3v2::Tag tag(fileref.file(),0);
00123         TagLib::ID3v2::FrameList list = tag.frameListMap()["APIC"];
00124         if (!list.isEmpty())
00125         {
00126             TagLib::ID3v2::AttachedPictureFrame *picture = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(list.front());
00127             image.loadFromData((const uchar *) picture->picture().data(), picture->picture().size());
00128             QByteArray  data;
00129             QBuffer     buf(&data);
00130             image.save(&buf, "jpg");
00131             attributes["image"] = data.toHex();
00132         }
00133         attributes["image-fetched-from-file"] = true;
00134     }
00135 }
00136 
00137 const QString   AudioTrack::getAbsolutePath() const
00138 {
00139     if (source)
00140         return source->toUrl().toLocalFile() + getPath();
00141     else
00142         return getPath();
00143 }
00144 
00145 AudioTrack* AudioTrack::getTrack(int id)
00146 {
00147     if (!tracksMap.contains(id))
00148     {
00149         AudioTrack  *track = new AudioTrack(id);
00150         if (!track->valid())
00151         {
00152             delete track;
00153             track = 0;
00154         }
00155         {
00156             QMutexLocker    lock(&trackMapMutex);
00157             tracksMap.insert(id, track);
00158         }
00159     }
00160     return tracksMap[id];
00161 }
00162 
00163 bool    AudioTrack::isLoaded(int id)
00164 {
00165     QMutexLocker    lock(&trackMapMutex);
00166     return tracksMap.contains(id);
00167 }
00168 
00169 void    AudioTrack::loadAndCall(int id, QObject* obj, const char* slot)
00170 {
00171     if (!trackLoaders.contains(id))
00172     {
00173         // Create loader
00174         AudioTrackLoader    *loader = new AudioTrackLoader(id);
00175         connect(loader, SIGNAL(loaded(int)), obj, slot);
00176         trackLoaders[id] = loader;
00177     }
00178     else
00179     {
00180         //connect(trackLoaders[id], SIGNAL(loaded(int)), obj, slot);
00181     }
00182 }
00183 
00184 const QString&  AudioTrack::getTitle() const
00185 {
00186     return (title);
00187 }
00188 
00189 const QString&  AudioTrack::getArtist() const
00190 {
00191     return (artist);
00192 }
00193 
00194 const QString&  AudioTrack::getAlbumArtist() const
00195 {
00196     return (albumArtist);
00197 }
00198 
00199 const QString&  AudioTrack::getAlbum() const
00200 {
00201     return (album);
00202 }
00203 
00204 const QString&  AudioTrack::getGenre() const
00205 {
00206     return (genre);
00207 }
00208 
00209 const QString& AudioTrack::getPath() const
00210 {
00211     return (path);
00212 }
00213 
00214 int             AudioTrack::getUid() const
00215 {
00216     return (uid);
00217 }
00218 
00219 int             AudioTrack::getYear() const
00220 {
00221     return (year);
00222 }
00223 
00224 int             AudioTrack::getTrack() const
00225 {
00226     return (track);
00227 }
00228 
00229 int             AudioTrack::getDuration() const
00230 {
00231     return (duration);
00232 }
00233 
00234 const QVariant  AudioTrack::getAttribute(const QString& name) const
00235 {
00236     if (attributes.contains(name))
00237         return attributes[name];
00238     else
00239         return QVariant();
00240 }
00241 
00242 Source* AudioTrack::getSource() const
00243 {
00244     return source;
00245 }
00246 
00247 void AudioTrack::setSource(Source *source)
00248 {
00249     this->source = source;
00250 }
00251 
00252 void            AudioTrack::setTitle(const QString& title)
00253 {
00254     this->title = title;
00255 }
00256 
00257 void            AudioTrack::setArtist(const QString& artist)
00258 {
00259     this->artist = artist;
00260 }
00261 
00262 void            AudioTrack::setAlbumArtist(const QString& albumArtist)
00263 {
00264     this->albumArtist = albumArtist;
00265 }
00266 
00267 void            AudioTrack::setAlbum(const QString& album)
00268 {
00269     this->album = album;
00270 }
00271 
00272 void            AudioTrack::setGenre(const QString& genre)
00273 {
00274     this->genre = genre;
00275 }
00276 
00277 void AudioTrack::setPath(const QString &path)
00278 {
00279     this->path = path;
00280 }
00281 
00282 void            AudioTrack::setUid(int uid)
00283 {
00284     this->uid = uid;
00285 }
00286 
00287 void            AudioTrack::setYear(int year)
00288 {
00289     this->year = year;
00290 }
00291 
00292 void            AudioTrack::setTrack(int track)
00293 {
00294     this->track = track;
00295 }
00296 
00297 void            AudioTrack::setDuration(int duration)
00298 {
00299     this->duration = duration;
00300 }
00301 
00302 void            AudioTrack::setAttribute(const QString& name, const QVariant& value)
00303 {
00304     attributes[name] = value;
00305 }
00306 
00307 bool            AudioTrack::newRecord() const
00308 {
00309     return uid == 0;
00310 }
00311 
00312 bool            AudioTrack::valid() const
00313 {
00314     return uid > 0;
00315 }
00316 
00317 void            AudioTrack::save()
00318 {
00319     QSqlQuery query;
00320 
00321     if (newRecord())
00322         query.prepare("INSERT INTO audio_tracks(title, artist, album_artist, album, year, track, "
00323                       "genre, duration, attributes, path, source) VALUES (:title, :artist, :album_artist, :album, "
00324                       ":year, :track, :genre, :duration, :attributes, :path, :source)");
00325     else
00326         query.prepare("UPDATE audio_tracks SET title = :title, artist = :artist, album_artist = :album_artist, "
00327                       "album = :album, year = :year, track = :track, genre = :genre, "
00328                       "duration = :duration, attributes = :attributes, path = :path, source = :source "
00329                       "WHERE id = :id");
00330     query.bindValue(":id", uid);
00331     query.bindValue(":title", title);
00332     query.bindValue(":artist", artist);
00333     query.bindValue(":album_artist", albumArtist);
00334     query.bindValue(":album", album);
00335     query.bindValue(":year", year);
00336     query.bindValue(":track", track);
00337     query.bindValue(":genre", genre);
00338     query.bindValue(":path", path);
00339     query.bindValue(":source", source->getId());
00340     query.bindValue(":duration", duration);
00341     query.bindValue(":attributes", Network::JsonParser().serialize(attributes));
00342     if (!query.exec())
00343         throw_exception(0x01, tr("Can't save audio track: %1").arg(query.lastError().databaseText()));
00344     // Fetch new id if inserted
00345     if (newRecord())
00346     {
00347         uid = query.lastInsertId().toInt();
00348     }
00349 }
00350 
00351 void            AudioTrack::remove()
00352 {
00353     QSqlQuery query;
00354 
00355     if (!newRecord())
00356     {
00357         query.prepare("DELETE FROM audio_tracks WHERE id = :id");
00358         query.bindValue(":id", uid);
00359         emit removed(this);
00360         if (query.exec())
00361         {
00362             tracksMap.remove(uid);
00363             uid = 0;
00364             deleteLater();
00365         }
00366         else
00367             qDebug() << QString("Can't remote tracks: %1 (%2)").arg(query.lastError().text()).arg(query.lastQuery());
00368 
00369     }
00370 }
00371 
00372 const QMap<int, AudioTrack*>& AudioTrack::getTracksMap()
00373 {
00374     return tracksMap;
00375 }
00376 
00377 const QImage&   AudioTrack::getAlbumArt() const
00378 {
00379     if (image.isNull())
00380     {
00381         if (defaultImage.isNull())
00382             defaultImage.load(":cover/default");
00383         return defaultImage;
00384     }
00385     else
00386         return image;
00387 }
00388 
00389 const QPixmap&  AudioTrack::getAlbumArtAtSize(int size)
00390 {
00391     if (!resizedImages.contains(size))
00392         resizedImages[size] = QPixmap::fromImage(getAlbumArt().scaled(QSize(size, size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
00393     return resizedImages[size];
00394 }
00395 
00396 const QImage&   AudioTrack::getArtistArt() const
00397 {
00398     if (artistImage.isNull())
00399         artistImage.load(":cover/artist");
00400     return artistImage;
00401 }
00402 
00403 const QPixmap&  AudioTrack::getArtistArtAtSize(int size)
00404 {
00405     if (!resizedArtist.contains(size))
00406         resizedArtist[size] = QPixmap::fromImage(getArtistArt().scaled(QSize(size, size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
00407     return resizedArtist[size];
00408 }
00409