Added support for black detection in video thumbnail generation

Added 3 new conf options:
* thumb_video_black_retries = 0
* thumb_video_black_retry_offset = 1
* thumb_video_black_max_colors = 4

They control how many retries a thumbnail generation should run before
failure. Each retry seeks the video another N seconds determined by
retry_offset in addition to the original thumb_video_delay setting.

A 'black' thumbnail is detected when the resulting image has max_colors
or less distinct color values in it. This is not exclusive to black, but
attempts to avoid any solid color images.
This commit is contained in:
Sean Grider
2022-12-30 23:32:36 -05:00
parent 03418f5a90
commit 2ca3e174a3
4 changed files with 40 additions and 8 deletions

View File

@@ -179,6 +179,9 @@ class Media:
s['thumb_video_delay'],
fit=s['thumb_fit'],
converter=s['video_converter'],
black_retries=s['thumb_video_black_retries'],
black_offset=s['thumb_video_black_retry_offset'],
black_max_colors=s['thumb_video_black_max_colors']
)
except Exception as e:
self.logger.error('Failed to generate thumbnail: %s', e)

View File

@@ -75,7 +75,10 @@ _DEFAULT_CONFIG = {
'thumb_prefix': '',
'thumb_size': (200, 150),
'thumb_suffix': '',
'thumb_video_delay': '0',
'thumb_video_delay': 0,
'thumb_video_black_retries': 0,
'thumb_video_black_retry_offset': 1,
'thumb_video_black_max_colors': 4,
'title': '',
'use_orig': False,
'user_css': None,

View File

@@ -119,7 +119,13 @@ thumb_size = (280, 210)
# thumb_fit_centering = (0.5, 0.5)
# Delay in seconds to avoid black thumbnails in videos with fade-in
# thumb_video_delay = '0'
# thumb_video_delay = 0
# Max retries to generate a non-black thumbnail
# thumb_video_black_retries = 0
# For each retry, advance another N seconds on top of original delay
# thumb_video_black_retry_offset = 1
# A thumbnail with more than max_colors will not be considered "all black"
# thumb_video_black_max_colors = 4
# Keep original image (default: False)
# keep_orig = True

View File

@@ -27,6 +27,8 @@ import shutil
import subprocess
from os.path import splitext
from PIL import Image as PILImage
from . import image, utils
from .utils import is_valid_html5_video
@@ -172,17 +174,32 @@ def generate_video(source, outname, settings):
def generate_thumbnail(
source, outname, box, delay, fit=True, options=None, converter="ffmpeg"
source, outname, box, delay, fit=True, options=None, converter="ffmpeg", black_retries=0, black_offset=1, black_max_colors=4
):
"""Create a thumbnail image for the video source, based on ffmpeg."""
logger = logging.getLogger(__name__)
tmpfile = outname + ".tmp.jpg"
# dump an image of the video
cmd = [converter, "-i", source, "-an", "-r", "1"]
cmd += ["-ss", str(delay), "-vframes", "1", "-y", tmpfile]
logger.debug("Create thumbnail for video: %s", " ".join(cmd))
check_subprocess(cmd, source, outname)
currentTry = 0
iDelay = int(delay)
while currentTry <= abs(black_retries):
# dump an image of the video
cmd = [converter, "-i", source, "-an", "-r", "1"]
cmd += ["-ss", str(iDelay), "-vframes", "1", "-y", tmpfile]
logger.debug("Create thumbnail for video: %s", " ".join(cmd))
check_subprocess(cmd, source, outname)
if os.path.isfile(tmpfile) and black_retries > 0:
img = PILImage.open(tmpfile)
colors = img.getcolors(maxcolors=black_max_colors)
if colors is None:
# There were more colors than maxcolors in the image, it looks suitable for a valid thumbnail
break
else:
# Only found 'maxcolors' unique colors, looks like a solid color, try again with another seek delay
currentTry += 1
iDelay += abs(black_offset)
else:
break
# Sometimes ffmpeg fails with returncode zero but without producing an
# output file Thus, we need to check if an output file was created. If
@@ -228,6 +245,9 @@ def process_video(media):
fit=settings["thumb_fit"],
options=settings["jpg_options"],
converter=settings["video_converter"],
black_retries=s['thumb_video_black_retries'],
black_offset=s['thumb_video_black_retry_offset'],
black_max_colors=s['thumb_video_black_max_colors']
)
return status.value