From 5413646e1cd0a8d469eeb727beabae0dcc0bd9ee Mon Sep 17 00:00:00 2001 From: Justin Maggard Date: Sun, 15 Feb 2009 21:32:48 +0000 Subject: [PATCH] * Downscale album art files to JPEG_TN specs and cache them if we find album art at a larger resolution. --- Makefile | 3 +- albumart.c | 100 +++++++++++++++++++++++++++++++++++++++++++++------ genconfig.sh | 2 +- inotify.c | 7 ++++ minidlna.c | 14 ++++++-- utils.c | 45 +++++++++++++++++++++++ utils.h | 3 ++ 7 files changed, 158 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 8257dbe..31ba17b 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,7 @@ BASEOBJS = minidlna.o upnphttp.o upnpdescgen.o upnpsoap.o \ ALLOBJS = $(BASEOBJS) $(LNXOBJS) -#LIBS = -liptc -LIBS = -lexif -ljpeg -ltag_c -lid3tag -lsqlite3 -lavformat -luuid #-lgd +LIBS = -lexif -ljpeg -ltag_c -lid3tag -lsqlite3 -lavformat -luuid -lgd TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o diff --git a/albumart.c b/albumart.c index 3197387..700927d 100644 --- a/albumart.c +++ b/albumart.c @@ -21,13 +21,16 @@ #include #include #include +#include #include #include #include +#include #include "upnpglobalvars.h" #include "sql.h" +#include "utils.h" /* For libjpeg error handling */ jmp_buf setjmp_buffer; @@ -56,6 +59,68 @@ check_res(int width, int height, char * dlna_pn) return 1; } +char * +save_resized_album_art(void * ptr, const char * path, int srcw, int srch, int file, int size) +{ + FILE *dstfile; + gdImagePtr imsrc = 0, imdst = 0; + int dstw, dsth; + char * cache_file; + char * cache_dir; + + asprintf(&cache_file, DB_PATH "/art_cache%s", path); + if( access(cache_file, F_OK) == 0 ) + return cache_file; + + cache_dir = strdup(cache_file); + make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); + free(cache_dir); + + if( file ) + imsrc = gdImageCreateFromJpeg((FILE *)ptr); + else + imsrc = gdImageCreateFromJpegPtr(size, ptr); + if( !imsrc ) + goto error; + + dstfile = fopen(cache_file, "w"); + if( !dstfile ) + goto error; + + if( srcw > srch ) + { + dstw = 160; + dsth = (srch<<8) / ((srcw<<8)/160); + } + else + { + dstw = (srcw<<8) / ((srch<<8)/160); + dsth = 160; + } + imdst = gdImageCreateTrueColor(dstw, dsth); + if( !imdst ) + { + gdImageDestroy(imsrc); + fclose(dstfile); + goto error; + } + #ifdef __sparc__ + gdImageCopyResized(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, imsrc->sx, imsrc->sy); + #else + gdImageCopyResampled(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, imsrc->sx, imsrc->sy); + #endif + gdImageJpeg(imdst, dstfile, -1); + fclose(dstfile); + gdImageDestroy(imsrc); + gdImageDestroy(imdst); + + return cache_file; +error: + free(cache_file); + return NULL; +} + + #ifdef HAVE_LIBID3TAG #include @@ -120,7 +185,7 @@ jpeg_memory_src(j_decompress_ptr cinfo, unsigned char const *buffer, size_t bufs } /* And our main album art functions */ -int +char * check_embedded_art(const char * path, char * dlna_pn) { struct id3_file *file; @@ -130,9 +195,10 @@ check_embedded_art(const char * path, char * dlna_pn) id3_latin1_t const *mime; id3_length_t length; int index; - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - int width = 0, height = 0; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + int width = 0, height = 0; + char * art_path = NULL; file = id3_file_open(path, ID3_FILE_MODE_READONLY); if( !file ) @@ -164,9 +230,17 @@ check_embedded_art(const char * path, char * dlna_pn) break; } } + if( width > 160 || height > 160 ) + { + art_path = save_resized_album_art((void *)image, path, width, height, 0, length); + } + else if( width > 0 && height > 0 ) + { + art_path = path; + } id3_file_close(file); - return( check_res(width, height, dlna_pn) ); + return(art_file); } #endif // HAVE_LIBID3TAG @@ -177,8 +251,9 @@ check_for_album_file(char * dir, char * dlna_pn) struct album_art_name_s * album_art_name; struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; - FILE *infile; + static FILE * infile; int width=0, height=0; + char * art_file; for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next ) { @@ -196,14 +271,18 @@ check_for_album_file(char * dir, char * dlna_pn) jpeg_start_decompress(&cinfo); width = cinfo.output_width; height = cinfo.output_height; + if( width > 160 || height > 160 ) + { + art_file = file; + rewind(infile); + file = save_resized_album_art((void *)infile, art_file, width, height, 1, 0); + free(art_file); + } error: jpeg_destroy_decompress(&cinfo); fclose(infile); - if( check_res(width, height, dlna_pn) ) - return(file); - else - return(NULL); + return(file); } } free(file); @@ -226,6 +305,7 @@ find_album_art(const char * path, char * dlna_pn) if( (album_art = check_for_album_file(dirname(mypath), dlna_pn)) ) #endif { + strcpy(dlna_pn, "JPEG_TN"); sql = sqlite3_mprintf("SELECT ID from ALBUM_ART where PATH = '%q'", album_art ? album_art : path); if( (sql_get_table(db, sql, &result, &rows, &cols) == SQLITE_OK) && rows ) { diff --git a/genconfig.sh b/genconfig.sh index 6213e75..64c04e8 100755 --- a/genconfig.sh +++ b/genconfig.sh @@ -15,7 +15,7 @@ UPNP_VERSION=20070827 # Facility to syslog LOG_MINIDLNA="LOG_DAEMON" # Database path -DB_PATH="/tmp/files.db" +DB_PATH="/tmp/minidlna" # detecting the OS name and version OS_NAME=`uname -s` diff --git a/inotify.c b/inotify.c index b74c8cf..41c26ab 100644 --- a/inotify.c +++ b/inotify.c @@ -404,6 +404,7 @@ int inotify_remove_file(const char * path) { char * sql; + char * art_cache; char **result; sqlite_int64 detailID = 0; int rows, ret = 1; @@ -428,6 +429,12 @@ inotify_remove_file(const char * path) sql_exec(db, sql); free(sql); } + asprintf(&art_cache, "%s/art_cache%s", DB_PATH, path); + if( access(art_cache, F_OK) == 0 ) + { + remove(art_cache); + } + free(art_cache); return ret; } diff --git a/minidlna.c b/minidlna.c index 264f65b..4ce84c4 100644 --- a/minidlna.c +++ b/minidlna.c @@ -37,6 +37,7 @@ #include "getifaddr.h" #include "upnpsoap.h" #include "options.h" +#include "utils.h" #include "minissdp.h" #include "minidlnatypes.h" #include "daemonize.h" @@ -606,7 +607,13 @@ main(int argc, char * * argv) LIST_INIT(&upnphttphead); - if( sqlite3_open(DB_PATH, &db) != SQLITE_OK ) + if( access(DB_PATH, F_OK) != 0 ) + { + char *db_path = strdup(DB_PATH); + make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); + free(db_path); + } + if( sqlite3_open(DB_PATH "/files.db", &db) != SQLITE_OK ) { fprintf(stderr, "ERROR: Failed to open sqlite database! Exiting...\n"); exit(-1); @@ -622,8 +629,9 @@ main(int argc, char * * argv) struct media_dir_s * media_path = media_dirs; printf("Database version mismatch; need to recreate...\n"); sqlite3_close(db); - unlink(DB_PATH); - sqlite3_open(DB_PATH, &db); + unlink(DB_PATH "/files.db"); + system("rm -rf " DB_PATH "/art_cache"); + sqlite3_open(DB_PATH "/files.db", &db); freopen("/dev/null", "a", stderr); if( CreateDatabase() != 0 ) { diff --git a/utils.c b/utils.c index ed404bb..ce82371 100644 --- a/utils.c +++ b/utils.c @@ -24,6 +24,7 @@ #include #include #include +#include int ends_with(const char * haystack, const char * needle) @@ -102,3 +103,47 @@ strip_ext(char * name) if( period ) *period = '\0'; } + +/* Code basically stolen from busybox */ +int +make_dir(char * path, mode_t mode) +{ + char * s = path; + char c; + struct stat st; + + do { + c = 0; + + /* Bypass leading non-'/'s and then subsequent '/'s. */ + while (*s) { + if (*s == '/') { + do { + ++s; + } while (*s == '/'); + c = *s; /* Save the current char */ + *s = 0; /* and replace it with nul. */ + break; + } + ++s; + } + + if (mkdir(path, mode) < 0) { + /* If we failed for any other reason than the directory + * already exists, output a diagnostic and return -1.*/ + if (errno != EEXIST + || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) { + break; + } + } + if (!c) + return 0; + + /* Remove any inserted nul from the path. */ + *s = c; + + } while (1); + + printf("make_dir: cannot create directory '%s'", path); + return -1; +} diff --git a/utils.h b/utils.h index 7172a2c..ace5305 100644 --- a/utils.h +++ b/utils.h @@ -22,4 +22,7 @@ modifyString(char * string, const char * before, const char * after, short like) void strip_ext(char * name); +int +make_dir(char * path, mode_t mode); + #endif