Clementine-audio-player-Mac.../src/core/tagfetcher.cpp

231 lines
7.3 KiB
C++

/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tagfetcher.h"
#include <QtDebug>
#include <QCoreApplication>
#include <QFile>
#include <QMutex>
#include <QNetworkReply>
#include <QStringList>
#include "version.h"
#ifdef HAVE_LIBTUNEPIMP
const char* kMusicBrainzLookupUrl = "http://musicbrainz.org/ws/1/track/?type=xml&puid=%1";
TagFetcher::TagFetcher(QObject *parent)
: QObject(parent),
network_(new NetworkAccessManager(this)),
pimp_(NULL)
{
QString plugin_path =
#ifdef Q_OS_DARWIN
QCoreApplication::applicationDirPath() + "/../PlugIns/tunepimp";
#else
QString();
#endif
pimp_ = tp_NewWithArgs(
QCoreApplication::applicationName().toAscii().constData(),
CLEMENTINE_VERSION,
TP_THREAD_ALL,
plugin_path.isNull()? NULL:plugin_path.toLocal8Bit().constData());
tp_SetDebug(pimp_, true);
tp_SetAutoSaveThreshold(pimp_, -1);
tp_SetMoveFiles(pimp_, false);
tp_SetRenameFiles(pimp_, false);
tp_SetFileNameEncoding(pimp_, "UTF-8");
tp_SetNotifyCallback(pimp_, NotifyCallback, this);
tp_SetMusicDNSClientId(pimp_, "0c6019606b1d8a54d0985e448f3603ca");
}
TagFetcher::~TagFetcher() {
delete network_;
tp_Delete(pimp_);
}
void TagFetcher::FetchFromFile(const QString& path) {
mutex_active_fetchers_.lock();
int id = tp_AddFile(pimp_, QFile::encodeName(path), 0);
TagFetcherItem* tagFetcherItem = new TagFetcherItem(path, id, pimp_, network_);
active_fetchers_.insert(id, tagFetcherItem);
mutex_active_fetchers_.unlock();
connect(tagFetcherItem, SIGNAL(PuidGeneratedSignal()), tagFetcherItem, SLOT(PuidGeneratedSlot()), Qt::QueuedConnection);
connect(tagFetcherItem, SIGNAL(FetchFinishedSignal(int)), SLOT(FetchFinishedSlot(int)));
}
void TagFetcher::NotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId, TPFileStatus status)
{
if(type == tpFileChanged) {
TagFetcher *tagFetcher = (TagFetcher*)data;
tagFetcher->CallReturned(fileId, status);
}
}
void TagFetcher::CallReturned(int fileId, TPFileStatus status)
{
// Using a lock, to prevent getting NULL item, when callback is called but
// item not yet inserted: this could happen because libtunepimp proceed in
// a separate thread after tp_AddFile call
mutex_active_fetchers_.lock();
TagFetcherItem *tagFetcherItem = active_fetchers_.value(fileId);
mutex_active_fetchers_.unlock();
switch(status) {
case eRecognized:
// TODO
break;
case eUnrecognized:
tagFetcherItem->Unrecognized();
break;
case ePUIDLookup:
case ePUIDCollision:
case eFileLookup:
//TODO
tagFetcherItem->PuidGenerated();
break;
case eUserSelection:
// TODO
break;
case eError:
//TODO
break;
default:
break;
}
}
void TagFetcher::FetchFinishedSlot(int id) {
TagFetcherItem *tagFetcherItem = active_fetchers_.value(id);
tp_Remove(pimp_, id);
active_fetchers_.erase(active_fetchers_.find(id));
emit FetchFinished(tagFetcherItem->getFilename(), tagFetcherItem->getSongsFetched());
delete tagFetcherItem;
}
TagFetcherItem::TagFetcherItem(const QString& _filename, int _fileId, tunepimp_t _pimp, NetworkAccessManager *_network)
: filename_(_filename),
fileId_(_fileId),
pimp_(_pimp),
network_(_network),
already_tried_to_recognize_(false)
{ }
void TagFetcherItem::Unrecognized() {
if(already_tried_to_recognize_) {
// We already tried to recognize this music and, apparently, nothing has been found: stopping here
emit FetchFinishedSignal(fileId_);
} else {
already_tried_to_recognize_ = true;
char trm[255];
trm[0] = 0;
track_t track = tp_GetTrack(pimp_, fileId_);
tr_Lock(track);
tr_GetPUID(track, trm, 255);
if ( !trm[0] ) {
tr_SetStatus(track, ePending);
tp_Wake(pimp_, track);
}
tr_Unlock(track);
tp_ReleaseTrack(pimp_, track);
}
}
void TagFetcherItem::PuidGenerated() {
// Get PUID
track_t track = tp_GetTrack(pimp_, fileId_);
tr_Lock(track);
tr_GetPUID(track, puid_, 255);
tr_Unlock(track);
tp_ReleaseTrack(pimp_, track);
// Now we have PUID, let's download song's info from musicbrainz's Web Service
emit PuidGeneratedSignal();
}
void TagFetcherItem::PuidGeneratedSlot() {
QString reqString(QString(kMusicBrainzLookupUrl).arg(QString(puid_)));
QNetworkRequest req = QNetworkRequest(QUrl(reqString));
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
QNetworkRequest::AlwaysNetwork);
QNetworkReply* reply = network_->get(req);
connect(reply, SIGNAL(finished()), SLOT(DownloadInfoFinished()));
}
void TagFetcherItem::DownloadInfoFinished() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
Q_ASSERT(reply);
QXmlStreamReader reader(reply);
while(!reader.atEnd()) {
reader.readNext();
if(reader.tokenType() == QXmlStreamReader::StartElement &&
reader.name() == "track") {
songs_fetched_ << ReadTrack(&reader);
}
}
emit FetchFinishedSignal(fileId_);
}
SongList TagFetcherItem::ReadTrack(QXmlStreamReader *reader) {
QString currentArtist;
QString currentTitle;
SongList songs;
while(!reader->atEnd()) {
reader->readNext();
if(reader->tokenType() == QXmlStreamReader::StartElement) {
if(reader->name() == "title") { // track's title
currentTitle = reader->readElementText();
} else if(reader->name() == "artist") {
reader->readNext();
if(reader->name() == "name") {
currentArtist = reader->readElementText();
}
} else if(reader->name() == "release-list") {
reader->readNext();
while(!reader->atEnd()) {
if(reader->name() == "title") { // album (release) title
QString albumTitle = reader->readElementText();
Song newSongMatch;
newSongMatch.Init(currentTitle, currentArtist, albumTitle, 0); // Using 0 as length. We could have used <duration> field but it is not usefull, so don't wasting our time and keeping 0
reader->readNext();
if(reader->name() == "track-list") {
QXmlStreamAttributes attributes = reader->attributes();
if(attributes.hasAttribute("offset")) {
int track = attributes.value("offset").toString().toInt()+1;
newSongMatch.set_track(track);
}
reader->readNext();
}
songs << newSongMatch;
} else if(reader->tokenType() == QXmlStreamReader::EndElement &&
reader->name() == "release-list") {
break;
} else {
reader->readNext();
}
}
}
}
}
return songs;
}
#endif // HAVE_LIBTUNEPIMP