on xenial help sign in
Remove the albumart image provider
bc995adMichal Hruby
plugins/Utils/CMakeLists.txt−2
@@ -11,9 +11,7 @@ include_directories(
11)
11)
12
12
13set(QMLPLUGIN_SRC
13set(QMLPLUGIN_SRC
14− albumartprovider.cpp
15 bottombarvisibilitycommunicatorshell.cpp
14 bottombarvisibilitycommunicatorshell.cpp
16− mediaartcache.cpp
17 qlimitproxymodelqml.cpp
15 qlimitproxymodelqml.cpp
18 qsortfilterproxymodelqml.cpp
16 qsortfilterproxymodelqml.cpp
19 timeformatter.cpp
17 timeformatter.cpp
plugins/Utils/albumartprovider.cppdeleted−236
@@ -1,236 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− *
16− * Authors: Jussi Pakkanen <jussi.pakkanen@canonical.com>
17− * Pawel Stolowski <pawel.stolowski@canonical.com>
18−*/
19−
20−#include "albumartprovider.h"
21−#include <QString>
22−#include <QNetworkAccessManager>
23−#include <QEventLoop>
24−#include <QNetworkRequest>
25−#include <QNetworkReply>
26−#include <QXmlQuery>
27−#include <QTemporaryFile>
28−#include <QBuffer>
29−#include <QDebug>
30−#include <QFile>
31−#include <QStringList>
32−#include <QByteArray>
33−#include <QImage>
34−#include <QUrl>
35−#include <QUrlQuery>
36−#include <QtConcurrent>
37−
38−using namespace std;
39−
40−const std::string AlbumArtProvider::DEFAULT_ALBUM_ART = "/usr/share/unity/icons/album_missing.png";
41−const std::string AlbumArtProvider::UNITY_LENS_SCHEMA = "com.canonical.Unity.Lenses";
42−
43−static QByteArray download(QString url) {
44− QScopedPointer<QNetworkAccessManager> am(new QNetworkAccessManager);
45− QNetworkReply *reply = am->get(QNetworkRequest(QUrl(url)));
46− QEventLoop loop;
47− QObject::connect(am.data(), &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
48− loop.exec();
49−
50− if (reply->error() != QNetworkReply::NoError) {
51− qWarning() << "Error downloading from the network:" << reply->errorString();
52− return QByteArray();
53− }
54− return reply->readAll();
55−}
56−
57−AlbumArtProvider::AlbumArtProvider()
58− : QQuickImageProvider(QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading),
59− m_settings(nullptr)
60−{
61− auto schemas = g_settings_list_schemas();
62− if (schemas) {
63− for (int i = 0; schemas[i]; i++) {
64− if (g_strcmp0(schemas[i], UNITY_LENS_SCHEMA.c_str()) == 0) {
65− m_settings = g_settings_new(UNITY_LENS_SCHEMA.c_str());
66− break;
67− }
68− }
69− }
70− if (m_settings == nullptr) {
71− qWarning() << "Missing" << QString::fromStdString(UNITY_LENS_SCHEMA) << "schema";
72− }
73−}
74−
75−AlbumArtProvider::~AlbumArtProvider()
76−{
77− if (m_settings)
78− g_object_unref(m_settings);
79−}
80−
81−std::string AlbumArtProvider::get_lastfm_url(const albuminfo &ai) {
82− QString artist = QString::fromStdString(ai.artist);
83− QString album = QString::fromStdString(ai.album);
84− // audioscrobbler wants it without any / (as it would break the URL)
85− artist.remove('/');
86− album.remove('/');
87−
88− /// @todo: this is the old API which will probably get axed at some point in the future
89− /// The new 2.0 API requires an API key, but supports JSON output, etc, so switching
90− /// to it should be done ASAP.
91− QString request = QString("http://ws.audioscrobbler.com/1.0/album/%1/%2/info.xml").arg(artist)
92− .arg(album)
93− .toHtmlEscaped();
94− QFuture<QByteArray> future = QtConcurrent::run(download, request);
95− QByteArray arr = future.result();
96− if(arr.size() == 0) {
97− return "";
98− }
99− QXmlQuery query;
100− QBuffer tmp;
101− tmp.setData(arr);
102− tmp.open(QIODevice::ReadOnly);
103− query.bindVariable("reply", &tmp);
104− query.setQuery("doc($reply)/album/coverart/large[1]/text()");
105− QString image;
106− query.evaluateTo(&image);
107− image = image.trimmed();
108−
109− // return empty if last.fm tries to offer their default
110− // fallback artwork
111− if (image.contains("catalogue/noimage")) {
112− return "";
113− }
114−
115− return image.toStdString();
116−}
117−
118−bool AlbumArtProvider::download_and_store(const std::string &image_url, const std::string &output_file) {
119− QString url = QString::fromStdString(image_url);
120− QString fileName = QString::fromStdString(output_file);
121− QFuture<QByteArray> future = QtConcurrent::run(download, url);
122− QByteArray arr = future.result();
123− if (arr.size() == 0) {
124− return false;
125− }
126− QFile file(fileName);
127− if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
128− qWarning() << "Could not open file for writing:" << file.errorString();
129− return false;
130− }
131− if (file.write(arr) == -1){
132− qWarning() << "Could not write the image:" << file.error();
133− return false;
134− }
135−
136− return true;
137−}
138−
139−void AlbumArtProvider::fix_format(const std::string &fname) {
140− // MediaArtSpec requires jpg. Convert to it if necessary.
141− FILE *f = fopen(fname.c_str(), "r");
142− if(!f)
143− return;
144− unsigned char buf[2];
145− fread(buf, 1, 2, f);
146− fclose(f);
147− if(buf[0] == 0xff && buf[1] == 0xd8) {
148− return;
149− }
150− QImage im(fname.c_str());
151− im.save(fname.c_str(), "JPEG");
152−}
153−
154−std::string AlbumArtProvider::get_image(const std::string &artist, const std::string &album) {
155− albuminfo info;
156−
157− info.artist = artist;
158− info.album = album;
159−
160− if(info.album.empty() || info.artist.empty()) {
161− return DEFAULT_ALBUM_ART;
162− }
163− if(cache.has_art(info.artist, info.album)) {
164− // Image may have expired from cache between these two lines.
165− // It might expire before we return. C'est la vie.
166− return cache.get_art_file(info.artist, info.album);
167− }
168−
169− if (m_settings != nullptr && g_strcmp0(g_settings_get_string(m_settings, "remote-content-search"), "all") !=0) {
170− qDebug() << "Remote content disabled";
171− return DEFAULT_ALBUM_ART;
172− }
173−
174− std::string image_url = get_lastfm_url(info);
175− if(image_url.empty()) {
176− return DEFAULT_ALBUM_ART;
177− }
178− QTemporaryFile tempFile;
179− tempFile.open();
180− tempFile.setAutoRemove(true);
181− QString fname = tempFile.fileName();
182− std::string tmpname = fname.toUtf8().data();
183−
184− if(!download_and_store(image_url, tmpname)) {
185− return DEFAULT_ALBUM_ART;
186− }
187− fix_format(tmpname.c_str());
188− QFile f(fname);
189− if(!f.open(QIODevice::ReadWrite)) {
190− return DEFAULT_ALBUM_ART;
191− }
192− QByteArray arr = f.readAll();
193− f.close();
194−
195− cache.add_art(info.artist, info.album, arr.data(), arr.size());
196− try {
197− std::string res = cache.get_art_file(info.artist, info.album);
198− if(res.empty())
199− return DEFAULT_ALBUM_ART;
200− return res;
201− } catch(...) {
202− }
203− return DEFAULT_ALBUM_ART;
204−}
205−
206−QImage AlbumArtProvider::requestImage(const QString &id, QSize *realSize, const QSize &requestedSize) {
207− Q_UNUSED(requestedSize)
208−
209− QUrlQuery query(id);
210− if (!query.hasQueryItem(QStringLiteral("artist")) || !query.hasQueryItem(QStringLiteral("album"))) {
211− qWarning() << "Invalid albumart uri:" << id;
212− return QImage(QString::fromStdString(DEFAULT_ALBUM_ART));
213− }
214− const QString artist = query.queryItemValue(QStringLiteral("artist"), QUrl::FullyEncoded);
215− const QString album = query.queryItemValue(QStringLiteral("album"), QUrl::FullyEncoded);
216−
217− std::string tgt_path;
218− try {
219− tgt_path = get_image(artist.toStdString(), album.toStdString());
220− if(!tgt_path.empty()) {
221− QImage image;
222− image.load(QString::fromStdString(tgt_path));
223− // FIXME: Rescale to requested size preserving aspect.
224− *realSize = image.size();
225− return image;
226− }
227− } catch(std::exception &e) {
228− qDebug() << "Album art loader failed: " << e.what();
229− } catch(...) {
230− qDebug() << "Unknown error when generating image.";
231− }
232−
233− QImage fallback(QString::fromStdString(DEFAULT_ALBUM_ART));
234− *realSize = fallback.size();
235− return fallback;
236−}
plugins/Utils/albumartprovider.hdeleted−56
@@ -1,56 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− *
16− * Authors: Jussi Pakkanen <jussi.pakkanen@canonical.com>
17−*/
18−
19−#ifndef ALBUMART_PROVIDER_H_
20−#define ALBUMART_PROVIDER_H_
21−
22−#include <string>
23−#include <stdexcept>
24−#include <QQuickImageProvider>
25−#include <gio/gio.h>
26−
27−#include "mediaartcache.h"
28−
29−struct albuminfo {
30− std::string artist;
31− std::string album;
32−};
33−
34−class AlbumArtProvider : public QQuickImageProvider
35−{
36−public:
37− AlbumArtProvider();
38− ~AlbumArtProvider();
39− QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
40−
41− std::string get_image(const std::string &artist, const std::string &album);
42−
43− const static std::string DEFAULT_ALBUM_ART;
44− const static std::string UNITY_LENS_SCHEMA;
45−private:
46− MediaArtCache cache;
47− GSettings *m_settings;
48−
49− void fix_format(const std::string &fname);
50−
51− std::string get_lastfm_url(const albuminfo &ai);
52−
53− bool download_and_store(const std::string &image_url, const std::string &output_file);
54−};
55−
56−#endif
plugins/Utils/mediaartcache.cppdeleted−178
@@ -1,178 +0,0 @@
1−/*
2− * This file is part of the Ubuntu TV Media Scanner
3− * Copyright (C) 2012-2013 Canonical Ltd.
4− *
5− * This program is free software: you can redistribute it and/or modify
6− * it under the terms of the GNU Lesser General Public License version 3 as
7− * published by the Free Software Foundation.
8− *
9− * This program is distributed in the hope that it will be useful,
10− * but WITHOUT ANY WARRANTY; without even the implied warranty of
11− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12− * GNU Lesser General Public License for more details.
13− *
14− * You should have received a copy of the GNU Lesser General Public License
15− * along with this program. If not, see <http://www.gnu.org/licenses/>.
16− *
17− * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>
18− */
19−
20−#include"mediaartcache.h"
21−
22−#include<dirent.h>
23−#include<sys/stat.h>
24−#include<fcntl.h>
25−#include<unistd.h>
26−#include<errno.h>
27−#include<stdio.h>
28−#include<string.h>
29−#include<stdlib.h>
30−#include<assert.h>
31−#include<utime.h>
32−
33−#include<glib.h>
34−
35−#include<vector>
36−#include<algorithm>
37−#include<stdexcept>
38−
39−using namespace std;
40−
41−static string md5(const string &str) {
42− const unsigned char *buf = (const unsigned char *)str.c_str();
43− char *normalized = g_utf8_normalize((const gchar*)buf, str.size(), G_NORMALIZE_ALL);
44− string final;
45− gchar *result;
46−
47− if(normalized) {
48− buf = (const unsigned char*)normalized;
49− }
50− gssize bytes = str.length();
51−
52− result = g_compute_checksum_for_data(G_CHECKSUM_MD5, buf, bytes);
53− final = result;
54− g_free((gpointer)normalized);
55− g_free(result);
56− return final;
57−}
58−
59−MediaArtCache::MediaArtCache() {
60− string xdg_base = g_get_user_cache_dir();
61−
62− if (xdg_base == "") {
63− string s("Could not determine cache dir.");
64− throw runtime_error(s);
65− }
66− int ec = mkdir(xdg_base.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
67− if (ec < 0 && errno != EEXIST) {
68− string s("Could not create base dir.");
69− throw runtime_error(s);
70− }
71− root_dir = xdg_base + "/media-art";
72− ec = mkdir(root_dir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
73− if (ec < 0 && errno != EEXIST) {
74− string s("Could not create cache dir.");
75− throw runtime_error(s);
76− }
77−}
78−
79−bool MediaArtCache::has_art(const std::string &artist, const std::string &album) const {
80− string fname = get_art_file(artist, album);
81− return access(fname.c_str(), R_OK) == 0;
82−}
83−
84−void MediaArtCache::add_art(const std::string &artist, const std::string &album,
85− char *data, unsigned int datalen) {
86− string abs_fname = get_full_filename(artist, album);
87− GError *err = nullptr;
88− if(!g_file_set_contents(abs_fname.c_str(), data, datalen, &err)) {
89− string e("Could not write file ");
90− e += abs_fname;
91− e += ": ";
92− e += err->message;
93− g_error_free(err);
94− throw runtime_error(e);
95− }
96−}
97−
98−string MediaArtCache::get_art_file(const std::string &artist, const std::string &album) const {
99− string abs_fname = get_full_filename(artist, album);
100−
101− if (access(abs_fname.c_str(), R_OK) == 0) {
102− utime(abs_fname.c_str(), nullptr); // update access times to current time
103− return abs_fname;
104− }
105− return "";
106−}
107−
108−std::string MediaArtCache::get_full_filename(const std::string &artist, const std::string & album) const {
109− return root_dir + "/" + compute_base_name(artist, album);
110−}
111−
112−
113−std::string MediaArtCache::compute_base_name(const std::string &artist, const std::string &album) const {
114− string type = "album";
115− string h1 = md5(artist);
116− string h2 = md5(album);
117− return type + "-" + h1 + "-" + h2 + ".jpg";
118−}
119−
120−
121−void MediaArtCache::clear() const {
122− DIR *d = opendir(root_dir.c_str());
123− if(!d) {
124− string s = "Something went wrong.";
125− throw runtime_error(s);
126− }
127− struct dirent *entry, *de;
128− entry = (dirent*)malloc(sizeof(dirent) + NAME_MAX);
129− while(readdir_r(d, entry, &de) == 0 && de) {
130− string basename = entry->d_name;
131− if (basename == "." || basename == "..")
132− continue;
133− string fname = root_dir + "/" + basename;
134− if(remove(fname.c_str()) < 0) {
135− // This is not really an error worth
136− // halting everything for.
137− fprintf(stderr, "Could not delete file %s: %s.\n", fname.c_str(),
138− strerror(errno));
139− }
140− }
141− free(entry);
142− closedir(d);
143−}
144−
145−void MediaArtCache::prune() {
146− vector<pair<double, string>> mtimes;
147− DIR *d = opendir(root_dir.c_str());
148− if(!d) {
149− string s = "Something went wrong.";
150− throw runtime_error(s);
151− }
152− struct dirent *entry, *de;
153− entry = (dirent*)malloc(sizeof(dirent) + NAME_MAX);
154− while(readdir_r(d, entry, &de) == 0 && de) {
155− string basename = entry->d_name;
156− if (basename == "." || basename == "..")
157− continue;
158− string fname = root_dir + "/" + basename;
159− struct stat sbuf;
160− if(stat(fname.c_str(), &sbuf) != 0) {
161− continue;
162− }
163− // Use mtime because atime is not guaranteed to work if, for example
164− // the filesystem is mounted with noatime or relatime.
165− mtimes.push_back(make_pair(sbuf.st_mtim.tv_sec + sbuf.st_mtim.tv_nsec/1000000000.0, fname));
166− }
167− free(entry);
168− closedir(d);
169− if (mtimes.size() <= MAX_SIZE)
170− return;
171− sort(mtimes.begin(), mtimes.end());
172− for(size_t i=0; i < mtimes.size()-MAX_SIZE; i++) {
173− if(remove(mtimes[i].second.c_str()) < 0) {
174− fprintf(stderr, "Could not remove file %s: %s.\n",
175− mtimes[i].second.c_str(), strerror(errno));
176− }
177− }
178−}
plugins/Utils/mediaartcache.hdeleted−54
@@ -1,54 +0,0 @@
1−/*
2− * This file is part of the Ubuntu TV Media Scanner
3− * Copyright (C) 2012-2013 Canonical Ltd.
4− *
5− * This program is free software: you can redistribute it and/or modify
6− * it under the terms of the GNU Lesser General Public License version 3 as
7− * published by the Free Software Foundation.
8− *
9− * This program is distributed in the hope that it will be useful,
10− * but WITHOUT ANY WARRANTY; without even the implied warranty of
11− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12− * GNU Lesser General Public License for more details.
13− *
14− * You should have received a copy of the GNU Lesser General Public License
15− * along with this program. If not, see <http://www.gnu.org/licenses/>.
16− *
17− * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>
18− */
19−
20−#ifndef MEDIAARTCACHE_H
21−#define MEDIAARTCACHE_H
22−
23−#include<string>
24−
25−/*
26− * A class to store thumbnails for files according to
27− * https://wiki.gnome.org/MediaArtStorageSpec
28− *
29− * As this class deals mostly with the filesystem, all
30− * errors are reported with runtime_error exceptions.
31− */
32−
33−
34−class MediaArtCache {
35−private:
36− std::string root_dir;
37−
38− std::string compute_base_name(const std::string &artist, const std::string &album) const;
39− std::string get_full_filename(const std::string &artist, const std::string & album) const;
40−
41−public:
42− static const unsigned int MAX_SIZE = 200;
43−
44− MediaArtCache();
45− bool has_art(const std::string &artist, const std::string &album) const;
46− void add_art(const std::string &artist, const std::string &album,
47− char *data, unsigned int datalen);
48− std::string get_art_file(const std::string &artist, const std::string &album) const;
49− void clear() const;
50− void prune();
51− std::string get_cache_dir() const { return root_dir; }
52−};
53−
54−#endif
plugins/Utils/plugin.cpp−14
@@ -26,7 +26,6 @@
26#include "plugin.h"
26#include "plugin.h"
27
27
28// local
28// local
29−#include "albumartprovider.h"
30#include "bottombarvisibilitycommunicatorshell.h"
29#include "bottombarvisibilitycommunicatorshell.h"
31#include "qlimitproxymodelqml.h"
30#include "qlimitproxymodelqml.h"
32#include "qsortfilterproxymodelqml.h"
31#include "qsortfilterproxymodelqml.h"
@@ -52,19 +51,6 @@ void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
52{
51{
53 QQmlExtensionPlugin::initializeEngine(engine, uri);
52 QQmlExtensionPlugin::initializeEngine(engine, uri);
54
53
55− try
56− {
57− engine->addImageProvider(QLatin1String("albumart"), new AlbumArtProvider);
58− }
59− catch (const std::runtime_error &e)
60− {
61− qWarning() << "Failed to register image provider for albumart:" << e.what();
62− }
63− catch (...)
64− {
65− qWarning() << "Failed to register image provider for albumart (unknown error)";
66− }
67−
68 QDBusConnection::sessionBus().registerService(DBUS_SERVICE);
54 QDBusConnection::sessionBus().registerService(DBUS_SERVICE);
69 BottomBarVisibilityCommunicatorShell *bottomBarVisibilityCommunicator = &BottomBarVisibilityCommunicatorShell::instance();
55 BottomBarVisibilityCommunicatorShell *bottomBarVisibilityCommunicator = &BottomBarVisibilityCommunicatorShell::instance();
70 QDBusConnection::sessionBus().registerObject(BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH, bottomBarVisibilityCommunicator, QDBusConnection::ExportAllContents);
56 QDBusConnection::sessionBus().registerObject(BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH, bottomBarVisibilityCommunicator, QDBusConnection::ExportAllContents);