From e178925f7f0dc5c74f76ba81eaba6768635724af Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sun, 28 Aug 2022 15:37:49 +0200 Subject: [PATCH 01/14] Initial commit --- easyffmpeg/.gitignore | 162 ++++++++++++++++++++++++++++++++++++++++++ easyffmpeg/README.md | 2 + 2 files changed, 164 insertions(+) create mode 100644 easyffmpeg/.gitignore create mode 100644 easyffmpeg/README.md diff --git a/easyffmpeg/.gitignore b/easyffmpeg/.gitignore new file mode 100644 index 00000000..5d381cc4 --- /dev/null +++ b/easyffmpeg/.gitignore @@ -0,0 +1,162 @@ +# ---> Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/easyffmpeg/README.md b/easyffmpeg/README.md new file mode 100644 index 00000000..576a7b0a --- /dev/null +++ b/easyffmpeg/README.md @@ -0,0 +1,2 @@ +# config-easyffmpeg + From d8fcc4d7d601636aa8c25847f6f0fb9d119694a3 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sun, 28 Aug 2022 17:06:47 +0200 Subject: [PATCH 02/14] Initial version --- easyffmpeg/main.py | 129 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 easyffmpeg/main.py diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py new file mode 100755 index 00000000..6c9c7e3c --- /dev/null +++ b/easyffmpeg/main.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +# ffmpeg wrapper +import ffmpy +# argument parsing +import argparse + +parser = argparse.ArgumentParser(description='') + +# Input file +parser.add_argument("-i", "--input-file", required=True, type=str, + help="Input file") + +# Media title +parser.add_argument("-t", "--title", required=True, type=str, + help="Media title") + +# Video stuff +parser.add_argument("-vc", "--video-codec", required=False, type=str, + help="Output video codec. Defaults to 'copy'") +parser.add_argument("-crf", "--crf", required=False, type=int, + help="Codec crf. No effect if video codec is 'copy'. Defaults to 20") +parser.add_argument("-vt", "--video-tune", required=False, type=str, + help="Video codec tune") +parser.add_argument("-vp", "--video-preset", required=False, type=str, + help="Video compression preset. Defaults to 'medium'") + +# Audio stuff +parser.add_argument("-ac", "--audio-codec", required=False, type=str, + help="Output audio codec. Defaults to 'copy'") +parser.add_argument("-ab", "--audio-bitrate", required=False, type=str, + help="Output audio bitrate. No effect if audio codec is 'copy'. Defaults to '192k'") +parser.add_argument("-aj", "--audio-japanese", required=True, type=str, + help="Stream identifier for japanese audio") +parser.add_argument("-ae", "--audio-english", required=False, type=str, + help="Stream identifier for english audio") + +# Subtitle stuff +parser.add_argument("-sn", "--subtitle-name", required=True, type=str, + help="Name for subtitles") +parser.add_argument("-si", "--subtitle-stream", required=True, type=str, + help="Stream identifier for subtitles") + +# Output file +parser.add_argument("-o", "--output-file", required=True, type=str, + help="Output file") + +# Execute or print commands +parser.add_argument("-e", "--execute", action="store_true", + help="Execute script. If not set, shows the commands that would be run.") + +args = parser.parse_args() + +title = args.title + +inputfile = args.input_file + +# Default video codec is copy +if args.video_codec is None: + videocodec = "copy" +else: + videocodec = args.video_codec + +# Default crf of 20 +if args.crf is None: + crf = 20 +else: + crf = args.crf + +if args.video_tune is None: + tune = "" +else: + tune = "-tune " + args.video_tune + +if args.video_preset is None: + preset = "medium" +else: + preset = args.video_preset + +# Default audio codec is copy +if args.audio_codec is None: + audiocodec = "copy" +else: + audiocodec = args.audio_codec + +# Default audio codec is copy +if args.audio_bitrate is None: + audiobitrate = "192k" +else: + audiobitrate = args.audio_bitrate + +outputfile = args.output_file + +japaneseaudio = args.audio_japanese + +if args.audio_english is None: + englishaudio = "" +else: + englishaudio = "-map " + args.audio_english + +subtitle = args.subtitle_name +subtitlestream = args.subtitle_stream + +execute = args.execute + +ff = ffmpy.FFmpeg(inputs={inputfile:None}, + outputs={outputfile: "-metadata title='{title}' -disposition 0"" " + "-c:v {videocodec} -crf {crf} {tune} -preset {preset} -map 0:v:0 -metadata:s:v:0 title='Video' -disposition:v:0 default"" " + "-c:a {audiocodec} -b:a {audiobitrate} -map {jpnaudiostream}? {engaudiomap}"" " + "-metadata:s:a:0 title='Japanese' -metadata:s:a:0 language=jpn -metadata:s:a:1 title='English' -metadata:s:a:1 language=eng -disposition:a:0 default"" " + "-c:s copy -map {substream} -metadata:s:s:0 title='{subtitle}' -metadata:s:s:0 language=eng -disposition:s:0 default"" " + .format(title=title, + videocodec=videocodec, + crf=crf, + tune=tune, + preset=preset, + audiocodec=audiocodec, + audiobitrate=audiobitrate, + jpnaudiostream=japaneseaudio, + engaudiomap=englishaudio, + substream=subtitlestream, + subtitle=subtitle) + } + ) + +if execute: + ff.run() +else: + print(ff.cmd) From 59f87930ce06292ec77f4c8f06b14621117eb138 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sun, 28 Aug 2022 17:09:47 +0200 Subject: [PATCH 03/14] Add note for why this isn't useful --- easyffmpeg/.gitignore | 1 + easyffmpeg/README.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/easyffmpeg/.gitignore b/easyffmpeg/.gitignore index 5d381cc4..3d63286c 100644 --- a/easyffmpeg/.gitignore +++ b/easyffmpeg/.gitignore @@ -160,3 +160,4 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +README.md.tmp diff --git a/easyffmpeg/README.md b/easyffmpeg/README.md index 576a7b0a..338839ae 100644 --- a/easyffmpeg/README.md +++ b/easyffmpeg/README.md @@ -1,2 +1,3 @@ # config-easyffmpeg - +This script wraps the ffmpeg command I'm using often so I have to type less. +Probably not useful to anyone else, as it includes some hardcoded values for stuff I never change/want to keep constant in my media. From dfeb97df7fe630379f1c38dbd26f9a25ac352d84 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sun, 28 Aug 2022 21:19:05 +0200 Subject: [PATCH 04/14] Add comments --- easyffmpeg/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index 6c9c7e3c..f24075ce 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -93,6 +93,7 @@ outputfile = args.output_file japaneseaudio = args.audio_japanese +# Map english audio, if set if args.audio_english is None: englishaudio = "" else: @@ -101,6 +102,7 @@ else: subtitle = args.subtitle_name subtitlestream = args.subtitle_stream +# Flag to actually execute command execute = args.execute ff = ffmpy.FFmpeg(inputs={inputfile:None}, From 1a1671ed2b8d2000fb18061444aa8298e6507eaf Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sun, 25 Sep 2022 14:18:04 +0200 Subject: [PATCH 05/14] Add requirements file --- easyffmpeg/requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 easyffmpeg/requirements.txt diff --git a/easyffmpeg/requirements.txt b/easyffmpeg/requirements.txt new file mode 100644 index 00000000..cb25c650 --- /dev/null +++ b/easyffmpeg/requirements.txt @@ -0,0 +1 @@ +ffmpy From cea5b2aa9d28872019bd9a49af8f3a842282cda3 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Tue, 15 Nov 2022 17:23:55 +0100 Subject: [PATCH 06/14] Note about broken filename with single quotes --- easyffmpeg/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index f24075ce..41b6ff12 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -124,6 +124,7 @@ ff = ffmpy.FFmpeg(inputs={inputfile:None}, subtitle=subtitle) } ) +# NOTE Breaks if filename contains quotes: ' if execute: ff.run() From 0b7fd8468bd91cd061c3ea5f231a0e2b580b6cd6 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Tue, 15 Nov 2022 17:24:09 +0100 Subject: [PATCH 07/14] Reformat with black --- easyffmpeg/main.py | 148 ++++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 48 deletions(-) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index 41b6ff12..291926a6 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -2,52 +2,96 @@ # ffmpeg wrapper import ffmpy + # argument parsing import argparse -parser = argparse.ArgumentParser(description='') +parser = argparse.ArgumentParser(description="") # Input file -parser.add_argument("-i", "--input-file", required=True, type=str, - help="Input file") +parser.add_argument("-i", "--input-file", required=True, type=str, help="Input file") # Media title -parser.add_argument("-t", "--title", required=True, type=str, - help="Media title") +parser.add_argument("-t", "--title", required=True, type=str, help="Media title") # Video stuff -parser.add_argument("-vc", "--video-codec", required=False, type=str, - help="Output video codec. Defaults to 'copy'") -parser.add_argument("-crf", "--crf", required=False, type=int, - help="Codec crf. No effect if video codec is 'copy'. Defaults to 20") -parser.add_argument("-vt", "--video-tune", required=False, type=str, - help="Video codec tune") -parser.add_argument("-vp", "--video-preset", required=False, type=str, - help="Video compression preset. Defaults to 'medium'") +parser.add_argument( + "-vc", + "--video-codec", + required=False, + type=str, + help="Output video codec. Defaults to 'copy'", +) +parser.add_argument( + "-crf", + "--crf", + required=False, + type=int, + help="Codec crf. No effect if video codec is 'copy'. Defaults to 20", +) +parser.add_argument( + "-vt", "--video-tune", required=False, type=str, help="Video codec tune" +) +parser.add_argument( + "-vp", + "--video-preset", + required=False, + type=str, + help="Video compression preset. Defaults to 'medium'", +) # Audio stuff -parser.add_argument("-ac", "--audio-codec", required=False, type=str, - help="Output audio codec. Defaults to 'copy'") -parser.add_argument("-ab", "--audio-bitrate", required=False, type=str, - help="Output audio bitrate. No effect if audio codec is 'copy'. Defaults to '192k'") -parser.add_argument("-aj", "--audio-japanese", required=True, type=str, - help="Stream identifier for japanese audio") -parser.add_argument("-ae", "--audio-english", required=False, type=str, - help="Stream identifier for english audio") +parser.add_argument( + "-ac", + "--audio-codec", + required=False, + type=str, + help="Output audio codec. Defaults to 'copy'", +) +parser.add_argument( + "-ab", + "--audio-bitrate", + required=False, + type=str, + help="Output audio bitrate. No effect if audio codec is 'copy'. Defaults to '192k'", +) +parser.add_argument( + "-aj", + "--audio-japanese", + required=True, + type=str, + help="Stream identifier for japanese audio", +) +parser.add_argument( + "-ae", + "--audio-english", + required=False, + type=str, + help="Stream identifier for english audio", +) # Subtitle stuff -parser.add_argument("-sn", "--subtitle-name", required=True, type=str, - help="Name for subtitles") -parser.add_argument("-si", "--subtitle-stream", required=True, type=str, - help="Stream identifier for subtitles") +parser.add_argument( + "-sn", "--subtitle-name", required=True, type=str, help="Name for subtitles" +) +parser.add_argument( + "-si", + "--subtitle-stream", + required=True, + type=str, + help="Stream identifier for subtitles", +) # Output file -parser.add_argument("-o", "--output-file", required=True, type=str, - help="Output file") +parser.add_argument("-o", "--output-file", required=True, type=str, help="Output file") # Execute or print commands -parser.add_argument("-e", "--execute", action="store_true", - help="Execute script. If not set, shows the commands that would be run.") +parser.add_argument( + "-e", + "--execute", + action="store_true", + help="Execute script. If not set, shows the commands that would be run.", +) args = parser.parse_args() @@ -105,26 +149,34 @@ subtitlestream = args.subtitle_stream # Flag to actually execute command execute = args.execute -ff = ffmpy.FFmpeg(inputs={inputfile:None}, - outputs={outputfile: "-metadata title='{title}' -disposition 0"" " - "-c:v {videocodec} -crf {crf} {tune} -preset {preset} -map 0:v:0 -metadata:s:v:0 title='Video' -disposition:v:0 default"" " - "-c:a {audiocodec} -b:a {audiobitrate} -map {jpnaudiostream}? {engaudiomap}"" " - "-metadata:s:a:0 title='Japanese' -metadata:s:a:0 language=jpn -metadata:s:a:1 title='English' -metadata:s:a:1 language=eng -disposition:a:0 default"" " - "-c:s copy -map {substream} -metadata:s:s:0 title='{subtitle}' -metadata:s:s:0 language=eng -disposition:s:0 default"" " - .format(title=title, - videocodec=videocodec, - crf=crf, - tune=tune, - preset=preset, - audiocodec=audiocodec, - audiobitrate=audiobitrate, - jpnaudiostream=japaneseaudio, - engaudiomap=englishaudio, - substream=subtitlestream, - subtitle=subtitle) - } - ) # NOTE Breaks if filename contains quotes: ' +ff = ffmpy.FFmpeg( + inputs={inputfile: None}, + outputs={ + outputfile: "-metadata title='{title}' -disposition 0" + " " + "-c:v {videocodec} -crf {crf} {tune} -preset {preset} -map 0:v:0 -metadata:s:v:0 title='Video' -disposition:v:0 default" + " " + "-c:a {audiocodec} -b:a {audiobitrate} -map {jpnaudiostream}? {engaudiomap}" + " " + "-metadata:s:a:0 title='Japanese' -metadata:s:a:0 language=jpn -metadata:s:a:1 title='English' -metadata:s:a:1 language=eng -disposition:a:0 default" + " " + "-c:s copy -map {substream} -metadata:s:s:0 title='{subtitle}' -metadata:s:s:0 language=eng -disposition:s:0 default" + " ".format( + title=title, + videocodec=videocodec, + crf=crf, + tune=tune, + preset=preset, + audiocodec=audiocodec, + audiobitrate=audiobitrate, + jpnaudiostream=japaneseaudio, + engaudiomap=englishaudio, + substream=subtitlestream, + subtitle=subtitle, + ) + }, +) if execute: ff.run() From 3689ecd8d4ea8db4cd6905f1184ec16e460e71ef Mon Sep 17 00:00:00 2001 From: RealStickman Date: Fri, 16 Dec 2022 14:51:10 +0100 Subject: [PATCH 08/14] WIP add check if duration is set/valid --- easyffmpeg/main.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index 291926a6..f63cb0c3 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # ffmpeg wrapper +import subprocess import ffmpy # argument parsing @@ -182,3 +183,28 @@ if execute: ff.run() else: print(ff.cmd) + +# NOTE check all files for an intact/valid duration +# Valid file example output: +# { +# "format": { +# "duration": "1425.058000" +# } +# } +# Invalid file: +# { +# "format": { +# +# } +# } +ff = ffmpy.FFprobe( + inputs={outputfile: None}, + global_options=("-show_entries format=duration -v quiet -print_format json"), +) + +proc = subprocess.Popen( + ff.cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True +) + +# TODO continue here +print(proc.stdout.read().rstrip().decode("utf8")) From c71a71d7646e1658b1e3676373525e15a618ccc4 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sat, 17 Dec 2022 16:26:51 +0100 Subject: [PATCH 09/14] Put duration validity check in separate function. Input and Output side --- easyffmpeg/main.py | 76 +++++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index f63cb0c3..8b884160 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -1,12 +1,57 @@ #!/usr/bin/env python3 -# ffmpeg wrapper import subprocess + +import json + +import csv + +# ffmpeg wrapper import ffmpy # argument parsing import argparse + +def valid_duration(filename: str, filetype: str): + """ + Check given file for presence of duration metadata + + Parameters: + filename (str): Path to file + filetype (str): Should be INPUT or OUTPUT, to define if the issue appeared after encoding or before + """ + # NOTE check all files for an intact/valid duration + # Valid file example output: + # { + # "format": { + # "duration": "1425.058000" + # } + # } + # Invalid file: + # { + # "format": { + # + # } + # } + ff = ffmpy.FFprobe( + inputs={filename: None}, + global_options=("-show_entries format=duration -v quiet -print_format json"), + ) + + proc = subprocess.Popen( + ff.cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True + ) + + info = json.loads(proc.stdout.read().rstrip().decode("utf8")) + + # write broken files to error.csv files in current directory + if "duration" not in info["format"]: + with open("error.csv", "a", newline="") as file: + write = csv.writer(file) + write.writerow((filetype, filename)) + + parser = argparse.ArgumentParser(description="") # Input file @@ -150,6 +195,9 @@ subtitlestream = args.subtitle_stream # Flag to actually execute command execute = args.execute +# check input file for valid duration +valid_duration(inputfile, "INPUT") + # NOTE Breaks if filename contains quotes: ' ff = ffmpy.FFmpeg( inputs={inputfile: None}, @@ -184,27 +232,5 @@ if execute: else: print(ff.cmd) -# NOTE check all files for an intact/valid duration -# Valid file example output: -# { -# "format": { -# "duration": "1425.058000" -# } -# } -# Invalid file: -# { -# "format": { -# -# } -# } -ff = ffmpy.FFprobe( - inputs={outputfile: None}, - global_options=("-show_entries format=duration -v quiet -print_format json"), -) - -proc = subprocess.Popen( - ff.cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True -) - -# TODO continue here -print(proc.stdout.read().rstrip().decode("utf8")) +# check output file for valid duration +valid_duration(outputfile, "OUTPUT") From 1b26cb5605d3a288255738a833dc611b8a18e23a Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sat, 17 Dec 2022 16:32:13 +0100 Subject: [PATCH 10/14] Add note to check error.csv --- easyffmpeg/main.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index 8b884160..537d6df5 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -4,6 +4,8 @@ import subprocess import json +import os + import csv # ffmpeg wrapper @@ -234,3 +236,8 @@ else: # check output file for valid duration valid_duration(outputfile, "OUTPUT") + +if os.path.isfile("error.csv"): + print( + "Some media might have errors. Please check the error.csv file in this directory" + ) From 3b1a6a8329d6a3d90225aa969de8152e47603fe7 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sat, 17 Dec 2022 17:04:38 +0100 Subject: [PATCH 11/14] Allow handling more generic content --- easyffmpeg/main.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index 537d6df5..898b3aec 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -106,7 +106,7 @@ parser.add_argument( parser.add_argument( "-aj", "--audio-japanese", - required=True, + required=False, type=str, help="Stream identifier for japanese audio", ) @@ -120,12 +120,12 @@ parser.add_argument( # Subtitle stuff parser.add_argument( - "-sn", "--subtitle-name", required=True, type=str, help="Name for subtitles" + "-sn", "--subtitle-name", required=False, type=str, help="Name for subtitles" ) parser.add_argument( "-si", "--subtitle-stream", - required=True, + required=False, type=str, help="Stream identifier for subtitles", ) @@ -183,7 +183,11 @@ else: outputfile = args.output_file -japaneseaudio = args.audio_japanese +# Map japanese audio, if set +if args.audio_japanese is None: + japaneseaudio = "" +else: + japaneseaudio = "-map " + args.audio_japanese # Map english audio, if set if args.audio_english is None: @@ -191,6 +195,12 @@ if args.audio_english is None: else: englishaudio = "-map " + args.audio_english +# Audiometadata +if args.audio_japanese is None: + audiometa = "-metadata:s:a:0 title='English' -metadata:s:a:0 language=eng" +else: + audiometa = "-metadata:s:a:0 title='Japanese' -metadata:s:a:0 language=jpn -metadata:s:a:1 title='English' -metadata:s:a:1 language=eng" + subtitle = args.subtitle_name subtitlestream = args.subtitle_stream @@ -208,11 +218,11 @@ ff = ffmpy.FFmpeg( " " "-c:v {videocodec} -crf {crf} {tune} -preset {preset} -map 0:v:0 -metadata:s:v:0 title='Video' -disposition:v:0 default" " " - "-c:a {audiocodec} -b:a {audiobitrate} -map {jpnaudiostream}? {engaudiomap}" + "-c:a {audiocodec} -b:a {audiobitrate} {jpnaudiomap} {engaudiomap}" " " - "-metadata:s:a:0 title='Japanese' -metadata:s:a:0 language=jpn -metadata:s:a:1 title='English' -metadata:s:a:1 language=eng -disposition:a:0 default" + "{audiometa} -disposition:a:0 default" " " - "-c:s copy -map {substream} -metadata:s:s:0 title='{subtitle}' -metadata:s:s:0 language=eng -disposition:s:0 default" + "-c:s copy -map {substream}? -metadata:s:s:0 title='{subtitle}' -metadata:s:s:0 language=eng -disposition:s:0 default" " ".format( title=title, videocodec=videocodec, @@ -221,8 +231,9 @@ ff = ffmpy.FFmpeg( preset=preset, audiocodec=audiocodec, audiobitrate=audiobitrate, - jpnaudiostream=japaneseaudio, + jpnaudiomap=japaneseaudio, engaudiomap=englishaudio, + audiometa=audiometa, substream=subtitlestream, subtitle=subtitle, ) From dd2963c2e9994bedf25502b38706ce8e90f08162 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Mon, 19 Dec 2022 20:46:10 +0100 Subject: [PATCH 12/14] Add cli flag to set first subtitle as default --- easyffmpeg/main.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index 898b3aec..5d5f3bfd 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -130,6 +130,14 @@ parser.add_argument( help="Stream identifier for subtitles", ) +parser.add_argument( + "-sd", + "--set-default-subtitle", + required=False, + action="store_true", + help="If passed, set the first subtitle as default", +) + # Output file parser.add_argument("-o", "--output-file", required=True, type=str, help="Output file") @@ -204,6 +212,11 @@ else: subtitle = args.subtitle_name subtitlestream = args.subtitle_stream +if args.set_default_subtitle: + defaultsub = "-disposition:s:0 default" +else: + defaultsub = "" + # Flag to actually execute command execute = args.execute @@ -222,7 +235,7 @@ ff = ffmpy.FFmpeg( " " "{audiometa} -disposition:a:0 default" " " - "-c:s copy -map {substream}? -metadata:s:s:0 title='{subtitle}' -metadata:s:s:0 language=eng -disposition:s:0 default" + "-c:s copy -map {substream}? -metadata:s:s:0 title='{subtitle}' -metadata:s:s:0 language=eng {defaultsub}" " ".format( title=title, videocodec=videocodec, @@ -236,6 +249,7 @@ ff = ffmpy.FFmpeg( audiometa=audiometa, substream=subtitlestream, subtitle=subtitle, + defaultsub=defaultsub, ) }, ) From d134bc81b1f72e415067a6a79c8bd2fc016bd977 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Mon, 3 Apr 2023 16:23:19 +0200 Subject: [PATCH 13/14] Add note about quotes --- easyffmpeg/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index 5d5f3bfd..99f39da9 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -223,7 +223,7 @@ execute = args.execute # check input file for valid duration valid_duration(inputfile, "INPUT") -# NOTE Breaks if filename contains quotes: ' +# FIXME Breaks if any field (filename, title, Language) contains quotes: ' ff = ffmpy.FFmpeg( inputs={inputfile: None}, outputs={ From d61c74b35caaeb52061d3a1da27cdd1d0c8d31f7 Mon Sep 17 00:00:00 2001 From: RealStickman Date: Thu, 6 Apr 2023 20:39:48 +0200 Subject: [PATCH 14/14] map attachments in the file to the new version if present --- easyffmpeg/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/easyffmpeg/main.py b/easyffmpeg/main.py index 99f39da9..7e5723f0 100755 --- a/easyffmpeg/main.py +++ b/easyffmpeg/main.py @@ -224,10 +224,11 @@ execute = args.execute valid_duration(inputfile, "INPUT") # FIXME Breaks if any field (filename, title, Language) contains quotes: ' +# Maps the first video stream, selected audio streams, selected subtitle streams and any attachments to the remuxed/reencoded video ff = ffmpy.FFmpeg( inputs={inputfile: None}, outputs={ - outputfile: "-metadata title='{title}' -disposition 0" + outputfile: "-metadata title='{title}' -disposition 0 -map 0:t?" " " "-c:v {videocodec} -crf {crf} {tune} -preset {preset} -map 0:v:0 -metadata:s:v:0 title='Video' -disposition:v:0 default" " "