From 035f3da6bb44bc98f44b174a5afca95e18760dee Mon Sep 17 00:00:00 2001 From: Urban Wallasch Date: Mon, 24 May 2021 17:19:09 +0200 Subject: [PATCH] * Addded -a/--addss option to render subtitles in thumbnail images. --- README.md | 43 +++++++++++++++++----------------- ffpreview.conf.sample | 5 ++++ ffpreview.py | 54 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 76 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 225f796..6f9f346 100644 --- a/README.md +++ b/README.md @@ -59,36 +59,37 @@ to learn more about video frame select filter expressions. ## Usage Running `ffpreview.py -h` will print the following help text: - ``` usage: ffpreview.py [-h] [-b] [-m] [-c F] [-g G] [-w N] [-o P] [-f] [-r] [-i] - [-n N] [-N F] [-s F] [-C S] [-S T] [-E T] [-v] [--version] + [-n N] [-N F] [-s F] [-C S] [-S T] [-E T] [-a [N]] [-v] + [--version] [filename [filename ...]] Generate interactive video thumbnail preview. positional arguments: - filename input video file + filename input video file optional arguments: - -h, --help show this help message and exit - -b, --batch batch mode, do not draw window - -m, --manage start with thumbnail manager - -c F, --config F read configuration from file F - -g G, --grid G set grid geometry in COLS[xROWS] format - -w N, --width N thumbnail image width in pixel - -o P, --outdir P set thumbnail parent directory to P - -f, --force force thumbnail and index rebuild - -r, --reuse reuse filter settings from index file - -i, --iframe select only I-frames (default) - -n N, --nskip N select only every Nth frame - -N F, --nsecs F select one frame every F seconds - -s F, --scene F select by scene change threshold; 0 < F < 1 - -C S, --customvf S select frames using custom filter string S - -S T, --start T start video analysis at time T - -E T, --end T end video analysis at time T - -v, --verbose be more verbose; repeat to increase - --version print version info and exit + -h, --help show this help message and exit + -b, --batch batch mode, do not draw window + -m, --manage start with thumbnail manager + -c F, --config F read configuration from file F + -g G, --grid G set grid geometry in COLS[xROWS] format + -w N, --width N thumbnail image width in pixel + -o P, --outdir P set thumbnail parent directory to P + -f, --force force thumbnail and index rebuild + -r, --reuse reuse filter settings from index file + -i, --iframe select only I-frames (default) + -n N, --nskip N select only every Nth frame + -N F, --nsecs F select one frame every F seconds + -s F, --scene F select by scene change threshold; 0 < F < 1 + -C S, --customvf S select frames using custom filter string S + -S T, --start T start video analysis at time T + -E T, --end T end video analysis at time T + -a [N], --addss [N] add subtitles from stream N + -v, --verbose be more verbose; repeat to increase + --version print version info and exit The -C, -i, -N, -n and -s options are mutually exclusive. If more than one is supplied: -C beats -i beats -N beats -n beats -s. diff --git a/ffpreview.conf.sample b/ffpreview.conf.sample index 32535dc..2d87385 100644 --- a/ffpreview.conf.sample +++ b/ffpreview.conf.sample @@ -97,4 +97,9 @@ time_skip=60 # to learn more about video filter expressions. customvf=scdet=s=1:t=12 +# Add rendered captions from specified subtitle stream. Stream numbering +# starts at 0, set to -1 to disable. If the specified stream is not found, +# this setting is silently ignored. +addss=-1 + # EOF diff --git a/ffpreview.py b/ffpreview.py index d1b0a0d..adbf03a 100755 --- a/ffpreview.py +++ b/ffpreview.py @@ -199,6 +199,7 @@ class ffConfig: 'customvf': 'scdet=s=1:t=12', 'start': '0', 'end': '0', + 'addss': -1, 'verbosity': 0, 'batch': 0, 'manage': 0, @@ -266,6 +267,7 @@ class ffConfig: parser.add_argument('-C', '--customvf', metavar='S', help='select frames using custom filter string S') parser.add_argument('-S', '--start', metavar='T', help='start video analysis at time T') parser.add_argument('-E', '--end', metavar='T', help='end video analysis at time T') + parser.add_argument('-a', '--addss', nargs='?', type=int, const=0, metavar='N', help='add subtitles from stream N') parser.add_argument('-v', '--verbose', action='count', help='be more verbose; repeat to increase') parser.add_argument('--version', action='count', help='print version info and exit') args = parser.parse_args() @@ -298,6 +300,8 @@ class ffConfig: cfg['start'] = hms2s(args.start) if args.end: cfg['end'] = hms2s(args.end) + if args.addss is not None: + cfg['addss'] = args.addss if args.grid: grid = re.split(r'[xX,;:]', args.grid) cfg['grid_columns'] = int(grid[0]) @@ -360,6 +364,7 @@ class ffConfig: cfg['scene_thresh'] = str2float(cfg['scene_thresh']) cfg['start'] = str2float(cfg['start']) cfg['end'] = str2float(cfg['end']) + cfg['addss'] = str2int(cfg['addss']) return True @classmethod @@ -906,7 +911,9 @@ class cfgDialog(QDialog): ['frame_skip', 'spin', 'Number of frames to skip for method \'skip\''], ['time_skip', 'spin', 'Number of seconds to skip for method \'time\''], ['scene_thresh', 'dblspin', 'Scene detection threshold for method \'scene\''], - ['customvf', 'edit', 'Filter expression for method \'customvf\''] ] + ['customvf', 'edit', 'Filter expression for method \'customvf\''], + ['addss', 'spin', 'Add subtitles from stream'], + ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -921,7 +928,7 @@ class cfgDialog(QDialog): self.table_widget.setStyleSheet('QTableView::item {border-bottom: 1px solid lightgrey;}') self.table_widget.setRowCount(len(self.opt)) self.table_widget.setColumnCount(1) - self.resize(self.table_widget.width() + 150, self.table_widget.height()+100) + self.resize(self.table_widget.width() + 150, self.table_widget.height()+120) self.btn_layout = QHBoxLayout() self.reset_button = QPushButton('Reset') self.reset_button.setIcon(ffIcon.revert) @@ -1116,7 +1123,7 @@ class cfgDialog(QDialog): w.textChanged.connect(self.changed) elif o[1] == 'spin': w = QSpinBox() - w.setRange(1, 9999) + w.setRange(0, 9999) w.setValue(self.cfg[o[0]]) w.setToolTip(o[2]) w.valueChanged.connect(self.changed) @@ -1689,12 +1696,47 @@ def get_meta(vidfile): proc = kill_proc(proc) return meta, False +# count subtitle streams +def count_subss(vidfile): + global proc + if proc: + return -1 + nstreams = -1 + try: + cmd = cfg['ffprobe'] + ' -v error -select_streams s -show_entries stream=index -of csv=p=0' + cmd += ' "' + vidfile + '"' + eprint(2, cmd) + proc = Popen(cfg['exec'] + ' ' + cmd, shell=True, stdout=PIPE, stderr=PIPE, env=cfg['env']) + stdout, stderr = proc.communicate() + retval = proc.wait() + proc = None + if retval == 0: + nstreams = len(stdout.decode().splitlines()) + eprint(2, 'number of subtitle streams:', nstreams) + else: + eprint(0, cmd + '\n returned %d' % retval) + eprint(1, stderr.decode()) + except Exception as e: + eprint(0, cmd + '\n failed: ' + str(e)) + proc = kill_proc(proc) + return nstreams + # extract thumbnails from video and collect timestamps def make_thumbs(vidfile, thinfo, thdir, prog_cb=None): global proc rc = False if proc: return thinfo, rc + # check for subtitle streams + addss = cfg['addss'] + if addss >= 0: + nstreams = count_subss(vidfile) + if addss > nstreams - 1: + eprint(1, 'ignoring invalid subtitle stream index:', addss) + addss = -1 + else: + eprint(2, 'including subtitle stream index:', addss) + # generate thumbnail images from video pictemplate = '%08d.png' cmd = cfg['ffmpeg'] + ' -loglevel info -hide_banner -y' if cfg['start']: @@ -1713,8 +1755,10 @@ def make_thumbs(vidfile, thinfo, thdir, prog_cb=None): cmd += ' -vf "' + cfg['customvf'] else: # iframe cmd += ' -vf "select=eq(pict_type\,I)' - cmd += ',showinfo,scale=' + str(cfg['thumb_width']) + ':-1"' - cmd += ' -vsync vfr "' + os.path.join(thdir, pictemplate) + '"' + cmd += ',showinfo,scale=' + str(cfg['thumb_width']) + ':-1' + if addss >= 0: + cmd += ',subtitles=\'' + vidfile + '\':si=' + str(addss) + cmd += '" -vsync vfr "' + os.path.join(thdir, pictemplate) + '"' eprint(2, cmd) ebuf = '' cnt = 0 -- 2.30.2