- added patches:
- 01-run-instead-of-var-run.patch
use /run for runtime generated files
- 07-fix-multi-artist-album-handling.patch
fix for compilation albums
- 08-Fix-testupnpdescgen-build.patch
skip unneccessary lib when linking
- 10-do-not-close-socket-on-sighup.patch
no need to restart the interfaces when we just rescan
- 13-spelling-and-typos.patch
cleanup error message
- 15-thumbnails.patch
use ffmpegthumbnailer
- 16-Add-compatibility-with-FFMPEG-7.0.patch
fix build with current ffmpeg
- add br for pkgconfig(libffmpegthumbnailer)
OBS-URL: https://build.opensuse.org/package/show/multimedia:apps/minidlna?expand=0&rev=27
397 lines
12 KiB
Diff
397 lines
12 KiB
Diff
From: "Barak A. Pearlmutter" <barak+git@pearlmutter.net>
|
|
Date: Mon, 21 Aug 2023 12:00:00 +0000
|
|
Subject: thumbnails
|
|
|
|
This patch is from
|
|
https://sourceforge.net/p/minidlna/patches/_discuss/thread/28a9f79d/9aca/attachment/minidlna-1.3.3-thumb.patch
|
|
by https://sourceforge.net/u/wilderman/profile/
|
|
with changes to derived and cache files (configure and such) removed.
|
|
---
|
|
Makefile.am | 4 ++--
|
|
albumart.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
|
|
configure.ac | 15 ++++++++++++++
|
|
minidlna.c | 29 ++++++++++++++++++++++++++
|
|
minidlna.conf | 12 +++++++++++
|
|
minidlnatypes.h | 4 ++++
|
|
monitor.c | 8 +++++++
|
|
monitor_inotify.c | 25 ++++++++++++++++++++++
|
|
options.c | 6 ++++++
|
|
options.h | 6 ++++++
|
|
upnpglobalvars.h | 5 +++++
|
|
utils.c | 14 +++++++++++++
|
|
utils.h | 3 +++
|
|
13 files changed, 189 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/Makefile.am b/Makefile.am
|
|
index dcfc625..603ee03 100644
|
|
--- a/Makefile.am
|
|
+++ b/Makefile.am
|
|
@@ -62,7 +62,7 @@ minidlnad_LDADD = \
|
|
@LIBEXIF_LIBS@ \
|
|
@LIBINTL@ \
|
|
@LIBICONV@ \
|
|
- -lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs)
|
|
+ -lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs) @LIBFFMPEGTHUMBNAILER_LIBS@
|
|
|
|
testupnpdescgen_SOURCES = testupnpdescgen.c upnpdescgen.c
|
|
testupnpdescgen_LDADD = \
|
|
@@ -71,7 +71,7 @@ testupnpdescgen_LDADD = \
|
|
@LIBSQLITE3_LIBS@ \
|
|
@LIBAVFORMAT_LIBS@ \
|
|
@LIBEXIF_LIBS@ \
|
|
- -lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs)
|
|
+ -lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs) @LIBFFMPEGTHUMBNAILER_LIBS@
|
|
|
|
SUFFIXES = .tmpl .
|
|
|
|
diff --git a/albumart.c b/albumart.c
|
|
index de026ec..28f174b 100644
|
|
--- a/albumart.c
|
|
+++ b/albumart.c
|
|
@@ -32,6 +32,10 @@
|
|
|
|
#include <jpeglib.h>
|
|
|
|
+#ifdef THUMBNAIL_CREATION
|
|
+#include <libffmpegthumbnailer/videothumbnailerc.h>
|
|
+#endif
|
|
+
|
|
#include "upnpglobalvars.h"
|
|
#include "albumart.h"
|
|
#include "sql.h"
|
|
@@ -348,14 +352,68 @@ found_file:
|
|
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 )
|
|
diff --git a/configure.ac b/configure.ac
|
|
index 11421a5..2c3278b 100644
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -642,6 +642,21 @@ AC_ARG_ENABLE(lto,
|
|
]
|
|
)
|
|
|
|
+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*)
|
|
diff --git a/minidlna.c b/minidlna.c
|
|
index 6d2d06b..74e1fac 100644
|
|
--- a/minidlna.c
|
|
+++ b/minidlna.c
|
|
@@ -589,6 +589,11 @@ init(int argc, char **argv)
|
|
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)
|
|
@@ -812,6 +817,30 @@ init(int argc, char **argv)
|
|
if (!strtobool(ary_options[i].value))
|
|
CLEARFLAG(SUBTITLES_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);
|
|
diff --git a/minidlna.conf b/minidlna.conf
|
|
index 82db5f1..f6ededf 100644
|
|
--- a/minidlna.conf
|
|
+++ b/minidlna.conf
|
|
@@ -95,3 +95,15 @@ model_number=1
|
|
# enable subtitle support by default on unknown clients.
|
|
# note: the default is yes
|
|
#enable_subtitles=yes
|
|
+
|
|
+# 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
|
|
diff --git a/minidlnatypes.h b/minidlnatypes.h
|
|
index a92cc7d..98f91b1 100644
|
|
--- a/minidlnatypes.h
|
|
+++ b/minidlnatypes.h
|
|
@@ -51,6 +51,10 @@ struct runtime_vars_s {
|
|
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 {
|
|
diff --git a/monitor.c b/monitor.c
|
|
index b5757d9..f064ee5 100644
|
|
--- a/monitor.c
|
|
+++ b/monitor.c
|
|
@@ -117,6 +117,14 @@ monitor_remove_file(const char * path)
|
|
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;
|
|
diff --git a/monitor_inotify.c b/monitor_inotify.c
|
|
index 365b3fe..d051dd0 100644
|
|
--- a/monitor_inotify.c
|
|
+++ b/monitor_inotify.c
|
|
@@ -265,6 +265,10 @@ inotify_thread(void *arg)
|
|
char * esc_name = NULL;
|
|
struct stat st;
|
|
sigset_t set;
|
|
+#ifdef THUMBNAIL_CREATION
|
|
+ char renpath_buf[PATH_MAX];
|
|
+ int cookie = 0;
|
|
+#endif
|
|
|
|
sigfillset(&set);
|
|
sigdelset(&set, SIGCHLD);
|
|
@@ -332,6 +336,16 @@ inotify_thread(void *arg)
|
|
{
|
|
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)) &&
|
|
@@ -364,7 +378,18 @@ inotify_thread(void *arg)
|
|
(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);
|
|
diff --git a/options.c b/options.c
|
|
index cee3dff..f161d60 100644
|
|
--- a/options.c
|
|
+++ b/options.c
|
|
@@ -68,6 +68,12 @@ static const struct {
|
|
{ WIDE_LINKS, "wide_links" },
|
|
{ TIVO_DISCOVERY, "tivo_discovery" },
|
|
{ ENABLE_SUBTITLES, "enable_subtitles" },
|
|
+#ifdef THUMBNAIL_CREATION
|
|
+ { ENABLE_THUMB, "enable_thumbnail" },
|
|
+ { THUMB_WIDTH, "thumbnail_width" },
|
|
+ { THUMB_QUALITY, "thumbnail_quality" },
|
|
+ { ENABLE_THUMB_FILMSTRIP, "enable_thumbnail_filmstrip" },
|
|
+#endif
|
|
};
|
|
|
|
int
|
|
diff --git a/options.h b/options.h
|
|
index 1ff5b22..0e8c53c 100644
|
|
--- a/options.h
|
|
+++ b/options.h
|
|
@@ -61,6 +61,12 @@ enum upnpconfigoptions {
|
|
WIDE_LINKS, /* allow following symlinks outside the defined media_dirs */
|
|
TIVO_DISCOVERY, /* TiVo discovery protocol: bonjour or beacon. Defaults to bonjour if supported */
|
|
ENABLE_SUBTITLES, /* Enable generic subtitle support for all clients by default */
|
|
+#ifdef THUMBNAIL_CREATION
|
|
+ ENABLE_THUMB, /* enable thumbnail generation */
|
|
+ THUMB_WIDTH, /* thunbnail image with */
|
|
+ THUMB_QUALITY, /* thumnail image quality */
|
|
+ ENABLE_THUMB_FILMSTRIP /* film strip overlay */
|
|
+#endif
|
|
};
|
|
|
|
/* readoptionsfile()
|
|
diff --git a/upnpglobalvars.h b/upnpglobalvars.h
|
|
index 8a35555..66be7ba 100644
|
|
--- a/upnpglobalvars.h
|
|
+++ b/upnpglobalvars.h
|
|
@@ -208,6 +208,11 @@ extern uint32_t runtime_flags;
|
|
#define SUBTITLES_MASK 0x0400
|
|
#define FORCE_ALPHASORT_MASK 0x0800
|
|
|
|
+#ifdef THUMBNAIL_CREATION
|
|
+#define THUMB_MASK 0x1000
|
|
+#define THUMB_FILMSTRIP 0x2000
|
|
+#endif
|
|
+
|
|
#define SETFLAG(mask) runtime_flags |= mask
|
|
#define GETFLAG(mask) (runtime_flags & mask)
|
|
#define CLEARFLAG(mask) runtime_flags &= ~mask
|
|
diff --git a/utils.c b/utils.c
|
|
index 03ba850..eb809b8 100644
|
|
--- a/utils.c
|
|
+++ b/utils.c
|
|
@@ -576,3 +576,17 @@ timevalfix(struct timeval *t1)
|
|
t1->tv_usec -= 1000000;
|
|
}
|
|
}
|
|
+
|
|
+#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
|
|
diff --git a/utils.h b/utils.h
|
|
index 84b9923..641b57b 100644
|
|
--- a/utils.h
|
|
+++ b/utils.h
|
|
@@ -100,6 +100,9 @@ const char *mime_to_ext(const char * mime);
|
|
/* Others */
|
|
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
|
|
|
|
/* Timeval manipulations */
|
|
void timevaladd(struct timeval *t1, const struct timeval *t2);
|