From 2cc6dc0a2fbe46aa6f17d4eccb4950cc60fcb700 Mon Sep 17 00:00:00 2001 From: "craig.p.drummond" Date: Thu, 11 Apr 2013 19:37:07 +0000 Subject: [PATCH] Use musibrainz id from last.fm to search coverartarchive --- ChangeLog | 2 +- gui/coverdialog.cpp | 317 ++++++++++++++++++++++++-------------------- gui/coverdialog.h | 4 +- 3 files changed, 175 insertions(+), 148 deletions(-) diff --git a/ChangeLog b/ChangeLog index 84ddb794a..0c02c75c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,7 +25,7 @@ and cache items deleted. 13. Move group warning to last stage of intial settings wizard. 14. Basic cover fetching dialog, allowing to change downloaded cover. Currently - Last.fm, Google, and Discogs are used to find images. + Last.fm, Google, Discogs, and CoverArtArchive are used to find images. 15. Use cache by default for devices. 16. GZip compress cache files. 17. Save remote device library settings on remote device. diff --git a/gui/coverdialog.cpp b/gui/coverdialog.cpp index 7b08ac891..b36658532 100644 --- a/gui/coverdialog.cpp +++ b/gui/coverdialog.cpp @@ -73,6 +73,12 @@ int CoverDialog::instanceCount() return iCount; } +// Only really want square-ish covers! +bool canUse(int w, int h) +{ + return w>90 && h>90 && (w==h || (h<=(w*1.1) && w<=(h*1.1))); +} + class CoverItem : public QListWidgetItem { public: @@ -81,8 +87,8 @@ public: Type_Local, Type_LastFm, Type_Google, -// Type_Yahoo - Type_Discogs + Type_Discogs, + Type_CoverArtArchive }; CoverItem(const QString &u, const QString &tu, QListWidget *parent) @@ -175,19 +181,6 @@ private: int height; }; -//class YahooCover : public CoverItem -//{ -//public: -// YahooCover(const QString &u, const QString &tu, const QImage &img, QListWidget *parent) -// : CoverItem(u, tu, parent) { -// setImage(img); -// setText(i18n("Yahoo!")); -// } - -// quint32 key() const { return 0xFFFFFFFE; } -// Type type() const { return Type_Yahoo; } -//}; - class DiscogsCover : public CoverItem { public: @@ -206,6 +199,19 @@ private: int height; }; +class CoverArtArchiveCover : public CoverItem +{ +public: + CoverArtArchiveCover(const QString &u, const QString &tu, const QImage &img, QListWidget *parent) + : CoverItem(u, tu, parent) { + setImage(img); + setText("coverartarchive.org"); + } + + quint32 key() const { return 0xFFFFFFFC; } + Type type() const { return Type_CoverArtArchive; } +}; + class ExistingCover : public CoverItem { public: @@ -430,11 +436,13 @@ static const char * constWidthProperty="w"; static const char * constHeightProperty="h"; static const char * constSizeProperty="sz"; static const char * constTypeProperty="type"; +static const char * constRedirectsProperty="redirects"; static const char * constLastFmHost="ws.audioscrobbler.com"; static const char * constGoogleHost="images.google.com"; -//static const char * constYahooHost="boss.yahooapis.com"; static const char * constDiscogsHost="api.discogs.com"; -//static const char * constYahooApiKey=0; +static const char * constCoverArtArchiveHost="coverartarchive.org"; + +static const int constMaxRedirects=5; void CoverDialog::queryJobFinished() { @@ -445,18 +453,25 @@ void CoverDialog::queryJobFinished() currentQuery.remove(reply); if(QNetworkReply::NoError==reply->error()) { - QByteArray resp=reply->readAll(); QString host=reply->property(constHostProperty).toString(); - if (constLastFmHost==host) { - parseLstFmQueryResponse(resp); - } else if (constGoogleHost==host) { - parseGoogleQueryResponse(resp); - } else if (constDiscogsHost==host) { - parseDiscogsQueryResponse(resp); + QVariant redirect = reply->header(QNetworkRequest::LocationHeader); + if (redirect.isValid()) { + int rd=reply->property(constRedirectsProperty).toInt(); + if (rdreadAll(); + if (constLastFmHost==host) { + parseLstFmQueryResponse(resp); + } else if (constGoogleHost==host) { + parseGoogleQueryResponse(resp); + } else if (constDiscogsHost==host) { + parseDiscogsQueryResponse(resp); + } else if (constCoverArtArchiveHost==host) { + parseCoverArtArchiveQueryResponse(resp); + } } -// else if (constYahooHost==host) { -// parseYahooQueryResponse(resp); -// } } reply->deleteLater(); if (spinner && currentQuery.isEmpty()) { @@ -518,63 +533,93 @@ void CoverDialog::downloadJobFinished() currentQuery.remove(reply); if(QNetworkReply::NoError==reply->error()) { - QString url=reply->url().toString(); - QByteArray data=reply->readAll(); - FileType fileType=url.endsWith(".jpg") || url.endsWith(".jpeg") ? FT_Jpg : (url.endsWith(".png") ? FT_Png : FT_Other); - QImage img=QImage::fromData(data, FT_Jpg==fileType ? "JPG" : (FT_Png==fileType ? "PNG" : 0)); - if (!img.isNull()) { - QString host=reply->property(constHostProperty).toString(); - bool isLarge=reply->property(constThumbProperty).toString().isEmpty(); - QTemporaryFile *temp=0; - - if (isLarge || (reply->property(constThumbProperty).toString()==reply->property(constLargeProperty).toString())) { - temp=new QTemporaryFile(QDir::tempPath()+"/cantata_XXXXXX."+(FT_Jpg==fileType ? "jpg" : "png")); - - if (temp->open()) { - if (FT_Other==fileType) { - img.save(temp, "PNG"); - } else { - temp->write(data); + QString host=reply->property(constHostProperty).toString(); + QVariant redirect = reply->header(QNetworkRequest::LocationHeader); + if (redirect.isValid()) { + int rd=reply->property(constRedirectsProperty).toInt(); + if (rdsetProperty(constRedirectsProperty, rd+1); + QStringList stringProps=QStringList() << constHostProperty << constLargeProperty << constThumbProperty; + QStringList intProps=QStringList() << constWidthProperty << constHeightProperty << constSizeProperty; + foreach (const QString &prop, stringProps) { + QVariant p=reply->property(prop.toLatin1().constData()); + if (p.isValid()) { + j->setProperty(prop.toLatin1().constData(), p.toString()); + } } - if (tempFiles.size()>=constMaxTempFiles) { - QTemporaryFile *last=tempFiles.takeLast(); - last->remove(); - delete last; + foreach (const QString &prop, intProps) { + QVariant p=reply->property(prop.toLatin1().constData()); + if (p.isValid()) { + j->setProperty(prop.toLatin1().constData(), p.toInt()); + } } - temp->setProperty(constLargeProperty, reply->property(constLargeProperty)); - tempFiles.prepend(temp); - } else { - delete temp; - temp=0; } } - if (isLarge) { - if (DL_LargePreview==dlType) { - previewDialog()->showImage(img, reply->property(constLargeProperty).toString()); - } else if (DL_LargeSave==dlType) { - if (!temp) { - MessageBox::error(this, i18n("Failed to set cover!\nCould not download to temporary file!")); - } else if (saveCover(temp->fileName(), img)) { - accept(); + } else { + QString url=reply->url().toString(); + QByteArray data=reply->readAll(); + FileType fileType=url.endsWith(".jpg") || url.endsWith(".jpeg") ? FT_Jpg : (url.endsWith(".png") ? FT_Png : FT_Other); + QImage img=QImage::fromData(data, FT_Jpg==fileType ? "JPG" : (FT_Png==fileType ? "PNG" : 0)); + if (!img.isNull()) { + bool isLarge=reply->property(constThumbProperty).toString().isEmpty(); + QTemporaryFile *temp=0; + + if (isLarge || (reply->property(constThumbProperty).toString()==reply->property(constLargeProperty).toString())) { + temp=new QTemporaryFile(QDir::tempPath()+"/cantata_XXXXXX."+(FT_Jpg==fileType ? "jpg" : "png")); + + if (temp->open()) { + if (FT_Other==fileType) { + img.save(temp, "PNG"); + } else { + temp->write(data); + } + if (tempFiles.size()>=constMaxTempFiles) { + QTemporaryFile *last=tempFiles.takeLast(); + last->remove(); + delete last; + } + temp->setProperty(constLargeProperty, reply->property(constLargeProperty)); + tempFiles.prepend(temp); + } else { + delete temp; + temp=0; } } - } else { - CoverItem *item=0; - if (constLastFmHost==host) { - item=new LastFmCover(reply->property(constLargeProperty).toString(), url, img, list); - } else if (constGoogleHost==host) { - item=new GoogleCover(reply->property(constLargeProperty).toString(), url, img, - reply->property(constWidthProperty).toInt(), reply->property(constHeightProperty).toInt(), - reply->property(constSizeProperty).toInt(), list); - } else if (constDiscogsHost==host) { - item=new DiscogsCover(reply->property(constLargeProperty).toString(), url, img, - reply->property(constWidthProperty).toInt(), reply->property(constHeightProperty).toInt(), list); - } -// else if (constYahooHost==host) { -// item=new YahooCover(reply->property(constLargeProperty).toString(), url, img, list); -// } - if (item) { - insertItem(item); + if (isLarge) { + if (DL_LargePreview==dlType) { + previewDialog()->showImage(img, reply->property(constLargeProperty).toString()); + } else if (DL_LargeSave==dlType) { + if (!temp) { + MessageBox::error(this, i18n("Failed to set cover!\nCould not download to temporary file!")); + } else if (saveCover(temp->fileName(), img)) { + accept(); + } + } + } else { + CoverItem *item=0; + if (constLastFmHost==host) { + item=new LastFmCover(reply->property(constLargeProperty).toString(), url, img, list); + } else if (constGoogleHost==host) { + int w=reply->property(constWidthProperty).toInt(); + int h=reply->property(constHeightProperty).toInt(); + if (canUse(w, h)) { + item=new GoogleCover(reply->property(constLargeProperty).toString(), url, img, w, h, + reply->property(constSizeProperty).toInt(), list); + } + } else if (constDiscogsHost==host) { + int w=reply->property(constWidthProperty).toInt(); + int h=reply->property(constHeightProperty).toInt(); + if (canUse(w, h)) { + item=new DiscogsCover(reply->property(constLargeProperty).toString(), url, img, w, h, list); + } + } else if (constCoverArtArchiveHost==host) { + item=new CoverArtArchiveCover(reply->property(constLargeProperty).toString(), url, img, list); + } + if (item) { + insertItem(item); + } } } } @@ -708,16 +753,6 @@ void CoverDialog::sendQuery() #endif sendQueryRequest(googleUrl); -// QUrl yahoo; -// yahoo.setScheme("http"); -// yahoo.setHost(constYahooHost); -// yahoo.setPath("/ysearch/images/v1/" + fixedQuery); -// yahoo.addQueryItem("appid", constYahooApiKey); -// yahoo.addQueryItem("count", QString::number(20)); -// yahoo.addQueryItem("start", QString::number(20 * page)); -// yahoo.addQueryItem("format", "xml"); -// sendQueryRequest(yahoo); - QUrl discogsUrl; #if QT_VERSION < 0x050000 QUrl &discogsQuery=discogsUrl; @@ -834,11 +869,12 @@ void CoverDialog::clearTempFiles() } } -void CoverDialog::sendQueryRequest(const QUrl &url) +void CoverDialog::sendQueryRequest(const QUrl &url, int redirects, const QString &host) { QNetworkReply *j=NetworkAccessManager::self()->get(QNetworkRequest(url)); - j->setProperty(constHostProperty, url.host()); + j->setProperty(constHostProperty, host.isEmpty() ? url.host() : host); j->setProperty(constTypeProperty, (int)DL_Query); + j->setProperty(constRedirectsProperty, redirects); connect(j, SIGNAL(finished()), this, SLOT(queryJobFinished())); currentQuery.insert(j); } @@ -881,6 +917,7 @@ QNetworkReply * CoverDialog::downloadImage(const QString &url, DownloadType dlTy connect(j, SIGNAL(finished()), this, SLOT(downloadJobFinished())); currentQuery.insert(j); j->setProperty(constTypeProperty, (int)dlType); + j->setProperty(constRedirectsProperty, 0); if (saving) { previewDialog()->downloading(url); connect(j, SIGNAL(downloadProgress(qint64, qint64)), preview, SLOT(progress(qint64, qint64))); @@ -895,6 +932,7 @@ void CoverDialog::parseLstFmQueryResponse(const QByteArray &resp) QXmlStreamReader doc(resp); SizeMap urls; QList entries; + QStringList musibBrainzIds; while (!doc.atEnd()) { doc.readNext(); @@ -909,6 +947,11 @@ void CoverDialog::parseLstFmQueryResponse(const QByteArray &resp) if (!size.isEmpty() && !url.isEmpty()) { urls.insert(size, url); } + } else if (inSection && QLatin1String("mbid")==doc.name()) { + QString id=doc.readElementText(); + if (id.length()>4) { + musibBrainzIds.append(id); + } } } else if (doc.isEndElement() && inSection && QLatin1String("album")==doc.name()) { if (!urls.isEmpty()) { @@ -949,6 +992,14 @@ void CoverDialog::parseLstFmQueryResponse(const QByteArray &resp) } } } + + foreach (const QString &id, musibBrainzIds) { + QUrl coverartUrl; + coverartUrl.setScheme("http"); + coverartUrl.setHost(constCoverArtArchiveHost); + coverartUrl.setPath("/release/"+id); + sendQueryRequest(coverartUrl); + } } void CoverDialog::parseGoogleQueryResponse(const QByteArray &resp) @@ -990,61 +1041,6 @@ void CoverDialog::parseGoogleQueryResponse(const QByteArray &resp) } } -//void CoverDialog::parseYahooQueryResponse(const QString &resp) -//{ -// QXmlStreamReader xml(resp); - -// while (!xml.atEnd() && !xml.hasError()) { -// xml.readNext(); -// if (!xml.isStartElement() || "resultset_images"!=xml.name()) { -// continue; -// } - -// while (!xml.atEnd() && !xml.hasError()) { -// xml.readNext(); -// if (xml.isEndElement() && "resultset_images"==xml.name()) { -// break; -// } -// if (!xml.isStartElement()) { -// continue; -// } - -// if ("result"==xml.name()) { -// QString thumbUrl; -// QString largeUrl; -// while (!xml.atEnd() && !xml.hasError()) { -// xml.readNext(); -// const QStringRef &n = xml.name(); -// if (xml.isEndElement() && "result"==n) { -// break; -// } - -// if (!xml.isStartElement()) { -// continue; -// } - -// if ("thumbnail_url"==n) { -// thumbUrl=xml.readElementText(); -// } else if ("url"==n) { -// largeUrl=xml.readElementText(); -// } -// } - -// if (!thumbUrl.isEmpty() && !largeUrl.isEmpty()) { -// QNetworkReply *j=downloadImage(thumbUrl, DL_Thumbnail); -// if (j) { -// j->setProperty(constHostProperty, constYahooHost); -// j->setProperty(constLargeProperty, largeUrl); -// j->setProperty(constThumbProperty, thumbUrl); -// } -// } -// } else { -// xml.skipCurrentElement(); -// } -// } -// } -//} - void CoverDialog::parseDiscogsQueryResponse(const QByteArray &resp) { QJson::Parser parser; @@ -1120,6 +1116,37 @@ void CoverDialog::parseDiscogsQueryResponse(const QByteArray &resp) } } +void CoverDialog::parseCoverArtArchiveQueryResponse(const QByteArray &resp) +{ + QJson::Parser parser; + bool ok=false; + QVariantMap parsed=parser.parse(resp, &ok).toMap(); + if (ok && parsed.contains("images")) { + QVariantList images=parsed["images"].toList(); + foreach (const QVariant &i, images) { + QVariantMap im=i.toMap(); + if (im.contains("front") && im["front"].toBool() && im.contains("image") && im.contains("thumbnails")) { + QVariantMap thumb=im["thumbnails"].toMap(); + QString largeUrl=im["image"].toString(); + QString thumbUrl; + if (thumb.contains("small")) { + thumbUrl=thumb["small"].toString(); + } else if (thumb.contains("large")) { + thumbUrl=thumb["large"].toString(); + } + if (!thumbUrl.isEmpty() && !largeUrl.isEmpty()) { + QNetworkReply *j=downloadImage(thumbUrl, DL_Thumbnail); + if (j) { + j->setProperty(constHostProperty, constCoverArtArchiveHost); + j->setProperty(constLargeProperty, largeUrl); + j->setProperty(constThumbProperty, thumbUrl); + } + } + } + } + } +} + void CoverDialog::slotButtonClicked(int button) { switch (button) { diff --git a/gui/coverdialog.h b/gui/coverdialog.h index d9ef45e03..37d1434a3 100644 --- a/gui/coverdialog.h +++ b/gui/coverdialog.h @@ -121,11 +121,11 @@ private: void insertItem(CoverItem *item); QNetworkReply * downloadImage(const QString &url, DownloadType dlType); void clearTempFiles(); - void sendQueryRequest(const QUrl &url); + void sendQueryRequest(const QUrl &url, int redirects=0, const QString &host=QString()); void parseLstFmQueryResponse(const QByteArray &resp); void parseGoogleQueryResponse(const QByteArray &resp); -// void parseYahooQueryResponse(const QByteArray &resp); void parseDiscogsQueryResponse(const QByteArray &resp); + void parseCoverArtArchiveQueryResponse(const QByteArray &resp); void slotButtonClicked(int button); bool saveCover(const QString &src, const QImage &img); void dragEnterEvent(QDragEnterEvent *event);