From da0d60eecf654a879393557093c78a2db6ae184e Mon Sep 17 00:00:00 2001 From: Martinez Date: Mon, 7 May 2018 01:21:48 +0200 Subject: [PATCH] - removed Theora support - removed --context - added Opus support (now default) - added VP9 support (now default) - added WebM support --- aux.py | 31 ----------------------------- over-video.py | 54 +++++++++++++++++++++++++++------------------------ version.py | 6 +++--- 3 files changed, 32 insertions(+), 59 deletions(-) diff --git a/aux.py b/aux.py index 06c9f8e..f053a6f 100644 --- a/aux.py +++ b/aux.py @@ -31,37 +31,6 @@ def to_Path(raw_path): # -------------------------------------------------- -context_file_header = """# Context file for %s-%s -# This file stores configuration valid for this directory. -# It is updated automatically with options you use. - -""" - -def update_cfg_context(main, ignore=[]): - """ - All main.options that are sourced from either the cfg file or defaults get overridden by those in .over-video. - Those that are from command line get saved into .over-video. - - File format: - name=value - """ - - options_to_consider = [option for option in main.options.values() if option.name not in ignore and option.in_cfg_file] - options_to_write = {option.name: option for option in options_to_consider if option.source == over.app.Option_sources.command_line} - options_to_read = {option.name: option for option in options_to_consider if option.source != over.app.Option_sources.command_line} - - context_file_read = over.app.ConfigFile(options_to_read, ".over-video", ignore_unknown=True) - restored = context_file_read.read_config() - if restored: - main.print("restored from .over-video<.>: %s" %(", ".join("%s<.>" %(o.name) for o in restored)), main.print.tl.note) - - context_file_write = over.app.ConfigFile(options_to_write, ".over-video") - added = context_file_write.update_config(context_file_header, (main.name, main.version)) - if added: - main.print("added to .over-video<.>: %s" %(", ".join("%s<.>" %(o.name) for o in added))) - -# -------------------------------------------------- - def float_or_string(a): try: return float(a) diff --git a/over-video.py b/over-video.py index 433ce74..6da1228 100755 --- a/over-video.py +++ b/over-video.py @@ -23,11 +23,12 @@ command = over.types.ndict() command.identify = Command("ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", "INFILE") command.normalize_prepass = Command("ffmpeg", "-i", "INFILE", "-max_muxing_queue_size", "512", "-filter:a", "loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json", "-f", "null", "/dev/null") command.encode_generic = Command("ffmpeg", "FPS", "CUT_FROM", "-i", "INFILE", "-max_muxing_queue_size", "512", "CUT_TO", "MAP", "VIDEO", "AUDIO", "-sn", "OUTFILE") +command.sub_opus = Command("-codec:a", "libopus", "NORMALIZE") command.sub_vorbis = Command("-codec:a", "libvorbis", "-qscale:a", "QUALITY", "NORMALIZE") command.sub_pcm = Command("-codec:a", "pcm_s16le", "NORMALIZE") -command.sub_theora = Command("-codec:v", "libtheora", "-qscale:v", "QUALITY", "VFILTER") command.sub_x264 = Command("PIXFMT", "-codec:v", "libx264", "-preset", "PRESET", "-crf", "QUALITY", "-profile:v", "high", "-level", "4.2", "VFILTER") -command.sub_x265 = Command("PIXFMT", "-codec:v", "libx265", "-preset", "PRESET", "-crf", "QUALITY", "VFILTER") +command.sub_x265 = Command("-codec:v", "libx265", "-preset", "PRESET", "-crf", "QUALITY", "VFILTER") +command.sub_vp9 = Command("-codec:v", "libvpx-vp9", "-crf", "QUALITY", "-b:v", "0", "VFILTER") command.sub_normalize = Command("-filter:a", "LOUDNORM_INCANTATION", "-ar", "48k") command.sub_vfilter = Command("-filter:v", "ARGS") command.force_yuv420p = Command("-pix_fmt", "yuv420p") @@ -38,24 +39,24 @@ command.sub_copy_video = Command("-codec:v", "copy") if __name__ == "__main__": main = over.app.Main("over-video", version.str, "AO-JSL", features={"config_file": True}) - main.add_option("audio", "Audio codec to use, either vorbis<.>, pcm<.>, copy<.> or drop<.>.", str, ["vorbis"], abbr="a", count=1) - main.add_option("audio-quality", "Audio encoding quality with -1<.> being the worst and 10<.> being the best.", float, [4], abbr="q", count=1) - main.add_option("video", "Video codec to use, either x265<.>, x264<.>, theora<.>, copy<.> or drop<.>.", str, ["x264"], abbr="v", count=1) - main.add_option("video-preset", "Video encoding preset, if supported by the selected encoder.", str, ["slow"], abbr="P", count=1) - main.add_option("video-quality", "Video encoding quality (CRF). Use 0<.>-10<.> for Theora (0<.> being the lowest, 5<.>-7<.> is generally watchable) and 0<.>-51<.> for x264/5 (0<.> being lossless, 18<.>-28<.> is reasonable).", float, [22], abbr="Q", count=1) - main.add_option("context", "Use .over-video file in CWD, if available, to remember encoding parameters per-directory.", bool, [True], abbr="C") + main.add_option("audio", "Audio codec to use, either opus<.>, vorbis<.>, pcm<.>, copy<.> or drop<.>.", str, ["opus"], abbr="a", count=1) + main.add_option("audio-quality", "Audio encoding quality with -1<.> being the worst and 10<.> being the best. Ignored by --audio<.> opus<.>.", float, [4], abbr="q", count=1) + main.add_option("video", "Video codec to use, either vp9<.>, x265<.>, x264<.>, copy<.> or drop<.>.", str, ["vp9"], abbr="v", count=1) + main.add_option("video-preset", "Video encoding preset to use by --video<.> x264<.> and x265<.>.", str, ["slow"], abbr="P", count=1) + main.add_option("video-quality", "Video encoding quality (CRF). Use 0<.>-51<.> for --video<.> x264<.> and x265<.> (0<.> being lossless, 18<.>-28<.> is reasonable) and 0<.>-63<.> for --video<.> vp9<.> (0<.> being highest, 15<.>-35<.> typical, and 31<.> recommended for HD video).", float, [31], abbr="Q", count=1) + main.add_option("container", "The initial container type. Either mkv<.> or webm<.> (or anything else supported by ffmpeg).", str, ["mkv"], count=1) main.add_option("normalize", "Normalize the audio track without clipping. May use dynamic range compression.", bool, [True], abbr="n") main.add_option("ffmpeg-vfilter", 'Raw ffmpeg -filter:v options, e.g. "scale=1280:trunc(ow/a/2)*2,transpose=dir=1<.>"', str, abbr="F", count=1) main.add_option("ffmpeg-map", "Raw ffmpeg -map<.> options, e.g. --map<.> 0:1<.> --map<.> 0:2<.>. This is a drop-in fix until we get proper stream selection.", str, abbr="M", overwrite=False, count=1) - main.add_option("cut", "Start and end timestamps of the portion to cut out. Uses native ffmpeg -ss<.> and -to<.> format, so it's either seconds from start or [:]:[.<...]<.>. Example: --cut<.> 25 35<.> uses 10 seconds of video starting at 25s, --cut<.> 1:10:45 1:23:54.5<.> uses video from 4245s to 5034.5s.", over.callback.strings, abbr="X", count=2) + main.add_option("cut", "Start timestamp and the duration of the portion to use. Uses native ffmpeg -ss<.> and -to<.> format, so it's either seconds from start or [:]:[.<...]<.>. Example: --cut<.> 25 10<.> uses 10 seconds of video starting at 25s, --cut<.> 1:10:45 13:9.5<.> uses video from 4245s to 5034.5s.", over.callback.strings, abbr="X", count=2) main.add_option("fps", "Override input framerate.", float, abbr="f", count=1) - main.add_option("move-source", "Move source file to this directory after conversion. Use an empty string to disable.", str, ["processed"], count=1) + main.add_option("move-source", "Move source file to this directory after conversion. Pass an empty string to disable.", str, ["processed"], count=1) main.add_option("dump-commands", "Print ffmpeg commands that would be executed. If --normalize<.> is in effect, the normalization pre-pass will still be performed so that the proper volume correction can be computed.", bool, [False], abbr="D", in_cfg_file=False) - main.add_option("probe", "Print the raw JSON output of ffprobe and exit.", bool, [False], abbr="p", in_cfg_file=False) + main.add_option("probe", "Print the raw dict (JSON-esque) output of ffprobe and exit.", bool, [False], abbr="p", in_cfg_file=False) main.add_option("armed", "Perform the suggested action.", bool, [False], abbr="A", in_cfg_file=False) - main.add_doc("Description", ["A video converter meant to coerce all video formats into HEVC+Vorbis within Matroska containers. Other uses include extracting audio from video files, resizing, cutting..."]) - main.add_doc("Good encoder settings", ["x264<.>: --video<.> x264<.> --video-preset<.> slow<.> --video-quality<.> 22<.>", "x265<.>: --video<.> x265<.> --video-preset<.> medium<.> --video-quality<.> 20<.>"]) + main.add_doc("Description", ["A video converter meant to coerce all video formats into one format with properly normalized audio. It can also be used to extract audio from video files, resizing, or very basic cutting."]) + main.add_doc("Good encoder settings", ["vp9<.>: --video<.> vp9<.> --video-quality<.> 31<.> --audio<.> opus<.> (this is the default and should provide best overall results)", "x264<.>: --video<.> x264<.> --video-preset<.> slow<.> --video-quality<.> 22<.>", "x265<.>: --video<.> x265<.> --video-preset<.> medium<.> --video-quality<.> 20<.>"]) main.setup() @@ -65,10 +66,7 @@ if __name__ == "__main__": files = over.types.ndict() audio_words = [] video_words = [] - files.container = "mkv" - - if main.cfg.context: - aux.update_cfg_context(main, ["context", "armed", "probe", "dump-commands", "ffmpeg-map"]) + files.container = main.cfg.container if main.cfg.audio in ("copy", "drop"): audio_words.append("%s<.>" %(main.cfg.audio)) @@ -99,6 +97,8 @@ if __name__ == "__main__": files.container = "wav" elif main.cfg.audio == "vorbis": files.container = "ogg" + elif main.cfg.audio == "opus": + files.container = "opus" main.print("settings", main.print.tl.start, end=":\n") main.print("audio: %s" %(", ".join(audio_words))) @@ -108,10 +108,10 @@ if __name__ == "__main__": if main.cfg.move_source: main.print("move source files to %s<.>/" %(main.cfg.move_source)) - if main.cfg.audio not in ("drop", "copy", "pcm", "vorbis"): + if main.cfg.audio not in ("drop", "copy", "pcm", "vorbis", "opus"): raise ValueError("unknown audio codec: %s" %(main.cfg.audio)) - if main.cfg.video not in ("drop", "copy", "theora", "x264", "x265"): + if main.cfg.video not in ("drop", "copy", "x264", "x265", "vp9"): raise ValueError("unknown video codec: %s" %(main.cfg.video)) if not main.targets: @@ -300,6 +300,11 @@ if __name__ == "__main__": command.sub_vorbis.NORMALIZE = info.normalize_command encode_cmd.AUDIO = command.sub_vorbis + elif main.cfg.audio == "opus": + command.sub_opus.reset() + command.sub_opus.NORMALIZE = info.normalize_command + + encode_cmd.AUDIO = command.sub_opus if main.cfg.ffmpeg_vfilter: info.vfilter_command = command.sub_vfilter @@ -323,12 +328,12 @@ if __name__ == "__main__": encode_cmd.VIDEO = command.sub_copy_video elif main.cfg.video == "drop": encode_cmd.VIDEO = "-vn" - elif main.cfg.video == "theora": - command.sub_theora.reset() - command.sub_theora.QUALITY = main.cfg.video_quality - command.sub_theora.VFILTER = info.vfilter_command + elif main.cfg.video == "vp9": + command.sub_vp9.reset() + command.sub_vp9.QUALITY = main.cfg.video_quality + command.sub_vp9.VFILTER = info.vfilter_command - encode_cmd.VIDEO = command.sub_theora + encode_cmd.VIDEO = command.sub_vp9 elif main.cfg.video == "x264": command.sub_x264.reset() command.sub_x264.QUALITY = main.cfg.video_quality @@ -347,7 +352,6 @@ if __name__ == "__main__": command.sub_x265.QUALITY = main.cfg.video_quality command.sub_x265.PRESET = main.cfg.video_preset command.sub_x265.VFILTER = info.vfilter_command - command.sub_x265.PIXFMT = None encode_cmd.VIDEO = command.sub_x265 diff --git a/version.py b/version.py index 5910c23..f8fed45 100644 --- a/version.py +++ b/version.py @@ -2,7 +2,7 @@ # encoding: utf-8 major = 1 # VERSION_MAJOR_IDENTIFIER -minor = 102 # VERSION_MINOR_IDENTIFIER -# VERSION_LAST_MM 1.102 -patch = 3 # VERSION_PATCH_IDENTIFIER +minor = 110 # VERSION_MINOR_IDENTIFIER +# VERSION_LAST_MM 1.110 +patch = 0 # VERSION_PATCH_IDENTIFIER str = ".".join(str(v) for v in (major, minor, patch))