SVN commit 1282145 by sandsmark: Redo cover fetching. Yahoo has discontinued their image search API, so cover downloading has been broken for a while now. Google has also deprecated their own image search API, so now we instead get all covers directly from last.fm. M +0 -1 CMakeLists.txt M +1 -1 playlist.cpp M +86 -122 webimagefetcher.cpp M +6 -30 webimagefetcher.h D webimagefetcherdialog.cpp D webimagefetcherdialog.h --- trunk/KDE/kdemultimedia/juk/CMakeLists.txt #1282144:1282145 @@ -57,7 +57,6 @@ filerenameroptions.cpp filerenamerconfigdlg.cpp webimagefetcher.cpp - webimagefetcherdialog.cpp historyplaylist.cpp juk.cpp k3bexporter.cpp --- trunk/KDE/kdemultimedia/juk/playlist.cpp #1282144:1282145 @@ -844,7 +844,7 @@ if(!retrieveLocal) { m_fetcher->setFile((*items.begin())->file()); - m_fetcher->chooseCover(); + m_fetcher->searchCover(); return; } --- trunk/KDE/kdemultimedia/juk/webimagefetcher.cpp #1282144:1282145 @@ -1,6 +1,7 @@ /*************************************************************************** copyright : (C) 2004 Nathan Toone copyright : (C) 2007 Michael Pyne + copyright : (C) 2012 Martin Sandsmark ***************************************************************************/ /*************************************************************************** @@ -14,17 +15,18 @@ #include "webimagefetcher.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "covermanager.h" -#include "webimagefetcherdialog.h" #include "filehandle.h" #include "tag.h" #include "juk.h" @@ -33,34 +35,25 @@ #include #include #include +#include +#include +#include -WebImage::WebImage() -{ -} -WebImage::WebImage(const QString &imageURL, const QString &thumbURL, - int width, int height) : - m_imageURL(imageURL), - m_thumbURL(thumbURL), - m_size(QString("\n%1 x %2").arg(width).arg(height)) -{ -} - class WebImageFetcher::Private { friend class WebImageFetcher; - Private() : selectedIndex(0), connection(0), dialog(0) + Private() : connection(0), dialog(0) { } FileHandle file; - QString searchString; - QString loadedQuery; - WebImageList imageList; - uint selectedIndex; + QString artist; + QString albumName; QPointer connection; - WebImageFetcherDialog *dialog; + KDialog *dialog; + KUrl url; }; WebImageFetcher::WebImageFetcher(QObject *parent) @@ -70,17 +63,14 @@ WebImageFetcher::~WebImageFetcher() { - delete d->dialog; delete d; } void WebImageFetcher::setFile(const FileHandle &file) { d->file = file; - d->searchString = QString(file.tag()->artist() + ' ' + file.tag()->album()); - - if(d->dialog) - d->dialog->setFile(file); + d->artist = file.tag()->artist(); + d->albumName = file.tag()->album(); } void WebImageFetcher::abortSearch() @@ -88,20 +78,17 @@ if(d->connection) d->connection->kill(); } - -void WebImageFetcher::chooseCover() +void WebImageFetcher::searchCover() { - slotLoadImageURLs(); -} + KStatusBar *statusBar = JuK::JuKInstance()->statusBar(); + statusBar->showMessage(i18n("Searching for cover. Please Wait...")); -void WebImageFetcher::slotLoadImageURLs() -{ - d->imageList.clear(); - KUrl url("http://search.yahooapis.com/ImageSearchService/V1/imageSearch"); - url.addQueryItem("appid", "org.kde.juk/kde4"); - url.addQueryItem("query", d->searchString); - url.addQueryItem("results", "25"); + KUrl url("http://ws.audioscrobbler.com/2.0/"); + url.addQueryItem("method", "album.getInfo"); + url.addQueryItem("api_key", "3e6ecbd7284883089e8f2b5b53b0aecd"); + url.addQueryItem("artist", d->artist); + url.addQueryItem("album", d->albumName); kDebug() << "Using request " << url.encodedPathAndQuery(); @@ -119,14 +106,14 @@ return; if(!job || job->error()) { - kError() << "Error reading image results from Yahoo!\n"; + kError() << "Error reading image results from last.fm!\n"; kError() << d->connection->errorString() << endl; return; } kDebug() << "Checking for data!!\n"; if(d->connection->data().isEmpty()) { - kError() << "Yahoo image search returned an empty result!\n"; + kError() << "last.fm returned an empty result!\n"; return; } @@ -135,7 +122,7 @@ QString errorStr; int errorCol, errorLine; if(!results.setContent(d->connection->data(), &errorStr, &errorLine, &errorCol)) { - kError() << "Unable to create XML document from Yahoo results.\n"; + kError() << "Unable to create XML document from results.\n"; kError() << "Line " << errorLine << ", " << errorStr << endl; return; @@ -143,112 +130,89 @@ QDomNode n = results.documentElement(); - bool hasNoResults = false; - if(n.isNull()) { kDebug() << "No document root in XML results??\n"; - hasNoResults = true; + return; } - else { - QDomElement result = n.toElement(); - if(result.attribute("totalResultsReturned").toInt() == 0) - kDebug() << "Search returned " << result.attribute("totalResultsAvailable") << " results.\n"; + n = n.firstChildElement("album"); - if(result.isNull() || !result.hasAttribute("totalResultsReturned") || - result.attribute("totalResultsReturned").toInt() == 0) - { - hasNoResults = true; + d->url = n.lastChildElement("image").text(); //FIXME: We assume they have a sane sorting (smallest -> largest) + //TODO: size attribute can have the values mega, extralarge, large, medium and small + + kDebug() << "Got cover:" << d->url; + + KStatusBar *statusBar = JuK::JuKInstance()->statusBar(); + statusBar->showMessage(i18n("Downloading cover. Please Wait...")); + + KIO::StoredTransferJob *newJob = KIO::storedGet(d->url, KIO::Reload /* reload always */, KIO::HideProgressInfo); + connect(newJob, SIGNAL(result(KJob*)), SLOT(slotImageFetched(KJob*))); } - } - if(hasNoResults) +void WebImageFetcher::slotImageFetched(KJob* j) { - kDebug() << "Search returned no results.\n"; - requestNewSearchTerms(true /* no results */); - return; - } + KStatusBar *statusBar = JuK::JuKInstance()->statusBar(); + statusBar->clearMessage(); - // Go through each of the top (result) nodes + KIO::StoredTransferJob *job = qobject_cast(j); - n = n.firstChild(); - while(!n.isNull()) { - QDomNode resultUrl = n.namedItem("Url"); - QDomNode thumbnail = n.namedItem("Thumbnail"); - QDomNode height = n.namedItem("Height"); - QDomNode width = n.namedItem("Width"); + if (d->dialog) return; + d->dialog = new KDialog(); + d->dialog->setCaption("Cover found"); + d->dialog->setButtons(KDialog::Apply | KDialog::Cancel); + d->dialog->button(KDialog::Apply)->setText(i18n("Store")); + QWidget *mainWidget = new QWidget(); + d->dialog->setMainWidget(mainWidget); + mainWidget->setLayout(new QVBoxLayout); - // We have the necessary info, move to next node before we forget. - n = n.nextSibling(); - - if(resultUrl.isNull() || thumbnail.isNull() || height.isNull() || width.isNull()) { - kError() << "Invalid result returned, skipping.\n"; - continue; + if(job->error()) { + kError() << "Unable to grab image\n"; + d->dialog->setWindowIcon(DesktopIcon("dialog-error")); + return; } - d->imageList.append( - WebImage( - resultUrl.toElement().text(), - thumbnail.namedItem("Url").toElement().text(), - width.toElement().text().toInt(), - height.toElement().text().toInt() - ) - ); + QPixmap iconImage, realImage(150, 150); + iconImage.loadFromData(job->data()); + realImage.fill(Qt::transparent); + + if(iconImage.isNull()) { + kError() << "Thumbnail image is not of a supported format\n"; + return; } - // Have results, show them and pick one. + // Scale down if necesssary + if(iconImage.width() > 150 || iconImage.height() > 150) + iconImage = iconImage.scaled(150, 150, Qt::KeepAspectRatio, Qt::SmoothTransformation); - if(!d->dialog) { - d->dialog = new WebImageFetcherDialog(d->imageList, d->file, 0); - d->dialog->setModal(true); + QLabel *cover = new QLabel(); + cover->setPixmap(iconImage); + mainWidget->layout()->addWidget(cover); + QLabel *infoLabel = new QLabel(i18n("Cover fetched from last.fm.")); + infoLabel->setOpenExternalLinks(true); + infoLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + mainWidget->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + mainWidget->layout()->addWidget(infoLabel); - connect(d->dialog, SIGNAL(coverSelected(KUrl)), SLOT(slotCoverChosen(KUrl))); - connect(d->dialog, SIGNAL(newSearchRequested()), SLOT(slotNewSearch())); - } - - d->dialog->refreshScreen(d->imageList); + d->dialog->setWindowIcon(realImage); d->dialog->show(); + connect(d->dialog, SIGNAL(applyClicked()), SLOT(slotCoverChosen())); } -void WebImageFetcher::slotCoverChosen(const KUrl &path) + +void WebImageFetcher::slotCoverChosen() { kDebug() << "Adding new cover for " << d->file.tag()->fileName() - << "from URL" << path; + << "from URL" << d->url; - coverKey newId = CoverManager::addCover(path, d->file.tag()->artist(), d->file.tag()->album()); + coverKey newId = CoverManager::addCover(d->url, d->file.tag()->artist(), d->file.tag()->album()); if(newId != CoverManager::NoMatch) { emit signalCoverChanged(newId); d->dialog->close(); + d->dialog->deleteLater(); + d->dialog = 0; } } -void WebImageFetcher::slotNewSearch() -{ - requestNewSearchTerms(); -} - -void WebImageFetcher::displayWaitMessage() -{ - KStatusBar *statusBar = JuK::JuKInstance()->statusBar(); - statusBar->showMessage(i18n("Searching for Images. Please Wait...")); - slotLoadImageURLs(); - statusBar->clearMessage(); -} - -void WebImageFetcher::requestNewSearchTerms(bool noResults) -{ - bool ok; - QString search = KInputDialog::getText(i18n("Cover Downloader"), - noResults ? - i18n("No matching images found, please enter new search terms:") : - i18n("Enter new search terms:"), - d->searchString, &ok); - if(ok && !search.isEmpty()) { - d->searchString = search; - displayWaitMessage(); // This kicks off the new search - } -} - #include "webimagefetcher.moc" // vim: set et sw=4 tw=0 sta: --- trunk/KDE/kdemultimedia/juk/webimagefetcher.h #1282144:1282145 @@ -3,6 +3,8 @@ email : nathan@toonetown.com copyright : (C) 2007 Michael Pyne email : michael.pyne@kdemail.net + copyright : (C) 2012 Martin Sandsmark + email : martin.sandsmark@kde.org ***************************************************************************/ /*************************************************************************** @@ -18,7 +20,6 @@ #define WEBIMAGEFETCHER_H #include -#include // Predeclare some classes. @@ -31,27 +32,7 @@ class FileHandle; -class WebImage -{ -public: - WebImage(); - WebImage(const QString &imageURL, - const QString &thumbURL, - int width, int height); - - QString imageURL() const { return m_imageURL; } - QString thumbURL() const { return m_thumbURL; } - QString size() const { return m_size; } - -private: - QString m_imageURL; - QString m_thumbURL; - QString m_size; -}; - -typedef QList WebImageList; - class WebImageFetcher : public QObject { Q_OBJECT @@ -61,24 +42,19 @@ ~WebImageFetcher(); void setFile(const FileHandle &file); - void chooseCover(); public slots: void abortSearch(); + void searchCover(); + signals: - void signalNewSearch(WebImageList &images); void signalCoverChanged(int coverId); -private: - void displayWaitMessage(); - void requestNewSearchTerms(bool noResults = false); - private slots: - void slotLoadImageURLs(); void slotWebRequestFinished(KJob *job); - void slotCoverChosen(const KUrl &); - void slotNewSearch(); + void slotImageFetched(KJob *job); + void slotCoverChosen(); private: class Private;