on 71269bb help sign in
unity8/plugins/Utils/mediaartcache.cpp
178 lines 5.6 kB history raw
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 */
20#include"mediaartcache.h"
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>
33#include<glib.h>
35#include<vector>
36#include<algorithm>
37#include<stdexcept>
39using namespace std;
41static 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;
47 if(normalized) {
48 buf = (const unsigned char*)normalized;
49 }
50 gssize bytes = str.length();
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;
59MediaArtCache::MediaArtCache() {
60 string xdg_base = g_get_user_cache_dir();
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 }
79bool 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;
84void 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 }
98string MediaArtCache::get_art_file(const std::string &artist, const std::string &album) const {
99 string abs_fname = get_full_filename(artist, album);
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 "";
108std::string MediaArtCache::get_full_filename(const std::string &artist, const std::string & album) const {
109 return root_dir + "/" + compute_base_name(artist, album);
113std::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";
121void 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);
145void 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 }