Collection of scripts to make automatic video highlights of varying quality
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
3.3 KiB

  1. #!/bin/python
  2. """jsonclips.py - generate highlights based on JSON
  3. usage: jsonclips.py clips.json
  4. JSON file can specify:
  5. - output filename
  6. - source filepath
  7. - start time
  8. - duration
  9. - transition type [default: luma]
  10. - transition duration [default: 30]
  11. """
  12. DEFAULT_TRANSITION_DURATION = 30 # 30 frames, ie 0.5s at 60FPS
  13. DEFAULT_TRANSITION_TYPE = "luma"
  14. def process_highlights(jsonfile):
  15. """Go through highlights, make clips, join to a video"""
  16. import tempfile
  17. def make_clips(sources=None, outputdir=None):
  18. """Use ffmpeg to create output from processed JSON"""
  19. import subprocess
  20. import os
  21. ffmpeg_common_args = ["ffmpeg", "-loglevel", "quiet", "-hide_banner"]
  22. clipnum = 0
  23. for source in sources:
  24. print("Making {}s clip from {} at {}".format(
  25. source["duration"], source["filepath"], source["time"]))
  26. ffmpeg_file_args = ["-ss", str(source["time"]),
  27. "-i", source["filepath"],
  28. "-t", str(source["duration"]),
  29. "-c", "copy",
  30. os.path.join(outputdir,
  31. "clip{}.mkv".format(clipnum))]
  32. ffmpeg_args = ffmpeg_common_args + ffmpeg_file_args
  33. print("Running ffmpeg with args:\n\n{}".
  34. format(ffmpeg_args))
  35. subprocess.run(ffmpeg_args)
  36. clipnum += 1
  37. def make_output(workingdir=None, outputfile=None, numsources=None):
  38. """Use MLT (melt) to join clips"""
  39. import subprocess
  40. import os
  41. melt_args = ["melt"]
  42. for i in range(numsources):
  43. if i == 0:
  44. melt_args.extend([os.path.join(workingdir, "clip{}.mkv"
  45. .format(i))])
  46. else:
  47. melt_args.extend([
  48. os.path.join(workingdir, "clip{}.mkv".format(i)),
  49. "-mix", str(DEFAULT_TRANSITION_DURATION),
  50. "-mixer", str(DEFAULT_TRANSITION_TYPE)])
  51. melt_args.extend(["-consumer", "avformat:{}".format(outputfile),
  52. "crf=20"])
  53. print("Running melt with args:\n\n{}".format(melt_args))
  54. subprocess.run(melt_args)
  55. def parse_json(jsonfile=None):
  56. """Parse global / per--clip options from jsonfile
  57. Requires:
  58. - outputfile
  59. - list of sources
  60. """
  61. import json
  62. import sys
  63. if jsonfile:
  64. with open(jsonfile, "r") as fh:
  65. json_in = json.load(fh)
  66. if "outputfile" not in json_in:
  67. print("Please specify an outputfile to write to")
  68. sys.exit(1)
  69. if "sources" not in json_in:
  70. print("Please specify some sources to process")
  71. sys.exit(1)
  72. return json_in
  73. # Start
  74. if jsonfile:
  75. json_in = parse_json(jsonfile)
  76. # in temporary dir
  77. with tempfile.TemporaryDirectory() as tmpdir:
  78. # make clips
  79. make_clips(sources=json_in["sources"], outputdir=tmpdir)
  80. # join clips using melt
  81. make_output(workingdir=tmpdir, outputfile=json_in["outputfile"],
  82. numsources=len(json_in["sources"]))
  83. if __name__ == "__main__":
  84. import sys
  85. process_highlights(sys.argv[1])