2
* Copyright (C) 2012-2014 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 version 3 as
6
* published by the Free Software Foundation.
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
* Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>
19
#include<internal/mediaartcache.h>
40
static string md5(const string &str) {
41
const unsigned char *buf = (const unsigned char *)str.c_str();
42
char *normalized = g_utf8_normalize((const gchar*)buf, str.size(), G_NORMALIZE_ALL);
47
buf = (const unsigned char*)normalized;
50
result = g_compute_checksum_for_data(G_CHECKSUM_MD5, buf, strlen((const char*)buf));
52
g_free((gpointer)normalized);
57
MediaArtCache::MediaArtCache() {
58
string xdg_base = g_get_user_cache_dir();
61
string s("Could not determine cache dir.");
62
throw runtime_error(s);
64
int ec = mkdir(xdg_base.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
65
if (ec < 0 && errno != EEXIST) {
66
string s("Could not create base dir.");
67
throw runtime_error(s);
69
root_dir = xdg_base + "/media-art";
70
ec = mkdir(root_dir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
71
if (ec < 0 && errno != EEXIST) {
72
string s("Could not create cache dir.");
73
throw runtime_error(s);
77
bool MediaArtCache::has_art(const std::string &artist, const std::string &album) const {
78
string fname = get_art_file(artist, album);
79
return access(fname.c_str(), R_OK) == 0;
82
void MediaArtCache::add_art(const std::string &artist, const std::string &album,
83
const char *data, unsigned int datalen) {
84
string abs_fname = get_full_filename(artist, album);
85
GError *err = nullptr;
86
if(!g_file_set_contents(abs_fname.c_str(), data, datalen, &err)) {
87
string e("Could not write file ");
92
throw runtime_error(e);
96
string MediaArtCache::get_art_file(const std::string &artist, const std::string &album) const {
97
string abs_fname = get_full_filename(artist, album);
99
if (access(abs_fname.c_str(), R_OK) == 0) {
100
utime(abs_fname.c_str(), nullptr); // update access times to current time
106
string MediaArtCache::get_art_uri(const string &artist, const string &album) const
108
std::string filename = get_art_file(artist, album);
110
if (filename == "") {
114
GError *err = nullptr;
115
char *c_uri = g_filename_to_uri(filename.c_str(), "", &err);
117
string e("Could not convert file name ");
122
throw runtime_error(e);
125
std::string uri = c_uri;
130
std::string MediaArtCache::get_full_filename(const std::string &artist, const std::string & album) const {
131
return root_dir + "/" + compute_base_name(artist, album);
135
std::string MediaArtCache::compute_base_name(const std::string &artist, const std::string &album) const {
136
string type = "album";
137
string h1 = md5(artist);
138
string h2 = md5(album);
139
return type + "-" + h1 + "-" + h2 + ".jpg";
143
void MediaArtCache::clear() const {
144
DIR *d = opendir(root_dir.c_str());
146
string s = "Something went wrong.";
147
throw runtime_error(s);
149
struct dirent *entry, *de;
150
entry = (dirent*)malloc(sizeof(dirent) + NAME_MAX + 1);
151
while(readdir_r(d, entry, &de) == 0 && de) {
152
string basename = entry->d_name;
153
if (basename == "." || basename == "..")
155
string fname = root_dir + "/" + basename;
156
if(remove(fname.c_str()) < 0) {
157
// This is not really an error worth
158
// halting everything for.
159
fprintf(stderr, "Could not delete file %s: %s.\n", fname.c_str(),
167
void MediaArtCache::prune() {
168
vector<pair<double, string>> mtimes;
169
DIR *d = opendir(root_dir.c_str());
171
string s = "Something went wrong.";
172
throw runtime_error(s);
174
struct dirent *entry, *de;
175
entry = (dirent*)malloc(sizeof(dirent) + NAME_MAX + 1);
176
while(readdir_r(d, entry, &de) == 0 && de) {
177
string basename = entry->d_name;
178
if (basename == "." || basename == "..")
180
string fname = root_dir + "/" + basename;
182
if(stat(fname.c_str(), &sbuf) != 0) {
185
// Use mtime because atime is not guaranteed to work if, for example
186
// the filesystem is mounted with noatime or relatime.
187
mtimes.push_back(make_pair(sbuf.st_mtim.tv_sec + sbuf.st_mtim.tv_nsec/1000000000.0, fname));
191
if (mtimes.size() <= MAX_SIZE)
193
sort(mtimes.begin(), mtimes.end());
194
for(size_t i=0; i < mtimes.size()-MAX_SIZE; i++) {
195
if(remove(mtimes[i].second.c_str()) < 0) {
196
fprintf(stderr, "Could not remove file %s: %s.\n",
197
mtimes[i].second.c_str(), strerror(errno));