From e5bcb8d9ef1d3572dcd71a130df9a48ac86318bc Mon Sep 17 00:00:00 2001 From: bertieb Date: Thu, 10 Dec 2020 00:17:47 +0000 Subject: [PATCH] jsonclips.py - generate highlights based on JSON usage: jsonclips.py clips.json JSON file can specify: - output filename - source filepath - start time - duration - transition type [default: luma] - transition duration [default: 30] --- jsonclips.py | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 jsonclips.py diff --git a/jsonclips.py b/jsonclips.py new file mode 100644 index 0000000..1ec32b0 --- /dev/null +++ b/jsonclips.py @@ -0,0 +1,113 @@ +#!/bin/python +"""jsonclips.py - generate highlights based on JSON + +usage: jsonclips.py clips.json + +JSON file can specify: + - output filename + - source filepath + - start time + - duration + - transition type [default: luma] + - transition duration [default: 30] +""" + +DEFAULT_TRANSITION_DURATION = 30 # 30 frames, ie 0.5s at 60FPS +DEFAULT_TRANSITION_TYPE = "luma" + + +def process_highlights(jsonfile): + """Go through highlights, make clips, join to a video""" + import tempfile + + def make_clips(sources=None, outputdir=None): + """Use ffmpeg to create output from processed JSON""" + import subprocess + import os + + ffmpeg_common_args = ["ffmpeg", "-loglevel", "quiet", "-hide_banner"] + clipnum = 0 + + for source in sources: + print("Making {}s clip from {} at {}".format( + source["duration"], source["filepath"], source["time"])) + + ffmpeg_file_args = ["-ss", str(source["time"]), + "-i", source["filepath"], + "-t", str(source["duration"]), + "-c", "copy", + os.path.join(outputdir, + "clip{}.mkv".format(clipnum))] + + ffmpeg_args = ffmpeg_common_args + ffmpeg_file_args + print("Running ffmpeg with args:\n\n{}". + format(ffmpeg_args)) + subprocess.run(ffmpeg_args) + + clipnum += 1 + + def make_output(workingdir=None, outputfile=None, numsources=None): + """Use MLT (melt) to join clips""" + import subprocess + import os + + melt_args = ["melt"] + + for i in range(numsources): + if i == 0: + melt_args.extend([os.path.join(workingdir, "clip{}.mkv" + .format(i))]) + else: + melt_args.extend([ + os.path.join(workingdir, "clip{}.mkv".format(i)), + "-mix", str(DEFAULT_TRANSITION_DURATION), + "-mixer", str(DEFAULT_TRANSITION_TYPE)]) + + melt_args.extend(["-consumer", "avformat:{}".format(outputfile), + "crf=20"]) + + print("Running melt with args:\n\n{}".format(melt_args)) + subprocess.run(melt_args) + + def parse_json(jsonfile=None): + """Parse global / per--clip options from jsonfile + + Requires: + - outputfile + - list of sources + """ + import json + import sys + + if jsonfile: + with open(jsonfile, "r") as fh: + json_in = json.load(fh) + + if "outputfile" not in json_in: + print("Please specify an outputfile to write to") + sys.exit(1) + + if "sources" not in json_in: + print("Please specify some sources to process") + sys.exit(1) + + return json_in + + # Start + + if jsonfile: + json_in = parse_json(jsonfile) + + # in temporary dir + with tempfile.TemporaryDirectory() as tmpdir: + # make clips + make_clips(sources=json_in["sources"], outputdir=tmpdir) + # join clips using melt + make_output(workingdir=tmpdir, outputfile=json_in["outputfile"], + numsources=len(json_in["sources"])) + + +if __name__ == "__main__": + import sys + + process_highlights(sys.argv[1])