Skip to content

Instantly share code, notes, and snippets.

@BenWiederhake
Created November 2, 2020 16:41
Show Gist options
  • Save BenWiederhake/830c76ba84902555450ec0e68b953924 to your computer and use it in GitHub Desktop.
Save BenWiederhake/830c76ba84902555450ec0e68b953924 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
# Example usage:
# $ ./video_cut.py lectXX-raw.mp4 lectXX-cut.mp4 00:07:59 00:56:48 01:06:26 01:56:59
# $ ffmpeg -i lectXX-cut.mp4 -c:v libx264 -preset slow -crf 23 lectXX.mkv # Re-encode (to save some space; make "23" higher for higher compression and lower quality)
# $ chmod -w lectXX.mkv
# $ rsync -Pv lectXX.mkv ${USER}@contact.mpi-inf.mpg.de:/www/inf-resources-0/download.mpi-inf.mpg.de/d1/tkids/
# And maybe remove write permissions, to prevent future mistakes.
import re
import os
import subprocess
import sys
def print_usage():
usage_str = \
"""Usage: {} INPUT OUTPUT PART_START PART_END [PART_START PART_END]...
This command takes the INPUT file, and produces an OUTPUT file which
consists only the selected PARTs.
Each PART_* must be in the format hh:mm:ss.
The last PART_END may be "end" to represent the end of the INPUT video.
"""
print(usage_str.format(sys.argv[0]), file=sys.stderr)
def run(args):
# === Parsing ===
if args == ['--help']:
print_usage()
return 0
if len(args) < 4:
print('Too few arguments: must be at least 4, got only {}'.format(
len(args)), file=sys.stderr, end='\n\n')
print_usage()
return -2
if len(args) % 2 != 0:
print('Need even amount of time points: expected even, got {} (odd)'.format(
len(args)), file=sys.stderr, end='\n\n')
print_usage()
return -2
file_in = args[0]
file_out = args[1]
args = args[2:]
parts = []
for i in range(len(args)//2):
begin = args[2 * i]
end = args[2 * i + 1]
if not re.fullmatch('\d\d:\d\d:\d\d', begin):
print('Begin time "{}" looks dubious: expected hh:mm:ss instead'.format(
begin), file=sys.stderr, end='\n\n')
print_usage()
return -2
if not re.fullmatch('(\d\d:\d\d:\d\d|end)', end):
print('End time "{}" looks dubious: expected hh:mm:ss or end instead'.format(
begin), file=sys.stderr, end='\n\n')
print_usage()
return -2
if end == 'end':
if 2 * i + 1 != len(args) - 1:
print('End time "end" only permitted in last part, but found already in part {}'.format(
i + 1), file=sys.stderr, end='\n\n')
print_usage()
return -2
end = None
parts.append((begin, end))
if len(parts) == 0:
print('There must be at least one extracted part.', file=sys.stderr, end='\n\n')
print_usage()
return -2
# === Extract parts ===
list_filename = 'tmp_video_cut_list.txt'
last_written = None
with open(list_filename, 'w') as fp:
for (i, (part_begin, part_end)) in enumerate(parts):
print('Writing part {} ...'.format(i + 1))
part_filename = 'tmp_video_cut_part_{}.mp4'.format(i)
last_written = part_filename
subprocess.run(
['ffmpeg',
'-y', # Overwrite silently
'-loglevel', 'warning', # Do print warnings, but no other "informative stuff"
'-i', file_in, # Read from file in
'-ss', part_begin, '-to', part_end, # Use the indicated part
'-c', 'copy', # Don't re-encode, just copy
part_filename],
check=True)
fp.write("file '{}'\n".format(part_filename))
# == Stitch/paste/concatenate together ===
if len(parts) == 1:
print('Renaming ...')
os.rename(last_written, file_out)
else:
print('Concatenating ...')
subprocess.run(
['ffmpeg',
'-y', # Overwrite silently
'-loglevel', 'warning', # Do print warnings, but no other "informative stuff"
'-f', 'concat', '-i', list_filename, # Concatenation instructions
'-c', 'copy', # Don't re-encode, just copy
file_out],
check=True)
print('Result was written to {}'.format(file_out))
if __name__ == '__main__':
exit_code = run(sys.argv[1:])
exit(exit_code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment