Skip to content

Commit

Permalink
desktop.media.tomkv: downsample video to 30fps as well
Browse files Browse the repository at this point in the history
  • Loading branch information
mk-fg committed Nov 17, 2024
1 parent a7a1b83 commit 13b0dcf
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 10 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3163,8 +3163,9 @@ Needs youtube-dl installed if URLs are specified instead of regular files.
##### [tomkv](desktop/media/tomkv)

Script to batch-convert video files to efficient A/V codecs and downscale
to ~720p h265 and 2-channel 96k opus audio, which is useful for modern systems
that have no trouble playing these codecs and take 2x+ less space than common h264.
to ~720p30 h265 and 2-channel 96k opus audio, which is useful for modern systems
that have no trouble playing these codecs and take 2x+ less space than common h264,
or much less space if downscaling/downsampling is also involved.

ffprobe is run on the files first to detect ones which won't benefit from
conversion or have any kind of ambiguity/errors not handled by this script
Expand Down
21 changes: 13 additions & 8 deletions desktop/media/tomkv
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ def src_probe(p):
for s in probe.streams:
if s.codec_type == 'video':
if video: return adict(t='video', msg='Multiple streams detected')
video = adict(c=s.codec_name, w=s.width, h=s.height)
if fps := s.get('avg_frame_rate') or 0:
if '/' not in fps: fps = float(fps)
else: a, b = map(float, fps.split('/')); fps = a/b
video = adict(c=s.codec_name, w=s.width, h=s.height, fps=fps)
if s.codec_type == 'audio':
if audio: return adict(t='audio', msg='Multiple streams detected')
br = (c := s.codec_name) == 'opus' and int(s.get('bit_rate')) or 0
Expand All @@ -75,7 +78,7 @@ def main(args=None):
usage='%(prog)s [options] src [src ...]', description=dd('''
ffprobe-check and convert source file(s)
to a more compact video format, as needed, into current dir.
Encodes to h265/opus/mkv, downscaling video/audio to ~720p/96k-stereo.
Encodes to h265/opus/mkv, downscaling video/audio to ~720p30/96k-stereo.
Initial ffprobe is intended to detect files that might already be
converted or won't benefit from it as much, and skip those by default,
as well as any files that can't be handled by this script correctly
Expand Down Expand Up @@ -140,9 +143,9 @@ def main(args=None):
p, dst, src_list[n] = src_probe(src), dst_name_format(src), None
if (errs := p.get('errs')) is None:
print(f'\n{dst} :: PROBLEM {p.get("t") or "-"} :: {p.msg}'); continue
if p.v.c == 'hevc' and p.v.w <= 1400:
errs.append(adict(t='video', msg='Already encoded to <1280x hevc'))
if p.v.w > 1400: p.v.scale = True
p.v.scale, p.v.resample = p.v.w > 1400, p.v.fps > 35
if p.v.c == 'hevc' and not p.v.scale and not p.v.resample:
errs.append(adict(t='video', msg='Already encoded to <1280x 30fps hevc'))
if p.a.c == 'opus' and p.a.chans <= 2 and p.a.br and p.a.br < 200_000:
errs.append(adict( t='audio', warn=1,
msg='Already encoded in <200k 2ch opus, will copy it as-is' ))
Expand All @@ -162,8 +165,10 @@ def main(args=None):
dry_run, src_list = not opts.convert, list(filter(None, src_list))
if dry_run: print()
for n, p in enumerate(src_list, 1):
scale = ( [] if not p.v.get('scale') else
['-filter:v', "scale='min(1280,iw)':-2,setsar=1:1"] )
filters = list()
if p.v.resample: filters.extend(['-r', '30'])
if p.v.scale: filters.extend([
'-filter:v', "scale='min(1280,iw)':-2,setsar=1:1" ])
if p.a.get('clone'): ac = ['-c:a', 'copy']
else:
ac = ['-c:a', 'libopus', '-b:a', '96k']
Expand All @@ -173,7 +178,7 @@ def main(args=None):
elif p.a.chans < 2: ac = ['-ac', '2'] + ac
movflags = ( ['-movflags', '+faststart']
if p.dst.rsplit('.', 1)[-1].lower() in ['mp4', 'mov', 'm4v'] else [] )
cmd = [ 'ffmpeg', '-hide_banner', '-i', str(p.src), *scale,
cmd = [ 'ffmpeg', '-hide_banner', '-i', str(p.src), *filters,
'-c:v', 'libx265', '-preset', 'slow', *movflags, *ac, '-y', p.dst ]
msg = ( f'\n\n- ----- [ {n} / {len(src_list)} ]'
f' :: {p.src} :-[ {td_repr(p.td)} ]->: {p.dst}\n\n' )
Expand Down

0 comments on commit 13b0dcf

Please sign in to comment.