mirror of
https://github.com/AnimeThemes/animethemes-batch-encoder.git
synced 2026-07-11 01:24:29 +02:00
feat: new prompts and ffmpeg commands (#21)
* Version 1.3 * Back changes and fixed bug * README update
This commit is contained in:
@@ -25,15 +25,13 @@ Ideally we are iterating over a combination of filters and settings, picking the
|
||||
|
||||
`--mode 1` generates commands from input files in the current directory.
|
||||
|
||||
The user will be prompted for values that are not determined programmatically, such as inclusion/exclusion of a source file candidate, start time, end time and output file name.
|
||||
The user will be prompted for values that are not determined programmatically, such as inclusion/exclusion of a source file candidate, start time, end time, output file name and new audio filters.
|
||||
|
||||
`--mode 2` executes commands from file in the current directory line-by-line.
|
||||
|
||||
By default, the program looks for a file named `commands.txt` in the current directory. This file name can be specified by the `--file` argument.
|
||||
|
||||
`--mode 3` generates commands from input files in the current directory and executes the commands sequentially without writing to file.
|
||||
|
||||
The `--file` argument will be ignored in this case.
|
||||
`--mode 3` generates commands from input files in the current directory and executes the commands sequentially.
|
||||
|
||||
**File**
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ def main():
|
||||
parser.add_argument('--file', nargs='?', default='commands.txt', type=commandfile_arg_type,
|
||||
help='1: Name of file commands are written to (default: commands.txt)\n'
|
||||
'2: Name of file commands are executed from (default: commands.txt)\n'
|
||||
'3: Unused')
|
||||
'3: Name of file commands are written to (default: commands.txt)')
|
||||
parser.add_argument('--configfile', nargs='?', default='batch_encoder.ini', type=configfile_arg_type,
|
||||
help='Name of config file (default: batch_encoder.ini)\n'
|
||||
'If the file does not exist, default configuration will be written\n'
|
||||
@@ -113,13 +113,19 @@ def main():
|
||||
|
||||
commands = output_list
|
||||
|
||||
# Write commands to file
|
||||
if args.mode == 1:
|
||||
# Write commands to file
|
||||
logging.info(f'Writing {len(commands)} commands to file \'{args.file}\'...')
|
||||
with open(args.file, mode='w', encoding='utf8') as f:
|
||||
for command in commands:
|
||||
f.write(command + '\n')
|
||||
|
||||
# Execute commands in memory and write commands to file if requested
|
||||
if args.mode == 3:
|
||||
logging.info(f'Executing {len(commands)} commands...')
|
||||
for command in commands:
|
||||
subprocess.call(command, shell=True)
|
||||
|
||||
|
||||
# Read and execute commands from file
|
||||
if args.mode == 2:
|
||||
if not os.path.isfile(args.file):
|
||||
@@ -135,13 +141,6 @@ def main():
|
||||
for command in commands:
|
||||
subprocess.call(command, shell=True)
|
||||
|
||||
# Execute commands in memory
|
||||
if args.mode == 3:
|
||||
logging.info(f'Executing {len(commands)} commands...')
|
||||
for command in commands:
|
||||
subprocess.call(command, shell=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
|
||||
@@ -104,6 +104,12 @@ class EncodeWebM:
|
||||
limit_size = max_allowed_size / 8
|
||||
|
||||
return str(round(limit_size))
|
||||
|
||||
# Command to preview a seek
|
||||
def preview_seek(self, webm_filename=''):
|
||||
return f'ffmpeg {self.seek.get_seek_string()} ' \
|
||||
f'{self.get_audio_filters()} ' \
|
||||
f'-vcodec copy -c:a aac -b:a 128k -sn -f mp4 {webm_filename}.mp4'
|
||||
|
||||
# First-pass encode
|
||||
def get_first_pass(self, encoding_mode, crf=None, threads=4):
|
||||
@@ -137,6 +143,7 @@ class EncodeWebM:
|
||||
audio_filters = []
|
||||
self.source_file.apply_audio_resampling(audio_filters)
|
||||
audio_filters.append(self.loudnorm_filter.get_normalization_filter())
|
||||
audio_filters.append(self.seek.new_audio_filter) if self.seek.new_audio_filter.strip() != '' else None
|
||||
return '-af ' + ','.join(audio_filters)
|
||||
|
||||
# Build video filtergraph for encodes
|
||||
@@ -177,6 +184,8 @@ class EncodeWebM:
|
||||
f'crfs: \'{encoding_config.crfs}\', '
|
||||
f'include_unfiltered: \'{encoding_config.include_unfiltered}\', '
|
||||
f'video_filters: \'{encoding_config.video_filters}\'')
|
||||
|
||||
file_commands.append(self.preview_seek(webm_filename=self.get_webm_filename()))
|
||||
|
||||
for encoding_mode in encoding_config.encoding_modes:
|
||||
if BitrateMode.CBR.name == encoding_mode.upper():
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# The seek information for our encode
|
||||
class Seek:
|
||||
def __init__(self, source_file, ss, to, output_name):
|
||||
def __init__(self, source_file, ss, to, output_name, new_audio_filter):
|
||||
self.source_file = source_file
|
||||
self.ss = ss
|
||||
self.to = to
|
||||
self.output_name = output_name
|
||||
self.new_audio_filter = new_audio_filter
|
||||
|
||||
# The seek string arguments for our encode
|
||||
def get_seek_string(self):
|
||||
|
||||
@@ -15,11 +15,15 @@ class SeekCollector:
|
||||
# Examples: Tamayura-OP1, PlanetWith-ED1v2
|
||||
filename_pattern = re.compile('^[a-zA-Z0-9\-]+$')
|
||||
|
||||
# List for all output_files
|
||||
all_output_names = []
|
||||
|
||||
def __init__(self, source_file):
|
||||
self.source_file = source_file
|
||||
self.start_positions = SeekCollector.prompt_time('Start time(s): ')
|
||||
self.end_positions = SeekCollector.prompt_time('End time(s): ')
|
||||
self.output_names = SeekCollector.prompt_output_name()
|
||||
self.new_audio_filters = SeekCollector.prompt_new_audio_filters()
|
||||
|
||||
# Prompt the user for our list of starting/ending positions of our WebMs
|
||||
# For starting positions, a blank input value is the 0 position of the source file
|
||||
@@ -50,6 +54,12 @@ class SeekCollector:
|
||||
break
|
||||
if not invalid_name:
|
||||
return filenames
|
||||
|
||||
# Prompt the user for ours list of audio filters of ours WebMs
|
||||
@staticmethod
|
||||
def prompt_new_audio_filters():
|
||||
new_audio_filters = input('Audio Filter(s): ').split(',,')
|
||||
return new_audio_filters
|
||||
|
||||
# Integrity Test 1: Our lists should be of equal length
|
||||
def is_length_consistent(self):
|
||||
@@ -99,6 +109,16 @@ class SeekCollector:
|
||||
|
||||
return len(set(self.output_names)) == len(self.output_names)
|
||||
|
||||
# Integrity Test 5: Unique output names in other source
|
||||
def is_unique_output_names_other_source(self):
|
||||
self.all_output_names.extend(self.output_names)
|
||||
|
||||
logging.debug(
|
||||
f'[SeekCollector.is_unique_output_names_other_source] len(set): \'{len(set(self.all_output_names))}\', '
|
||||
f'len: \'{len(self.all_output_names)}\'')
|
||||
|
||||
return len(set(self.all_output_names)) == len(self.all_output_names)
|
||||
|
||||
# Integrity Tests with feedback
|
||||
def is_valid(self):
|
||||
is_valid = True
|
||||
@@ -119,15 +139,22 @@ class SeekCollector:
|
||||
is_valid = False
|
||||
logging.error('Output Names are not unique')
|
||||
|
||||
if not self.is_unique_output_names_other_source():
|
||||
is_valid = False
|
||||
logging.error('Output Name is already being used')
|
||||
|
||||
if not is_valid:
|
||||
self.all_output_names.pop()
|
||||
|
||||
return is_valid
|
||||
|
||||
# Our list of positions, validated if called after is_valid
|
||||
def get_seek_list(self):
|
||||
seek_list = []
|
||||
|
||||
for start_position, end_position, output_name in zip(self.start_positions, self.end_positions,
|
||||
self.output_names):
|
||||
seek = Seek(self.source_file, start_position, end_position, output_name)
|
||||
for start_position, end_position, output_name, new_audio_filter in zip(self.start_positions, self.end_positions,
|
||||
self.output_names, self.new_audio_filters):
|
||||
seek = Seek(self.source_file, start_position, end_position, output_name, new_audio_filter)
|
||||
seek_list.append(seek)
|
||||
|
||||
return seek_list
|
||||
|
||||
Reference in New Issue
Block a user