mirror of
https://github.com/AnimeThemes/animethemes-batch-encoder.git
synced 2026-07-25 08:14:17 +02:00
219 lines
9.4 KiB
Python
219 lines
9.4 KiB
Python
from ._audio_filter import AudioFilter
|
|
from ._bitrate_mode import BitrateMode
|
|
from ._typing import Args, EncodingConfigType
|
|
from ._video_filter import VideoFilter
|
|
from typing import Literal
|
|
|
|
import inquirer
|
|
import logging
|
|
import re
|
|
import sys
|
|
|
|
|
|
# Use the inquirer lib to create a CLI
|
|
class CLI:
|
|
# Time Duration Specification: https://ffmpeg.org/ffmpeg-utils.html#time-duration-syntax
|
|
time_pattern = re.compile('^([0-5]?\d:){1,2}[0-5]?\d(?=\.\d+$|$)|\d+(?=\.\d+$|$)')
|
|
|
|
# Validations
|
|
validate_time = lambda _, x: all(CLI.time_pattern.match(y) for y in x.split(','))
|
|
validate_encoding_modes = lambda _, x: all(y.strip().upper() in [BitrateMode.VBR.name, BitrateMode.CBR.name, BitrateMode.CQ.name] for y in x.split(','))
|
|
validate_digits = lambda _, x: all(y.strip().isdigit() for y in x.split(',')) or len(x.strip()) == 0
|
|
|
|
# Prompt the user for text questions
|
|
def prompt_text(message, validate=lambda _, x: x) -> str:
|
|
answer = inquirer.prompt([inquirer.Text('text', message=message, validate=validate)])
|
|
|
|
if answer is None:
|
|
return 'NoName'
|
|
|
|
logging.debug(f'[CLI.prompt_text] answer["text"]: \'{answer["text"]}\'')
|
|
|
|
return answer['text']
|
|
|
|
# Prompt the user for time questions
|
|
def prompt_time(message, validate=validate_time) -> str:
|
|
answer = inquirer.prompt([inquirer.Text('time', message=message, validate=validate)])
|
|
|
|
if answer is None:
|
|
return ''
|
|
|
|
logging.debug(f'[CLI.prompt_time] answer["time"]: \'{answer["time"]}\'')
|
|
|
|
return answer['time']
|
|
|
|
# Prompt the user for our mode options to run to the user
|
|
def choose_mode(args: Args) -> Literal[1, 2, 3]:
|
|
modes = [
|
|
('Generate commands', 1),
|
|
('Execute commands', 2),
|
|
('Generate and execute commands', 3)
|
|
]
|
|
|
|
if args.generate and args.execute:
|
|
return 3
|
|
elif args.generate:
|
|
return 1
|
|
elif args.execute:
|
|
return 2
|
|
else:
|
|
answer = inquirer.prompt([inquirer.List('mode', message='Mode (Enter)', choices=modes)])
|
|
|
|
if answer is None:
|
|
sys.exit()
|
|
|
|
logging.debug(f'[CLI.choose_mode] answer["mode"]: \'{answer["mode"]}\'')
|
|
|
|
return answer['mode']
|
|
|
|
# Prompt the user for source files to choose
|
|
def choose_source_files(source_files) -> list[str]:
|
|
answer = inquirer.prompt([inquirer.Checkbox('source_files', message='Source Files (Space to select)', choices=source_files)])
|
|
|
|
if answer is None:
|
|
sys.exit()
|
|
|
|
if len(answer['source_files']) == 0:
|
|
logging.error('Select at least one file')
|
|
sys.exit()
|
|
|
|
logging.debug(f'[CLI.choose_source_files] answer["source_files"]: \'{answer["source_files"]}\'')
|
|
|
|
return answer['source_files']
|
|
|
|
# Prompt the user for audio filters options
|
|
def audio_filters_options(output_name) -> str:
|
|
af = AudioFilter.get_obj()
|
|
fadein, fadeout, mute, custom = (
|
|
AudioFilter.FADE_IN._value_[0],
|
|
AudioFilter.FADE_OUT._value_[0],
|
|
AudioFilter.MUTE._value_[0],
|
|
AudioFilter.CUSTOM._value_[0]
|
|
)
|
|
|
|
while not af['Exit']:
|
|
print(f'\n\033[92mOutput Name: {output_name}\033[0m')
|
|
answer = inquirer.prompt([inquirer.List('af', message='Audio Filters (Enter)', choices=list(af.keys()))])
|
|
|
|
if answer is None:
|
|
af['Exit'] = True
|
|
|
|
elif answer['af'] == fadein:
|
|
af[fadein] = CLI.prompt_time('Exponential Value').strip() or '0'
|
|
|
|
elif answer['af'] == fadeout:
|
|
af[fadeout]['Start Time'] = CLI.prompt_time('Start Time').strip() or '0'
|
|
af[fadeout]['Exp'] = CLI.prompt_time('Exponential Value').strip() or '0'
|
|
|
|
elif answer['af'] == mute:
|
|
af[mute]['Start Time'] = CLI.prompt_time('Start Time').strip() or '0'
|
|
af[mute]['End Time'] = CLI.prompt_time('End Time').strip() or '0'
|
|
|
|
elif answer['af'] == custom:
|
|
custom_audio_filter = CLI.prompt_text('Custom Audio Filter(s)').strip()
|
|
af[custom] = custom_audio_filter
|
|
|
|
else:
|
|
af['Exit'] = True
|
|
|
|
af_list = []
|
|
|
|
if float(af[fadein]) > 0:
|
|
af_list.append(AudioFilter.FADE_IN.toText(af[fadein]))
|
|
if float(af[fadeout]['Exp']) > 0:
|
|
af_list.append(AudioFilter.FADE_OUT.toText(af[fadeout]['Start Time'], af[fadeout]['Exp']))
|
|
if float(af[mute]['Start Time']) > 0 or float(af[mute]['End Time']) > 0:
|
|
af_list.append(AudioFilter.MUTE.toText(af[mute]['Start Time'], af[mute]['End Time']))
|
|
if len(af[custom]) > 0:
|
|
af_list.append(AudioFilter.CUSTOM.toText(af[custom]))
|
|
|
|
logging.debug(
|
|
f'[CLI.af_options] '
|
|
f'af[{fadein}]: \'{af[fadein]}\', '
|
|
f'af[{fadeout}]["Start Time"]: \'{af[fadeout]["Start Time"]}\', '
|
|
f'af[{fadeout}]["Exp"]: \'{af[fadeout]["Exp"]}\', '
|
|
f'af[{mute}]["Start Time"]: \'{af[mute]["Start Time"]}\', '
|
|
f'af[{mute}]["End Time"]: \'{af[mute]["End Time"]}\', '
|
|
f'af[{custom}]: \'{af[custom]}\''
|
|
)
|
|
|
|
return ','.join(af_list)
|
|
|
|
# Prompt the user for our list of video filters
|
|
def video_filters(encoding_config: EncodingConfigType) -> EncodingConfigType:
|
|
video_filters_options = VideoFilter.get_obj()
|
|
|
|
if encoding_config.include_unfiltered:
|
|
encoding_config.video_filters.append((None, 'No Filters'))
|
|
|
|
answer = inquirer.prompt([
|
|
inquirer.Checkbox('video_filters', message='Select Video Filters (Space to select)',
|
|
choices=video_filters_options.keys(), default=[tp[1] for tp in encoding_config.video_filters])
|
|
])
|
|
|
|
if answer is None:
|
|
return encoding_config
|
|
|
|
tp_list = [(name, filter_string) for filter_string, name in video_filters_options.items() if filter_string in answer['video_filters']]
|
|
custom = VideoFilter.CUSTOM
|
|
|
|
if VideoFilter.CUSTOM._value_[1] in answer['video_filters']:
|
|
tp_list.remove((custom._value_[0], custom._value_[1]))
|
|
custom_video_filters = CLI.prompt_text('Custom Video Filters (Separate with ",," if more than one)').split(',,')
|
|
tp_list.extend([(f'{custom._value_[0]}{i + 1}', value) for i, value in enumerate(custom_video_filters)])
|
|
|
|
encoding_config.video_filters = tp_list
|
|
|
|
logging.debug(f'[CLI.video_filters] tp_list: \'{tp_list}\'')
|
|
|
|
return encoding_config
|
|
|
|
# Prompt the user for custom options if requested
|
|
def custom_options(encoding_config: EncodingConfigType) -> EncodingConfigType:
|
|
create_preview = encoding_config.create_preview
|
|
limit_size_enable = encoding_config.limit_size_enable
|
|
encoding_modes = encoding_config.encoding_modes
|
|
crfs = encoding_config.crfs
|
|
cbr_bitrates = encoding_config.cbr_bitrates
|
|
cbr_max_bitrates = encoding_config.cbr_max_bitrates
|
|
|
|
answer = inquirer.prompt([
|
|
inquirer.Confirm('create_preview', message='Create Preview?', default=create_preview),
|
|
inquirer.Confirm('limit_size_enable', message='Limit Size Enable?', default=limit_size_enable),
|
|
inquirer.Text('encoding_modes', message='Encoding Modes', default=','.join(encoding_modes), validate=CLI.validate_encoding_modes),
|
|
])
|
|
|
|
if answer is None:
|
|
return encoding_config
|
|
|
|
encoding_mode_questions = []
|
|
for encoding_mode in answer['encoding_modes'].split(','):
|
|
if encoding_mode == BitrateMode.VBR.name or encoding_mode == BitrateMode.CQ.name:
|
|
encoding_mode_questions.append(inquirer.Text('crfs', message='CRFs', default=','.join(crfs), validate=CLI.validate_digits))
|
|
if encoding_mode == BitrateMode.CBR.name:
|
|
encoding_mode_questions.append(inquirer.Text('cbr_bitrates', message='Bit Rates', default=','.join(cbr_bitrates), validate=CLI.validate_digits))
|
|
encoding_mode_questions.append(inquirer.Text('cbr_max_bitrates', message='Max Bit Rates', default=','.join(cbr_max_bitrates), validate=CLI.validate_digits))
|
|
|
|
answer_em = inquirer.prompt(encoding_mode_questions)
|
|
|
|
encoding_config.create_preview = answer['create_preview']
|
|
encoding_config.limit_size_enable = answer['limit_size_enable']
|
|
encoding_config.encoding_modes = answer['encoding_modes'].split(',')
|
|
|
|
if 'crfs' in answer_em:
|
|
encoding_config.crfs = answer_em['crfs'].split(',')
|
|
if 'cbr_bitrates' in answer_em and 'cbr_max_bitrates' in answer_em:
|
|
encoding_config.cbr_bitrates = [x + 'k' for x in answer_em['cbr_bitrates'].split(',')] if len(answer_em['cbr_bitrates'].strip()) != 0 else None
|
|
encoding_config.cbr_max_bitrates = [x + 'k' for x in answer_em['cbr_max_bitrates'].split(',')] if len(answer_em['cbr_max_bitrates'].strip()) != 0 else None
|
|
|
|
logging.debug(
|
|
f'[CLI.custom_options] '
|
|
f'encoding_config.create_preview: \'{encoding_config.create_preview}\', '
|
|
f'encoding_config.limit_size_enable: \'{encoding_config.create_preview}\', '
|
|
f'encoding_config.encoding_modes: \'{encoding_config.encoding_modes}\', '
|
|
f'encoding_config.crfs: \'{encoding_config.crfs}\', '
|
|
f'encoding_config.cbr_bitrates: \'{encoding_config.cbr_bitrates}\', '
|
|
f'encoding_config.cbr_max_bitrates: \'{encoding_config.cbr_max_bitrates}\''
|
|
)
|
|
|
|
return encoding_config |