MiniDLNA update: 1.0.19.1 to 1.0.20
[tomato.git] / release / src / router / minidlna / albumart.c
blob9ba5c8f81b74ee686c9f3b270420cf5a479c808e
1 /* MiniDLNA media server
2 * Copyright (C) 2008 Justin Maggard
4 * This file is part of MiniDLNA.
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * MiniDLNA is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <dirent.h>
23 #include <sys/stat.h>
24 #include <libgen.h>
25 #include <setjmp.h>
26 #include <errno.h>
28 #include <jpeglib.h>
30 #include "upnpglobalvars.h"
31 #include "albumart.h"
32 #include "sql.h"
33 #include "utils.h"
34 #include "image_utils.h"
35 #include "log.h"
37 int
38 art_cache_exists(const char * orig_path, char ** cache_file)
40 asprintf(cache_file, "%s/art_cache%s", db_path, orig_path);
41 strcpy(strchr(*cache_file, '\0')-4, ".jpg");
43 return (!access(*cache_file, F_OK));
46 char *
47 save_resized_album_art(image_s * imsrc, const char * path)
49 int dstw, dsth;
50 image_s * imdst;
51 char * cache_file;
52 char * cache_dir;
54 if( !imsrc )
55 return NULL;
57 if( art_cache_exists(path, &cache_file) )
58 return cache_file;
60 cache_dir = strdup(cache_file);
61 make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
62 free(cache_dir);
64 if( imsrc->width > imsrc->height )
66 dstw = 160;
67 dsth = (imsrc->height<<8) / ((imsrc->width<<8)/160);
69 else
71 dstw = (imsrc->width<<8) / ((imsrc->height<<8)/160);
72 dsth = 160;
74 imdst = image_resize(imsrc, dstw, dsth);
75 if( !imdst )
76 goto error;
78 if( image_save_to_jpeg_file(imdst, cache_file) == 0 )
80 image_free(imdst);
81 return cache_file;
83 error:
84 free(cache_file);
85 return NULL;
88 /* Simple, efficient hash function from Daniel J. Bernstein */
89 unsigned int DJBHash(const char * str, int len)
91 unsigned int hash = 5381;
92 unsigned int i = 0;
94 for(i = 0; i < len; str++, i++)
96 hash = ((hash << 5) + hash) + (*str);
99 return hash;
102 /* And our main album art functions */
103 void
104 update_if_album_art(const char * path)
106 char * dir;
107 char * match = NULL;
108 char * file = NULL;
109 int ncmp = 0;
110 int album_art;
111 DIR * dh;
112 struct dirent *dp;
113 enum file_types type = TYPE_UNKNOWN;
114 sqlite_int64 art_id = 0;
116 match = strdup(basename((char *)path));
117 /* Check if this file name matches a specific audio or video file */
118 if( ends_with(match, ".cover.jpg") )
120 ncmp = strlen(match)-10;
122 else
124 ncmp = strrchr(match, '.') - match;
126 /* Check if this file name matches one of the default album art names */
127 album_art = is_album_art(match);
129 dir = dirname(strdup(path));
130 dh = opendir(dir);
131 if( !dh )
132 return;
133 while ((dp = readdir(dh)) != NULL)
135 switch( dp->d_type )
137 case DT_REG:
138 type = TYPE_FILE;
139 break;
140 case DT_LNK:
141 case DT_UNKNOWN:
142 asprintf(&file, "%s/%s", dir, dp->d_name);
143 type = resolve_unknown_type(file, ALL_MEDIA);
144 free(file);
145 break;
146 default:
147 type = TYPE_UNKNOWN;
148 break;
150 if( type != TYPE_FILE )
151 continue;
152 if( (*(dp->d_name) != '.') &&
153 (is_video(dp->d_name) || is_audio(dp->d_name)) &&
154 (album_art || strncmp(dp->d_name, match, ncmp) == 0) )
156 DPRINTF(E_DEBUG, L_METADATA, "New file %s looks like cover art for %s\n", path, dp->d_name);
157 asprintf(&file, "%s/%s", dir, dp->d_name);
158 art_id = find_album_art(file, NULL, 0);
159 if( sql_exec(db, "UPDATE DETAILS set ALBUM_ART = %lld where PATH = '%q'", art_id, file) != SQLITE_OK )
160 DPRINTF(E_WARN, L_METADATA, "Error setting %s as cover art for %s\n", match, dp->d_name);
161 free(file);
164 closedir(dh);
166 free(dir);
167 free(match);
170 char *
171 check_embedded_art(const char * path, const char * image_data, int image_size)
173 int width = 0, height = 0;
174 char * art_path = NULL;
175 char * cache_dir;
176 FILE * dstfile;
177 image_s * imsrc;
178 static char last_path[PATH_MAX];
179 static unsigned int last_hash = 0;
180 static int last_success = 0;
181 unsigned int hash;
183 if( !image_data || !image_size || !path )
185 return NULL;
187 /* If the embedded image matches the embedded image from the last file we
188 * checked, just make a hard link. Better than storing it on the disk twice. */
189 hash = DJBHash(image_data, image_size);
190 if( hash == last_hash )
192 if( !last_success )
193 return NULL;
194 art_cache_exists(path, &art_path);
195 if( link(last_path, art_path) == 0 )
197 return(art_path);
199 else
201 if( errno == ENOENT )
203 cache_dir = strdup(art_path);
204 make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
205 free(cache_dir);
206 if( link(last_path, art_path) == 0 )
207 return(art_path);
209 DPRINTF(E_WARN, L_METADATA, "Linking %s to %s failed [%s]\n", art_path, last_path, strerror(errno));
210 free(art_path);
211 art_path = NULL;
214 last_hash = hash;
216 imsrc = image_new_from_jpeg(NULL, 0, image_data, image_size, 1);
217 if( !imsrc )
219 last_success = 0;
220 return NULL;
222 width = imsrc->width;
223 height = imsrc->height;
225 if( width > 160 || height > 160 )
227 art_path = save_resized_album_art(imsrc, path);
229 else if( width > 0 && height > 0 )
231 size_t nwritten;
232 if( art_cache_exists(path, &art_path) )
233 goto end_art;
234 cache_dir = strdup(art_path);
235 make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
236 free(cache_dir);
237 dstfile = fopen(art_path, "w");
238 if( !dstfile )
240 free(art_path);
241 art_path = NULL;
242 goto end_art;
244 nwritten = fwrite((void *)image_data, 1, image_size, dstfile);
245 fclose(dstfile);
246 if( nwritten != image_size )
248 DPRINTF(E_WARN, L_METADATA, "Embedded art error: wrote %d/%d bytes\n", nwritten, image_size);
249 remove(art_path);
250 free(art_path);
251 art_path = NULL;
252 goto end_art;
255 end_art:
256 image_free(imsrc);
257 if( !art_path )
259 DPRINTF(E_WARN, L_METADATA, "Invalid embedded album art in %s\n", basename((char *)path));
260 last_success = 0;
261 return NULL;
263 DPRINTF(E_DEBUG, L_METADATA, "Found new embedded album art in %s\n", basename((char *)path));
264 last_success = 1;
265 strcpy(last_path, art_path);
267 return(art_path);
270 char *
271 check_for_album_file(char * dir, const char * path)
273 char * file = malloc(PATH_MAX);
274 struct album_art_name_s * album_art_name;
275 image_s * imsrc = NULL;
276 int width=0, height=0;
277 char * art_file;
279 /* First look for file-specific cover art */
280 sprintf(file, "%s.cover.jpg", path);
281 if( access(file, R_OK) == 0 )
283 if( art_cache_exists(file, &art_file) )
284 goto existing_file;
285 free(art_file);
286 imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1);
287 if( imsrc )
288 goto found_file;
290 sprintf(file, "%s", path);
291 strip_ext(file);
292 strcat(file, ".jpg");
293 if( access(file, R_OK) == 0 )
295 if( art_cache_exists(file, &art_file) )
296 goto existing_file;
297 free(art_file);
298 imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1);
299 if( imsrc )
300 goto found_file;
303 /* Then fall back to possible generic cover art file names */
304 for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
306 sprintf(file, "%s/%s", dir, album_art_name->name);
307 if( access(file, R_OK) == 0 )
309 if( art_cache_exists(file, &art_file) )
311 existing_file:
312 free(file);
313 return art_file;
315 free(art_file);
316 imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1);
317 if( !imsrc )
318 continue;
319 found_file:
320 width = imsrc->width;
321 height = imsrc->height;
322 if( width > 160 || height > 160 )
324 art_file = file;
325 file = save_resized_album_art(imsrc, art_file);
326 free(art_file);
328 image_free(imsrc);
329 return(file);
332 free(file);
333 return NULL;
336 sqlite_int64
337 find_album_art(const char * path, const char * image_data, int image_size)
339 char * album_art = NULL;
340 char * sql;
341 char ** result;
342 int cols, rows;
343 sqlite_int64 ret = 0;
344 char * mypath = strdup(path);
346 if( (image_size && (album_art = check_embedded_art(path, image_data, image_size))) ||
347 (album_art = check_for_album_file(dirname(mypath), path)) )
349 sql = sqlite3_mprintf("SELECT ID from ALBUM_ART where PATH = '%q'", album_art ? album_art : path);
350 if( (sql_get_table(db, sql, &result, &rows, &cols) == SQLITE_OK) && rows )
352 ret = strtoll(result[1], NULL, 10);
354 else
356 if( sql_exec(db, "INSERT into ALBUM_ART (PATH) VALUES ('%q')", album_art) == SQLITE_OK )
357 ret = sqlite3_last_insert_rowid(db);
359 sqlite3_free_table(result);
360 sqlite3_free(sql);
362 if( album_art )
363 free(album_art);
364 free(mypath);
366 return ret;