## 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.
'customvf': 'scdet=s=1:t=12',
'start': '0',
'end': '0',
+ 'addss': -1,
'verbosity': 0,
'batch': 0,
'manage': 0,
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()
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])
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
['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)
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)
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)
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']:
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