on xenial help sign in
Added albumart image provider
commita6bd0858a0c9f23c7a2b705f76f2521cbf369183 authorMichal Hruby <michal.mhr@gmail.com> dateMon 7 Oct 2013, 23:32 (13 years ago) parent6abd10c
11 files changed+589−6
plugins/Unity/categoryresults.cpp+2−1 plugins/Unity/iconutils.cpp+3−2 plugins/Unity/iconutils.h+1−1 plugins/Unity/preview.cpp+1−1 plugins/Utils/CMakeLists.txt+12−1 plugins/Utils/albumartprovider.cpp+275 plugins/Utils/albumartprovider.h+53 plugins/Utils/mediaartcache.cpp+179 plugins/Utils/mediaartcache.h+54 plugins/Utils/plugin.cpp+8 plugins/Utils/plugin.h+1
plugins/Unity/categoryresults.cpp+2−1
@@ -81,8 +81,9 @@ CategoryResults::data(const QModelIndex& index, int role) const
8181 case RoleIconHint: {
8282 QString giconString(DeeListModel::data(index, ResultsColumn::ICON_HINT).toString());
8383 if (giconString.isEmpty()) {
84+ QString mimetype(DeeListModel::data(index, ResultsColumn::MIMETYPE).toString());
8485 QString uri(DeeListModel::data(index, ResultsColumn::URI).toString());
85− QString thumbnailerUri(uriToThumbnailerProviderString(uri));
86+ QString thumbnailerUri(uriToThumbnailerProviderString(uri, mimetype));
8687 if (!thumbnailerUri.isNull()) {
8788 return QVariant::fromValue(thumbnailerUri);
8889 }
plugins/Unity/iconutils.cpp+3−2
@@ -25,6 +25,7 @@
2525
2626#define BASE_THEME_ICON_URI "image://theme/"
2727#define BASE_THUMBNAILER_URI "image://thumbnailer/"
28+#define BASE_ALBUMART_URI "image://albumart/"
2829
2930QString gIconToDeclarativeImageProviderString(QString const &giconString)
3031{
@@ -91,10 +92,10 @@ QString gIconToDeclarativeImageProviderString(QString const &giconString)
9192 return result;
9293}
9394
94−QString uriToThumbnailerProviderString(QString const &uri)
95+QString uriToThumbnailerProviderString(QString const &uri, QString const &mimetype)
9596{
9697 if (uri.startsWith(QLatin1String("file:///"))) {
97− QString thumbnailerUri(BASE_THUMBNAILER_URI);
98+ QString thumbnailerUri(mimetype.startsWith(QLatin1String("audio/")) ? BASE_ALBUMART_URI : BASE_THUMBNAILER_URI);
9899 thumbnailerUri.append(uri.midRef(7));
99100 return thumbnailerUri;
100101 }
plugins/Unity/iconutils.h+1−1
@@ -23,6 +23,6 @@
2323#include <QString>
2424
2525QString gIconToDeclarativeImageProviderString(QString const &giconString);
26−QString uriToThumbnailerProviderString(QString const &uri);
26+QString uriToThumbnailerProviderString(QString const &uri, QString const &mimetype);
2727
2828#endif
plugins/Unity/preview.cpp+1−1
@@ -113,7 +113,7 @@ QString Preview::image() const
113113 return result;
114114 } else {
115115 QString sourceMedia(QString::fromStdString(m_unityPreview->image_source_uri()));
116− QString thumbnailUri(uriToThumbnailerProviderString(sourceMedia));
116+ QString thumbnailUri(uriToThumbnailerProviderString(sourceMedia, m_result->mimeType()));
117117 if (!thumbnailUri.isNull()) return thumbnailUri;
118118 }
119119 } else {
plugins/Utils/CMakeLists.txt+12−1
@@ -2,10 +2,17 @@ include_directories(
22 ${CMAKE_CURRENT_SOURCE_DIR}
33 ${CMAKE_CURRENT_BINARY_DIR}
44 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
5+ ${GST_INCLUDE_DIRS}
56)
67
8+# Dependencies
9+include(FindPkgConfig)
10+pkg_search_module(GST gstreamer-1.0 REQUIRED)
11+
712set(QMLPLUGIN_SRC
13+ albumartprovider.cpp
814 applicationpaths.h
15+ mediaartcache.cpp
916 qlimitproxymodelqml.cpp
1017 qsortfilterproxymodelqml.cpp
1118 ubuntuwindow.cpp
@@ -16,13 +23,17 @@ set(QMLPLUGIN_SRC
1623add_library(Utils-qml SHARED
1724 ${QMLPLUGIN_SRC}
1825 )
26+
27+target_link_libraries(Utils-qml
28+ ${GST_LDFLAGS}
29+ )
1930# Because this is an internal support library, we want
2031# to expose all symbols in it. Consider changing this
2132# either to a static library or just using the
2233# files directly in targets.
2334set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
2435
25−qt5_use_modules(Utils-qml Qml Quick DBus)
36+qt5_use_modules(Utils-qml Qml Quick DBus Network XmlPatterns)
2637
2738# export the qmldir qmltypes and plugin files
2839export_qmlfiles(Utils Utils)
plugins/Utils/albumartprovider.cppnew file+275
@@ -0,0 +1,275 @@
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 <QBuffer>
28+#include<cstdio>
29+#include<QDebug>
30+#include<QFile>
31+#include<QByteArray>
32+#include<gst/gst.h>
33+#include<unistd.h>
34+#include<memory>
35+#include<QImage>
36+
37+using namespace std;
38+
39+static void on_new_pad (GstElement * /*dec*/, GstPad * pad, GstElement * fakesink) {
40+ GstPad *sinkpad;
41+
42+ sinkpad = gst_element_get_static_pad (fakesink, "sink");
43+ if (!gst_pad_is_linked (sinkpad)) {
44+ if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
45+ g_error ("Failed to link pads!");
46+ }
47+ gst_object_unref (sinkpad);
48+}
49+
50+static void process_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data) {
51+ struct albuminfo *md = (struct albuminfo*) user_data;
52+ int i, num;
53+ string tagname(tag);
54+
55+ num = gst_tag_list_get_tag_size (list, tag);
56+ for (i = 0; i < num; ++i) {
57+ gchar *val;
58+ if (gst_tag_get_type(tag) == G_TYPE_STRING) {
59+ if (gst_tag_list_get_string_index (list, tag, i, &val)) {
60+ if(tagname == "artist")
61+ md->artist = std::string(val);
62+ if(tagname == "album")
63+ md->album = std::string(val);
64+ }}
65+ }
66+}
67+
68+albuminfo AlbumArtProvider::get_album_info(const std::string &filename) throw (runtime_error)
69+{
70+ albuminfo ai;
71+ // FIXME: Need to do quoting. Files with %'s in their names seem to confuse gstreamer.
72+
73+ const string uri = "file://" + filename;
74+
75+ GstElement *dec = gst_element_factory_make ("uridecodebin", NULL);
76+ if (dec == nullptr)
77+ throw runtime_error("Failed to create uridecodebin element");
78+
79+ GstElement *pipe = gst_pipeline_new ("pipeline");
80+
81+ g_object_set (dec, "uri", uri.c_str(), NULL);
82+ gst_bin_add (GST_BIN (pipe), dec);
83+
84+ GstElement *sink = gst_element_factory_make ("fakesink", NULL);
85+ gst_bin_add (GST_BIN (pipe), sink);
86+ g_signal_connect (dec, "pad-added", G_CALLBACK (on_new_pad), sink);
87+
88+ gst_element_set_state (pipe, GST_STATE_PAUSED);
89+
90+ GstMessage *msg;
91+ while (true) {
92+ GstTagList *tags = NULL;
93+
94+ msg = gst_bus_timed_pop_filtered(GST_ELEMENT_BUS (pipe),
95+ GST_CLOCK_TIME_NONE, (GstMessageType) (GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_TAG | GST_MESSAGE_ERROR));
96+
97+ if (GST_MESSAGE_TYPE (msg) != GST_MESSAGE_TAG) /* error or async_done */
98+ break;
99+
100+ gst_message_parse_tag (msg, &tags);
101+ gst_tag_list_foreach (tags, process_one_tag, &ai);
102+ gst_tag_list_unref (tags);
103+
104+ gst_message_unref (msg);
105+ };
106+
107+ gst_element_set_state (pipe, GST_STATE_NULL);
108+ gst_object_unref (pipe);
109+
110+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
111+ GError *err;
112+ gchar *dbg_info;
113+ gst_message_parse_error(msg, &err, &dbg_info);
114+ string errortxt(err->message);
115+ string dbtxt(dbg_info);
116+ g_error_free(err);
117+ g_free(dbg_info);
118+
119+ gst_message_unref (msg);
120+ string msg = "Extracting metadata of file ";
121+ msg += filename;
122+ msg += " failed: ";
123+ msg += errortxt;
124+ msg += " ";
125+ msg += dbg_info;
126+ throw runtime_error(msg);
127+ }
128+
129+ gst_message_unref (msg);
130+
131+ return ai;
132+}
133+
134+std::string AlbumArtProvider::get_lastfm_url(const albuminfo &ai) {
135+ QString artist = QString::fromStdString(ai.artist);
136+ QString album = QString::fromStdString(ai.album);
137+
138+ /// @todo: this is the old API which will probably get axed at some point in the future
139+ /// The new 2.0 API requires an API key, but supports JSON output, etc, so switching
140+ /// to it should be done ASAP.
141+ QString request = QString("http://ws.audioscrobbler.com/1.0/album/%1/%2/info.xml").arg(artist)
142+ .arg(album)
143+ .toHtmlEscaped();
144+ QScopedPointer<QNetworkAccessManager> am(new QNetworkAccessManager);
145+ QNetworkReply *reply = am->get(QNetworkRequest(QUrl(request)));
146+ QEventLoop loop;
147+ QObject::connect(am.data(), &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
148+ loop.exec();
149+
150+ if (reply->error() != QNetworkReply::NoError) {
151+ qDebug() << "Error getting the XML:" << reply->errorString();
152+ return "";
153+ }
154+
155+ QXmlQuery query;
156+ QBuffer tmp;
157+ tmp.setData(reply->readAll());
158+ tmp.open(QIODevice::ReadOnly);
159+ query.bindVariable("reply", &tmp);
160+ query.setQuery("doc($reply)/album/coverart/large[1]/text()");
161+ QString image;
162+ query.evaluateTo(&image);
163+ image = image.trimmed();
164+
165+ return image.toStdString();
166+}
167+
168+bool AlbumArtProvider::download_and_store(const std::string &image_url, const std::string &output_file) {
169+ QString url = QString::fromStdString(image_url);
170+ QString fileName = QString::fromStdString(output_file);
171+
172+ QScopedPointer<QNetworkAccessManager> am(new QNetworkAccessManager);
173+ QNetworkReply *reply = am->get(QNetworkRequest(QUrl(url)));
174+ QEventLoop loop;
175+ QObject::connect(am.data(), &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
176+ loop.exec();
177+
178+ if (reply->error() != QNetworkReply::NoError) {
179+ qDebug() << "Error getting the image:" << reply->errorString();
180+ return false;
181+ }
182+
183+ QFile file(fileName);
184+ if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
185+ qDebug() << "Could not open file for writing:" << file.errorString();
186+ return false;
187+ }
188+ if (file.write(reply->readAll()) == -1){
189+ qDebug() << "Could not write the image:" << file.error();
190+ return false;
191+ }
192+
193+ return true;
194+}
195+
196+void AlbumArtProvider::fix_format(const std::string &fname) {
197+ // MediaArtSpec requires jpg. Convert to it if necessary.
198+ FILE *f = fopen(fname.c_str(), "r");
199+ if(!f)
200+ return;
201+ unsigned char buf[2];
202+ fread(buf, 1, 2, f);
203+ fclose(f);
204+ if(buf[0] == 0xff && buf[1] == 0xd8) {
205+ return;
206+ }
207+ QImage im(fname.c_str());
208+ im.save(fname.c_str(), "JPEG");
209+}
210+
211+std::string AlbumArtProvider::get_image(const std::string &filename) {
212+ albuminfo info;
213+ try {
214+ info = get_album_info(filename);
215+ } catch(std::exception &e) {
216+ qDebug() << "Could not determine album info: " << e.what();
217+ return "";
218+ }
219+ if(info.album.empty() || info.artist.empty()) {
220+ return "";
221+ }
222+ if(cache.has_art(info.artist, info.album)) {
223+ // Image may have expired from cache between these two lines.
224+ // It might expire before we return. C'est la vie.
225+ return cache.get_art_file(info.artist, info.album);
226+ }
227+ std::string image_url = get_lastfm_url(info);
228+ if(image_url.empty()) {
229+ return "";
230+ }
231+ char tmpname[] = "/tmp/path/to/some/file/somewhere/maybe.jpg";
232+ tmpnam(tmpname);
233+ //std::unique_ptr<char, int(*)(const char*)> deleter(tmpname, unlink);
234+ if(!download_and_store(image_url, tmpname)) {
235+ return "";
236+ }
237+ fix_format(tmpname);
238+ FILE *f = fopen(tmpname, "r");
239+ fseek(f, 0, SEEK_END);
240+ long s = ftell(f);
241+ fseek(f, 0, SEEK_SET);
242+ char *buf = new char[s];
243+ fread(buf, 1, s, f);
244+ fclose(f);
245+
246+ // Fixme, leaks buf if throws.
247+ cache.add_art(info.artist, info.album, buf, s);
248+ delete []buf;
249+ return cache.get_art_file(info.artist, info.album);
250+}
251+
252+QImage AlbumArtProvider::requestImage(const QString &id, QSize *realSize, const QSize &requestedSize) {
253+ Q_UNUSED(requestedSize)
254+
255+ std::string src_path(id.toUtf8().data());
256+ std::string tgt_path;
257+ try {
258+ tgt_path = get_image(src_path);
259+ if(!tgt_path.empty()) {
260+ QString tgt(tgt_path.c_str());
261+ QImage image;
262+ image.load(tgt);
263+ // FIXME: Rescale to requested size preserving aspect.
264+ *realSize = image.size();
265+ return image;
266+ }
267+ } catch(std::exception &e) {
268+ qDebug() << "Album art loader failed: " << e.what();
269+ } catch(...) {
270+ qDebug() << "Unknown error when generating image.";
271+ }
272+
273+ *realSize = QSize(0, 0);
274+ return QImage();
275+}
plugins/Utils/albumartprovider.hnew file+53
@@ -0,0 +1,53 @@
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+
26+#include "mediaartcache.h"
27+
28+struct albuminfo {
29+ std::string artist;
30+ std::string album;
31+};
32+
33+class AlbumArtProvider : public QQuickImageProvider
34+{
35+public:
36+ AlbumArtProvider() : QQuickImageProvider(QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading) {}
37+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
38+
39+ std::string get_image(const std::string &filename);
40+
41+private:
42+ MediaArtCache cache;
43+
44+ void fix_format(const std::string &fname);
45+
46+ albuminfo get_album_info(const std::string &filename) throw (std::runtime_error);
47+
48+ std::string get_lastfm_url(const albuminfo &ai);
49+
50+ bool download_and_store(const std::string &image_url, const std::string &output_file);
51+};
52+
53+#endif
plugins/Utils/mediaartcache.cppnew file+179
@@ -0,0 +1,179 @@
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+}
179+
plugins/Utils/mediaartcache.hnew file+54
@@ -0,0 +1,54 @@
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+8
@@ -24,6 +24,7 @@
2424#include "plugin.h"
2525
2626// local
27+#include "albumartprovider.h"
2728#include "applicationpaths.h"
2829#include "qlimitproxymodelqml.h"
2930#include "qsortfilterproxymodelqml.h"
@@ -46,3 +47,10 @@ void UtilsPlugin::registerTypes(const char *uri)
4647 qmlRegisterExtendedType<QQuickWindow, UbuntuWindow>(uri, 0, 1, "Window");
4748 qmlRegisterSingletonType<ApplicationPaths>(uri, 0, 1, "ApplicationPaths", applicationsPathsSingleton);
4849}
50+
51+void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
52+{
53+ QQmlExtensionPlugin::initializeEngine(engine, uri);
54+
55+ engine->addImageProvider(QLatin1String("albumart"), new AlbumArtProvider);
56+}
plugins/Utils/plugin.h+1
@@ -29,6 +29,7 @@ class UtilsPlugin : public QQmlExtensionPlugin
2929
3030public:
3131 void registerTypes(const char *uri);
32+ void initializeEngine(QQmlEngine *engine, const char *uri);
3233};
3334
3435#endif