defuze.me
Client
|
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