--- Makefile.am.orig 2017-05-18 05:55:17.000000000 +0900 +++ Makefile.am 2017-05-26 13:51:55.519612115 +0900 @@ -53,7 +53,7 @@ @LIBEXIF_LIBS@ \ @LIBINTL@ \ @LIBICONV@ \ - -lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs) + -lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs) @LIBFFMPEGTHUMBNAILER_LIBS@ minidlnad_LDFLAGS = @STATIC_LDFLAGS@ --- albumart.c.orig 2017-05-18 05:55:17.000000000 +0900 +++ albumart.c 2017-05-26 14:03:52.661089877 +0900 @@ -32,6 +32,10 @@ #include +#ifdef THUMBNAIL_CREATION +#include +#endif + #include "upnpglobalvars.h" #include "albumart.h" #include "sql.h" @@ -348,14 +352,68 @@ return NULL; } +#ifdef THUMBNAIL_CREATION +char * +generate_thumbnail(const char * path) +{ + char *tfile = NULL; + video_thumbnailer *vt = NULL; + char cache_dir[MAXPATHLEN]; + + if( art_cache_exists(path, &tfile) ) + return tfile; + + if ( is_video(path) ) + { + + vt = video_thumbnailer_create(); + if ( !vt ) + { + free(tfile); + return 0; + } + vt->thumbnail_image_type = Jpeg; + vt->thumbnail_image_quality = runtime_vars.thumb_quality; + vt->thumbnail_size = runtime_vars.thumb_width; + vt->seek_percentage = 20; + vt->overlay_film_strip = (GETFLAG(THUMB_FILMSTRIP))?1:0; + + DPRINTF(E_DEBUG, L_METADATA, "generating thumbnail: %s\n", path); + + strncpyt(cache_dir, tfile, sizeof(cache_dir)); + if ( !make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) && + !video_thumbnailer_generate_thumbnail_to_file(vt, path, tfile) ) + { + video_thumbnailer_destroy(vt); + return tfile; + } + + video_thumbnailer_destroy(vt); + } + free(tfile); + + return 0; +} +#endif + int64_t find_album_art(const char *path, uint8_t *image_data, int image_size) { char *album_art = NULL; int64_t ret = 0; - if( (image_size && (album_art = check_embedded_art(path, image_data, image_size))) || - (album_art = check_for_album_file(path)) ) + if(image_size) + album_art = check_embedded_art(path, image_data, image_size); + + if(!album_art) + album_art = check_for_album_file(path); + +#ifdef THUMBNAIL_CREATION + if(!album_art && GETFLAG(THUMB_MASK)) + album_art = generate_thumbnail(path); +#endif + + if(album_art) { ret = sql_get_int_field(db, "SELECT ID from ALBUM_ART where PATH = '%q'", album_art); if( !ret ) --- configure.ac.orig 2017-05-18 05:55:17.000000000 +0900 +++ configure.ac 2017-05-26 14:08:49.528778727 +0900 @@ -617,6 +617,21 @@ ] ) +AC_ARG_ENABLE(thumbnail, + [ --enable-thumbnail enable video thumbnail generation using libffmpegthumbnailer],[ + if test "$enableval" = "yes"; then + AC_DEFINE([THUMBNAIL_CREATION],[1],[Define to 1 if you want to enable video thumbnail generation]) + PKG_CHECK_MODULES([LIBFFMPEGTHUMBNAILER], libffmpegthumbnailer, , + AC_MSG_ERROR([Unable to find libffmpegthumbnailer])) + AC_SUBST([LIBFFMPEGTHUMBNAILER_CFLAGS]) + AC_SUBST([LIBFFMPEGTHUMBNAILER_LIBS]) + else + AC_MSG_RESULT([no]) + fi + ],[ + AC_MSG_RESULT([no]) + ] +) case "$target_os" in darwin*) --- minidlna.c.orig 2017-05-18 05:55:17.000000000 +0900 +++ minidlna.c 2017-05-26 14:48:58.925667651 +0900 @@ -539,6 +539,11 @@ runtime_vars.root_container = NULL; runtime_vars.ifaces[0] = NULL; +#ifdef THUMBNAIL_CREATION + runtime_vars.thumb_width = 160; + runtime_vars.thumb_quality = 8; +#endif + /* read options file first since * command line arguments have final say */ if (readoptionsfile(optionsfile) < 0) @@ -752,6 +757,30 @@ if (strcasecmp(ary_options[i].value, "beacon") == 0) CLEARFLAG(TIVO_BONJOUR_MASK); break; +#ifdef THUMBNAIL_CREATION + case ENABLE_THUMB: + if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) ) + SETFLAG(THUMB_MASK); + break; + case THUMB_WIDTH: + runtime_vars.thumb_width = atoi(ary_options[i].value); + if (runtime_vars.thumb_width < 120) + runtime_vars.thumb_width = 120; + if (runtime_vars.thumb_width > 480) + runtime_vars.thumb_width = 480; + break; + case THUMB_QUALITY: + runtime_vars.thumb_quality = atoi(ary_options[i].value); + if (runtime_vars.thumb_quality < 5) + runtime_vars.thumb_quality = 5; + if (runtime_vars.thumb_quality > 30) + runtime_vars.thumb_quality = 30; + break; + case ENABLE_THUMB_FILMSTRIP: + if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) ) + SETFLAG(THUMB_FILMSTRIP); + break; +#endif default: DPRINTF(E_ERROR, L_GENERAL, "Unknown option in file %s\n", optionsfile); --- minidlna.conf.orig 2017-05-18 05:55:17.000000000 +0900 +++ minidlna.conf 2017-05-26 14:52:57.185771270 +0900 @@ -88,3 +88,15 @@ # set this to yes to allow symlinks that point outside user-defined media_dirs. #wide_links=no + +# Suport to Movie Thumbnail generation. To use this option, thumbnail generation must be enable at compile time. +#enable_thumbnail=no + +# The width of the thumbnail image. Large images takes more time to generate. To use this option, thumbnail generation must be enable at compile time. +#thumbnail_width=160 + +# Thumbnail Image quality. To use this option, thumbnail generation must be enable at compile time. +#thumbnail_quality=8 + +# Should the thumbnail have a film strip? To use this option, thumbnail generation must be enable at compile time. +#enable_thumbnail_filmstrip=no --- options.c.orig 2017-05-18 05:55:17.000000000 +0900 +++ options.c 2017-05-26 14:56:38.408012333 +0900 @@ -64,7 +64,15 @@ { USER_ACCOUNT, "user" }, { FORCE_SORT_CRITERIA, "force_sort_criteria" }, { MAX_CONNECTIONS, "max_connections" }, +#ifndef THUMBNAIL_CREATION { MERGE_MEDIA_DIRS, "merge_media_dirs" }, +#else + { MERGE_MEDIA_DIRS, "merge_media_dirs" }, + { ENABLE_THUMB, "enable_thumbnail" }, + { THUMB_WIDTH, "thumbnail_width" }, + { THUMB_QUALITY, "thumbnail_quality" }, + { ENABLE_THUMB_FILMSTRIP, "enable_thumbnail_filmstrip" }, +#endif { WIDE_LINKS, "wide_links" }, { TIVO_DISCOVERY, "tivo_discovery" }, }; --- options.h.orig 2017-05-18 05:55:17.000000000 +0900 +++ options.h 2017-05-26 15:07:15.637791194 +0900 @@ -57,7 +57,15 @@ USER_ACCOUNT, /* user account to run as */ FORCE_SORT_CRITERIA, /* force sorting by a given sort criteria */ MAX_CONNECTIONS, /* maximum number of simultaneous connections */ +#ifndef THUMBNAIL_CREATION MERGE_MEDIA_DIRS, /* don't add an extra directory level when there are multiple media dirs */ +#else + MERGE_MEDIA_DIRS, /* don't add an extra directory level when there are multiple media dirs */ + ENABLE_THUMB, /* enable thumbnail generation */ + THUMB_WIDTH, /* thunbnail image with */ + THUMB_QUALITY, /* thumnail image quality */ + ENABLE_THUMB_FILMSTRIP, /* film strip overlay */ +#endif WIDE_LINKS, /* allow following symlinks outside the defined media_dirs */ TIVO_DISCOVERY, /* TiVo discovery protocol: bonjour or beacon. Defaults to bonjour if supported */ }; --- upnpglobalvars.h.orig 2017-05-18 05:55:17.000000000 +0900 +++ upnpglobalvars.h 2017-05-26 15:12:44.457010482 +0900 @@ -196,6 +196,11 @@ #define SCANNING_MASK 0x0100 #define RESCAN_MASK 0x0200 +#ifdef THUMBNAIL_CREATION +#define THUMB_MASK 0x0100 +#define THUMB_FILMSTRIP 0x0200 +#endif + #define SETFLAG(mask) runtime_flags |= mask #define GETFLAG(mask) (runtime_flags & mask) #define CLEARFLAG(mask) runtime_flags &= ~mask --- utils.h.orig 2017-05-18 05:55:17.000000000 +0900 +++ utils.h 2017-05-26 15:18:47.939957870 +0900 @@ -101,4 +101,8 @@ int make_dir(char * path, mode_t mode); unsigned int DJBHash(uint8_t *data, int len); +#ifdef THUMBNAIL_CREATION +int rename_artcache_dir(const char * oldpath, const char * newpath); +#endif + #endif --- minidlnatypes.h.orig 2017-05-18 05:55:17.000000000 +0900 +++ minidlnatypes.h 2017-05-26 15:25:08.389784656 +0900 @@ -51,6 +51,10 @@ int max_connections; /* max number of simultaneous conenctions */ const char *root_container; /* root ObjectID (instead of "0") */ const char *ifaces[MAX_LAN_ADDR]; /* list of configured network interfaces */ +#ifdef THUMBNAIL_CREATION + int thumb_width; + int thumb_quality; +#endif }; struct string_s { --- monitor.c.orig 2017-05-18 05:55:17.000000000 +0900 +++ monitor.c 2017-05-26 16:28:48.306100398 +0900 @@ -322,6 +322,16 @@ sql_exec(db, "DELETE from OBJECTS where DETAIL_ID = %lld", detailID); } snprintf(art_cache, sizeof(art_cache), "%s/art_cache%s", db_path, path); + +#ifdef THUMBNAIL_CREATION + /* Remove video thumbnails */ + if ( is_video(path) ) + { + char *vthumb = art_cache; + strcpy(strchr(vthumb, '\0')-4, ".jpg"); + } +#endif + remove(art_cache); return 0; @@ -616,6 +626,11 @@ sigdelset(&set, SIGCHLD); pthread_sigmask(SIG_BLOCK, &set, NULL); +#ifdef THUMBNAIL_CREATION + char renpath_buf[PATH_MAX]; + int cookie = 0; +#endif + pollfds[0].fd = inotify_init(); pollfds[0].events = POLLIN; @@ -685,6 +700,18 @@ { DPRINTF(E_DEBUG, L_INOTIFY, "The directory %s was %s.\n", path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created")); + +#ifdef THUMBNAIL_CREATION + /* We do not want to regenerate the thumbnails if e rename a directory. + We should keep at least four cookies/olddir since IN_MOVED_FROM/IN_MOVED_TO may + not arrive in sequence, but one should cover most cases */ + if (event->cookie == cookie && event->mask & IN_MOVED_TO) + { + DPRINTF(E_DEBUG, L_INOTIFY, "Directory rename: %s -> %s \n", renpath_buf, path_buf); + rename_artcache_dir(renpath_buf, path_buf); + } +#endif + monitor_insert_directory(pollfds[0].fd, esc_name, path_buf); } else if ( (event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE)) && @@ -717,7 +744,22 @@ (event->mask & IN_ISDIR ? "directory" : "file"), path_buf, (event->mask & IN_MOVED_FROM ? "moved away" : "deleted")); if ( event->mask & IN_ISDIR ) + +#ifdef THUMBNAIL_CREATION + { + if ( event->mask & IN_MOVED_FROM ) + { + strncpy(renpath_buf, path_buf, sizeof(renpath_buf)); + cookie = event->cookie; + } +#endif + monitor_remove_directory(pollfds[0].fd, path_buf); + +#ifdef THUMBNAIL_CREATION + } +#endif + else monitor_remove_file(path_buf); } --- utils.c.orig 2017-08-25 02:28:25.000000000 +0900 +++ utils.c 2017-09-15 04:00:57.174194577 +0900 @@ -531,3 +531,17 @@ return ALL_MEDIA; } + +#ifdef THUMBNAIL_CREATION +int +rename_artcache_dir(const char * oldpath, const char * newpath) +{ + char old_artcache[PATH_MAX]; + char new_artcache[PATH_MAX]; + + snprintf(old_artcache, sizeof(old_artcache), "%s/art_cache%s", db_path, oldpath); + snprintf(new_artcache, sizeof(old_artcache), "%s/art_cache%s", db_path, newpath); + + return rename(old_artcache, new_artcache); +} +#endif