2 * Copyright 2013 Canonical Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * Authors: Jussi Pakkanen <jussi.pakkanen@canonical.com>
17 * Pawel Stolowski <pawel.stolowski@canonical.com>
20#include "albumartprovider.h"
22#include <QNetworkAccessManager>
24#include <QNetworkRequest>
25#include <QNetworkReply>
27#include <QTemporaryFile>
39const std::string AlbumArtProvider::DEFAULT_ALBUM_ART = "/usr/share/unity/icons/album_missing.png";
40const std::string AlbumArtProvider::UNITY_LENS_SCHEMA = "com.canonical.Unity.Lenses";
42AlbumArtProvider::AlbumArtProvider()
43 : QQuickImageProvider(QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading),
46 auto schemas = g_settings_list_schemas();
48 for (int i = 0; schemas[i]; i++) {
49 if (g_strcmp0(schemas[i], UNITY_LENS_SCHEMA.c_str()) == 0) {
50 m_settings = g_settings_new(UNITY_LENS_SCHEMA.c_str());
55 if (m_settings == nullptr) {
56 qWarning() << "Missing" << QString::fromStdString(UNITY_LENS_SCHEMA) << "schema";
60AlbumArtProvider::~AlbumArtProvider()
63 g_object_unref(m_settings);
66std::string AlbumArtProvider::get_lastfm_url(const albuminfo &ai) {
67 QString artist = QString::fromStdString(ai.artist);
68 QString album = QString::fromStdString(ai.album);
69 // audioscrobbler wants it without any / (as it would break the URL)
73 /// @todo: this is the old API which will probably get axed at some point in the future
74 /// The new 2.0 API requires an API key, but supports JSON output, etc, so switching
75 /// to it should be done ASAP.
76 QString request = QString("http://ws.audioscrobbler.com/1.0/album/%1/%2/info.xml").arg(artist)
79 QScopedPointer<QNetworkAccessManager> am(new QNetworkAccessManager);
80 QNetworkReply *reply = am->get(QNetworkRequest(QUrl(request)));
82 QObject::connect(am.data(), &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
85 if (reply->error() != QNetworkReply::NoError) {
86 qWarning() << "Error getting the XML:" << reply->errorString();
92 tmp.setData(reply->readAll());
93 tmp.open(QIODevice::ReadOnly);
94 query.bindVariable("reply", &tmp);
95 query.setQuery("doc($reply)/album/coverart/large[1]/text()");
97 query.evaluateTo(&image);
98 image = image.trimmed();
100 // return empty if last.fm tries to offer their default
102 if (image.contains("catalogue/noimage")) {
106 return image.toStdString();
109bool AlbumArtProvider::download_and_store(const std::string &image_url, const std::string &output_file) {
110 QString url = QString::fromStdString(image_url);
111 QString fileName = QString::fromStdString(output_file);
113 QScopedPointer<QNetworkAccessManager> am(new QNetworkAccessManager);
114 QNetworkReply *reply = am->get(QNetworkRequest(QUrl(url)));
116 QObject::connect(am.data(), &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
119 if (reply->error() != QNetworkReply::NoError) {
120 qWarning() << "Error getting the image:" << reply->errorString();
124 QFile file(fileName);
125 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
126 qWarning() << "Could not open file for writing:" << file.errorString();
129 if (file.write(reply->readAll()) == -1){
130 qWarning() << "Could not write the image:" << file.error();
137void AlbumArtProvider::fix_format(const std::string &fname) {
138 // MediaArtSpec requires jpg. Convert to it if necessary.
139 FILE *f = fopen(fname.c_str(), "r");
142 unsigned char buf[2];
145 if(buf[0] == 0xff && buf[1] == 0xd8) {
148 QImage im(fname.c_str());
149 im.save(fname.c_str(), "JPEG");
152std::string AlbumArtProvider::get_image(const std::string &artist, const std::string &album) {
155 info.artist = artist;
158 if(info.album.empty() || info.artist.empty()) {
159 return DEFAULT_ALBUM_ART;
161 if(cache.has_art(info.artist, info.album)) {
162 // Image may have expired from cache between these two lines.
163 // It might expire before we return. C'est la vie.
164 return cache.get_art_file(info.artist, info.album);
167 if (m_settings != nullptr && g_strcmp0(g_settings_get_string(m_settings, "remote-content-search"), "all") !=0) {
168 qDebug() << "Remote content disabled";
169 return DEFAULT_ALBUM_ART;
172 std::string image_url = get_lastfm_url(info);
173 if(image_url.empty()) {
174 return DEFAULT_ALBUM_ART;
176 QTemporaryFile tempFile;
178 tempFile.setAutoRemove(true);
179 QString fname = tempFile.fileName();
180 std::string tmpname = fname.toUtf8().data();
182 if(!download_and_store(image_url, tmpname)) {
183 return DEFAULT_ALBUM_ART;
185 fix_format(tmpname.c_str());
187 if(!f.open(QIODevice::ReadWrite)) {
188 return DEFAULT_ALBUM_ART;
190 QByteArray arr = f.readAll();
193 cache.add_art(info.artist, info.album, arr.data(), arr.size());
195 std::string res = cache.get_art_file(info.artist, info.album);
197 return DEFAULT_ALBUM_ART;
201 return DEFAULT_ALBUM_ART;
204QImage AlbumArtProvider::requestImage(const QString &id, QSize *realSize, const QSize &requestedSize) {
205 Q_UNUSED(requestedSize)
208 if (!query.hasQueryItem(QStringLiteral("artist")) || !query.hasQueryItem(QStringLiteral("album"))) {
209 qWarning() << "Invalid albumart uri:" << id;
210 return QImage(QString::fromStdString(DEFAULT_ALBUM_ART));
212 const QString artist = query.queryItemValue(QStringLiteral("artist"), QUrl::FullyEncoded);
213 const QString album = query.queryItemValue(QStringLiteral("album"), QUrl::FullyEncoded);
215 std::string tgt_path;
217 tgt_path = get_image(artist.toStdString(), album.toStdString());
218 if(!tgt_path.empty()) {
220 image.load(QString::fromStdString(tgt_path));
221 // FIXME: Rescale to requested size preserving aspect.
222 *realSize = image.size();
225 } catch(std::exception &e) {
226 qDebug() << "Album art loader failed: " << e.what();
228 qDebug() << "Unknown error when generating image.";
231 QImage fallback(QString::fromStdString(DEFAULT_ALBUM_ART));
232 *realSize = fallback.size();
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list