Created
November 2, 2020 16:41
-
-
Save BenWiederhake/830c76ba84902555450ec0e68b953924 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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