source: titan/minidlna-1.0.22/albumart.c @ 13567

Last change on this file since 13567 was 13567, checked in by obi, 11 years ago

[titan] add minidlna-1.0.22 first step

File size: 8.7 KB
Line 
1/* MiniDLNA media server
2 * Copyright (C) 2008  Justin Maggard
3 *
4 * This file is part of MiniDLNA.
5 *
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.
9 *
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.
14 *
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/>.
17 */
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 <sys/param.h>
25#include <libgen.h>
26#include <setjmp.h>
27#include <errno.h>
28
29#include <jpeglib.h>
30
31#include "upnpglobalvars.h"
32#include "albumart.h"
33#include "sql.h"
34#include "utils.h"
35#include "image_utils.h"
36#include "log.h"
37
38int
39art_cache_exists(const char * orig_path, char ** cache_file)
40{
41        asprintf(cache_file, "%s/art_cache%s", db_path, orig_path);
42        strcpy(strchr(*cache_file, '\0')-4, ".jpg");
43
44        return (!access(*cache_file, F_OK));
45}
46
47char *
48save_resized_album_art(image_s * imsrc, const char * path)
49{
50        int dstw, dsth;
51        image_s * imdst;
52        char * cache_file;
53        char * cache_dir;
54
55        if( !imsrc )
56                return NULL;
57
58        if( art_cache_exists(path, &cache_file) )
59                return cache_file;
60
61        cache_dir = strdup(cache_file);
62        make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
63        free(cache_dir);
64
65        if( imsrc->width > imsrc->height )
66        {
67                dstw = 160;
68                dsth = (imsrc->height<<8) / ((imsrc->width<<8)/160);
69        }
70        else
71        {
72                dstw = (imsrc->width<<8) / ((imsrc->height<<8)/160);
73                dsth = 160;
74        }
75        imdst = image_resize(imsrc, dstw, dsth);
76        if( !imdst )
77                goto error;
78
79        if( image_save_to_jpeg_file(imdst, cache_file) == 0 )
80        {
81                image_free(imdst);
82                return cache_file;
83        }
84error:
85        free(cache_file);
86        return NULL;
87}
88
89/* Simple, efficient hash function from Daniel J. Bernstein */
90unsigned int DJBHash(const char * str, int len)
91{
92        unsigned int hash = 5381;
93        unsigned int i = 0;
94
95        for(i = 0; i < len; str++, i++)
96        {
97                hash = ((hash << 5) + hash) + (*str);
98        }
99
100        return hash;
101}
102
103/* And our main album art functions */
104void
105update_if_album_art(const char * path)
106{
107        char * dir;
108        char * match = NULL;
109        char * file = NULL;
110        int ncmp = 0;
111        int album_art;
112        DIR * dh;
113        struct dirent *dp;
114        enum file_types type = TYPE_UNKNOWN;
115        sqlite_int64 art_id = 0;
116
117        match = strdup(basename((char *)path));
118        /* Check if this file name matches a specific audio or video file */
119        if( ends_with(match, ".cover.jpg") )
120        {
121                ncmp = strlen(match)-10;
122        }
123        else
124        {
125                ncmp = strrchr(match, '.') - match;
126        }
127        /* Check if this file name matches one of the default album art names */
128        album_art = is_album_art(match);
129
130        dir = dirname(strdup(path));
131        dh = opendir(dir);
132        if( !dh )
133                return;
134        while ((dp = readdir(dh)) != NULL)
135        {
136                switch( dp->d_type )
137                {
138                        case DT_REG:
139                                type = TYPE_FILE;
140                                break;
141                        case DT_LNK:
142                        case DT_UNKNOWN:
143                                asprintf(&file, "%s/%s", dir, dp->d_name);
144                                type = resolve_unknown_type(file, ALL_MEDIA);
145                                free(file);
146                                break;
147                        default:
148                                type = TYPE_UNKNOWN;
149                                break;
150                }
151                if( type != TYPE_FILE )
152                        continue;
153                if( (*(dp->d_name) != '.') &&
154                    (is_video(dp->d_name) || is_audio(dp->d_name)) &&
155                    (album_art || strncmp(dp->d_name, match, ncmp) == 0) )
156                {
157                        DPRINTF(E_DEBUG, L_METADATA, "New file %s looks like cover art for %s\n", path, dp->d_name);
158                        asprintf(&file, "%s/%s", dir, dp->d_name);
159                        art_id = find_album_art(file, NULL, 0);
160                        if( sql_exec(db, "UPDATE DETAILS set ALBUM_ART = %lld where PATH = '%q'", art_id, file) != SQLITE_OK )
161                                DPRINTF(E_WARN, L_METADATA, "Error setting %s as cover art for %s\n", match, dp->d_name);
162                        free(file);
163                }
164        }
165        closedir(dh);
166       
167        free(dir);
168        free(match);
169}
170
171char *
172check_embedded_art(const char * path, const char * image_data, int image_size)
173{
174        int width = 0, height = 0;
175        char * art_path = NULL;
176        char * cache_dir;
177        FILE * dstfile;
178        image_s * imsrc;
179        static char last_path[PATH_MAX];
180        static unsigned int last_hash = 0;
181        static int last_success = 0;
182        unsigned int hash;
183
184        if( !image_data || !image_size || !path )
185        {
186                return NULL;
187        }
188        /* If the embedded image matches the embedded image from the last file we
189         * checked, just make a hard link.  Better than storing it on the disk twice. */
190        hash = DJBHash(image_data, image_size);
191        if( hash == last_hash )
192        {
193                if( !last_success )
194                        return NULL;
195                art_cache_exists(path, &art_path);
196                if( link(last_path, art_path) == 0 )
197                {
198                        return(art_path);
199                }
200                else
201                {
202                        if( errno == ENOENT )
203                        {
204                                cache_dir = strdup(art_path);
205                                make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
206                                free(cache_dir);
207                                if( link(last_path, art_path) == 0 )
208                                        return(art_path);
209                        }
210                        DPRINTF(E_WARN, L_METADATA, "Linking %s to %s failed [%s]\n", art_path, last_path, strerror(errno));
211                        free(art_path);
212                        art_path = NULL;
213                }
214        }
215        last_hash = hash;
216
217        imsrc = image_new_from_jpeg(NULL, 0, image_data, image_size, 1);
218        if( !imsrc )
219        {
220                last_success = 0;
221                return NULL;
222        }
223        width = imsrc->width;
224        height = imsrc->height;
225
226        if( width > 160 || height > 160 )
227        {
228                art_path = save_resized_album_art(imsrc, path);
229        }
230        else if( width > 0 && height > 0 )
231        {
232                size_t nwritten;
233                if( art_cache_exists(path, &art_path) )
234                        goto end_art;
235                cache_dir = strdup(art_path);
236                make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
237                free(cache_dir);
238                dstfile = fopen(art_path, "w");
239                if( !dstfile )
240                {
241                        free(art_path);
242                        art_path = NULL;
243                        goto end_art;
244                }
245                nwritten = fwrite((void *)image_data, 1, image_size, dstfile);
246                fclose(dstfile);
247                if( nwritten != image_size )
248                {
249                        DPRINTF(E_WARN, L_METADATA, "Embedded art error: wrote %d/%d bytes\n", nwritten, image_size);
250                        remove(art_path);
251                        free(art_path);
252                        art_path = NULL;
253                        goto end_art;
254                }
255        }
256end_art:
257        image_free(imsrc);
258        if( !art_path )
259        {
260                DPRINTF(E_WARN, L_METADATA, "Invalid embedded album art in %s\n", basename((char *)path));
261                last_success = 0;
262                return NULL;
263        }
264        DPRINTF(E_DEBUG, L_METADATA, "Found new embedded album art in %s\n", basename((char *)path));
265        last_success = 1;
266        strcpy(last_path, art_path);
267
268        return(art_path);
269}
270
271char *
272check_for_album_file(const char * dir, const char * path)
273{
274        char file[MAXPATHLEN];
275        struct album_art_name_s * album_art_name;
276        image_s * imsrc = NULL;
277        int width=0, height=0;
278        char * art_file;
279
280        /* First look for file-specific cover art */
281        snprintf(file, sizeof(file), "%s.cover.jpg", path);
282        if( access(file, R_OK) == 0 )
283        {
284                if( art_cache_exists(file, &art_file) )
285                        goto existing_file;
286                free(art_file);
287                imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1);
288                if( imsrc )
289                        goto found_file;
290        }
291        snprintf(file, sizeof(file), "%s", path);
292        art_file = strrchr(file, '.');
293        if( art_file )
294                strcpy(art_file, ".jpg");
295        if( access(file, R_OK) == 0 )
296        {
297                if( art_cache_exists(file, &art_file) )
298                        goto existing_file;
299                free(art_file);
300                imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1);
301                if( imsrc )
302                        goto found_file;
303        }
304
305        /* Then fall back to possible generic cover art file names */
306        for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
307        {
308                snprintf(file, sizeof(file), "%s/%s", dir, album_art_name->name);
309                if( access(file, R_OK) == 0 )
310                {
311                        if( art_cache_exists(file, &art_file) )
312                        {
313existing_file:
314                                return art_file;
315                        }
316                        free(art_file);
317                        imsrc = image_new_from_jpeg(file, 1, NULL, 0, 1);
318                        if( !imsrc )
319                                continue;
320found_file:
321                        width = imsrc->width;
322                        height = imsrc->height;
323                        if( width > 160 || height > 160 )
324                                art_file = save_resized_album_art(imsrc, file);
325                        else
326                                art_file = strdup(file);
327                        image_free(imsrc);
328                        return(art_file);
329                }
330        }
331        return NULL;
332}
333
334sqlite_int64
335find_album_art(const char * path, const char * image_data, int image_size)
336{
337        char * album_art = NULL;
338        char * sql;
339        char ** result;
340        int cols, rows;
341        sqlite_int64 ret = 0;
342        char * mypath;
343        const char * dir;
344        struct stat st;
345
346        if( stat(path, &st) == 0 && S_ISDIR(st.st_mode) )
347        {
348                mypath = NULL;
349                dir = path;
350        }
351        else
352        {
353                mypath = strdup(path);
354                dir = dirname(mypath);
355        }
356
357        if( (image_size && (album_art = check_embedded_art(path, image_data, image_size))) ||
358            (album_art = check_for_album_file(dir, path)) )
359        {
360                sql = sqlite3_mprintf("SELECT ID from ALBUM_ART where PATH = '%q'", album_art ? album_art : path);
361                if( (sql_get_table(db, sql, &result, &rows, &cols) == SQLITE_OK) && rows )
362                {
363                        ret = strtoll(result[1], NULL, 10);
364                }
365                else
366                {
367                        if( sql_exec(db, "INSERT into ALBUM_ART (PATH) VALUES ('%q')", album_art) == SQLITE_OK )
368                                ret = sqlite3_last_insert_rowid(db);
369                }
370                sqlite3_free_table(result);
371                sqlite3_free(sql);
372        }
373        free(album_art);
374        free(mypath);
375
376        return ret;
377}
Note: See TracBrowser for help on using the repository browser.