diff --git a/bin/ffmpeg.py b/bin/ffmpeg.py index 154a9d1..9fdc6c8 100644 --- a/bin/ffmpeg.py +++ b/bin/ffmpeg.py @@ -4,7 +4,7 @@ Name: FFMPEG Class Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: March 30, 2019 +Last Modified: April 21, 2019 Description: This class handles all FFMPEG related operations. @@ -29,10 +29,10 @@ class Ffmpeg: # add a forward slash to directory if not present # otherwise there will be a format error if self.ffmpeg_path[-1] != '/' and self.ffmpeg_path[-1] != '\\': - self.ffmpeg_path = '{}\\'.format(self.ffmpeg_path) + self.ffmpeg_path = f'{self.ffmpeg_path}\\' - self.ffmpeg_binary = '{}ffmpeg.exe'.format(self.ffmpeg_path) - self.ffmpeg_probe_binary = '{}ffprobe.exe'.format(self.ffmpeg_path) + self.ffmpeg_binary = f'{self.ffmpeg_path}ffmpeg.exe' + self.ffmpeg_probe_binary = f'{self.ffmpeg_path}ffprobe.exe' self.image_format = image_format def get_video_info(self, input_video): @@ -59,10 +59,10 @@ class Ffmpeg: '-show_format', '-show_streams', '-i', - '{}'.format(input_video) + input_video ] - Avalon.debug_info('Executing: {}'.format(' '.join(execute))) + Avalon.debug_info(f'Executing: {" ".join(execute)}') json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout return json.loads(json_str.decode('utf-8')) @@ -80,7 +80,7 @@ class Ffmpeg: self.ffmpeg_binary, '-i', input_video, - '{}\\extracted_%0d.{}'.format(extracted_frames, self.image_format) + f'{extracted_frames}\\extracted_%0d.{self.image_format}' ] self._execute(execute=execute, phase='video_to_frames') @@ -102,8 +102,8 @@ class Ffmpeg: '-s', resolution, '-i', - '{}\\extracted_%d.{}'.format(upscaled_frames, self.image_format), - '{}\\no_audio.mp4'.format(upscaled_frames) + f'{upscaled_frames}\\extracted_%d.{self.image_format}', + f'{upscaled_frames}\\no_audio.mp4' ] self._execute(execute=execute, phase='frames_to_video') @@ -118,7 +118,7 @@ class Ffmpeg: execute = [ self.ffmpeg_binary, '-i', - '{}\\no_audio.mp4'.format(upscaled_frames), + f'{upscaled_frames}\\no_audio.mp4', '-i', input_video, output_video @@ -143,5 +143,5 @@ class Ffmpeg: execute.append(str(value)) - Avalon.debug_info('Executing: {}'.format(execute)) + Avalon.debug_info(f'Executing: {execute}') return subprocess.run(execute, shell=True, check=True).returncode diff --git a/bin/image_cleaner.py b/bin/image_cleaner.py index 295682c..1f4106d 100644 --- a/bin/image_cleaner.py +++ b/bin/image_cleaner.py @@ -5,7 +5,7 @@ Name: Video2X Image Cleaner Author: BrianPetkovsek Author: K4YT3X Date Created: March 24, 2019 -Last Modified: March 24, 2019 +Last Modified: April 21, 2019 Description: This class is to remove the extracted frames that have already been upscaled. diff --git a/bin/upscaler.py b/bin/upscaler.py index 1a3ae64..a64ff7e 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -4,7 +4,7 @@ Name: Video2X Upscaler Author: K4YT3X Date Created: December 10, 2018 -Last Modified: March 30, 2019 +Last Modified: April 21, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -52,7 +52,7 @@ class Upscaler: self.scale_ratio = None self.model_dir = None self.threads = 5 - self.video2x_cache_folder = '{}\\video2x'.format(tempfile.gettempdir()) + self.video2x_cache_folder = f'{tempfile.gettempdir()}\\video2x' self.image_format = 'png' self.preserve_frames = False @@ -60,9 +60,9 @@ class Upscaler: """create temporary folder/directories """ self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) - Avalon.debug_info('Extracted frames are being saved to: {}'.format(self.extracted_frames)) + Avalon.debug_info(f'Extracted frames are being saved to: {self.extracted_frames}') self.upscaled_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) - Avalon.debug_info('Upscaled frames are being saved to: {}'.format(self.upscaled_frames)) + Avalon.debug_info(f'Upscaled frames are being saved to: {self.upscaled_frames}') def cleanup(self): """delete temp directories when done @@ -72,7 +72,7 @@ class Upscaler: try: # avalon framework cannot be used if python is shutting down # therefore, plain print is used - print('Cleaning up cache directory: {}'.format(directory)) + print(f'Cleaning up cache directory: {directory}') shutil.rmtree(directory) except (OSError, FileNotFoundError): pass @@ -152,8 +152,8 @@ class Upscaler: w2.upscale(self.extracted_frames, self.upscaled_frames, self.scale_ratio, self.threads, self.image_format, self.upscaler_exceptions) for image in [f for f in os.listdir(self.upscaled_frames) if os.path.isfile(os.path.join(self.upscaled_frames, f))]: - renamed = re.sub('_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{}'.format(self.image_format), '.{}'.format(self.image_format), image) - shutil.move('{}\\{}'.format(self.upscaled_frames, image), '{}\\{}'.format(self.upscaled_frames, renamed)) + renamed = re.sub(f'_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{self.image_format}', f'.{self.image_format}', image) + shutil.move(f'{self.upscaled_frames}\\{image}', f'{self.upscaled_frames}\\{renamed}') self.progress_bar_exit_signal = True progress_bar.join() @@ -176,7 +176,7 @@ class Upscaler: thread_pool = [] thread_folders = [] for thread_id in range(self.threads): - thread_folder = '{}\\{}'.format(self.extracted_frames, str(thread_id)) + thread_folder = f'{self.extracted_frames}\\{str(thread_id)}' thread_folders.append(thread_folder) # delete old folders and create new folders @@ -257,7 +257,7 @@ class Upscaler: elif self.waifu2x_driver == 'waifu2x_converter': w2 = Waifu2xConverter(self.waifu2x_settings, self.model_dir) else: - raise Exception('Unrecognized waifu2x driver: {}'.format(self.waifu2x_driver)) + raise Exception(f'Unrecognized waifu2x driver: {self.waifu2x_driver}') # extract frames from video fm.extract_frames(self.input_video, self.extracted_frames) @@ -281,7 +281,7 @@ class Upscaler: # get average frame rate of video stream framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate'])) - Avalon.info('Framerate: {}'.format(framerate)) + Avalon.info(f'Framerate: {framerate}') # width/height will be coded width/height x upscale factor if self.scale_ratio: @@ -299,7 +299,7 @@ class Upscaler: Avalon.info('Converting extracted frames into video') # use user defined output size - fm.convert_video(framerate, '{}x{}'.format(self.scale_width, self.scale_height), self.upscaled_frames) + fm.convert_video(framerate, f'{self.scale_width}x{self.scale_height}', self.upscaled_frames) Avalon.info('Conversion completed') # migrate audio tracks and subtitles diff --git a/bin/video2x.py b/bin/video2x.py index d7c1e48..cab306c 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -13,7 +13,7 @@ __ __ _ _ ___ __ __ Name: Video2X Controller Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: April 11, 2019 +Last Modified: April 21, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -80,7 +80,7 @@ def process_arguments(): upscaler_options.add_argument('-d', '--driver', help='Waifu2x driver', action='store', default='waifu2x_caffe', choices=['waifu2x_caffe', 'waifu2x_converter']) upscaler_options.add_argument('-y', '--model_dir', help='Folder containing model JSON files', action='store') upscaler_options.add_argument('-t', '--threads', help='Number of threads to use for upscaling', action='store', type=int, default=5) - upscaler_options.add_argument('-c', '--config', help='Video2X config file location', action='store', default='{}\\video2x.json'.format(os.path.dirname(os.path.abspath(sys.argv[0])))) + upscaler_options.add_argument('-c', '--config', help='Video2X config file location', action='store', default=f'{os.path.dirname(os.path.abspath(sys.argv[0]))}\\video2x.json') upscaler_options.add_argument('-b', '--batch', help='Enable batch mode (select all default values to questions)', action='store_true') # scaling options @@ -101,8 +101,8 @@ def print_logo(): print(' \\ / | | | (_| | | __/ | (_) | / /_ / . \\') print(' \\/ |_| \\__,_| \\___| \\___/ |____| /_/ \\_\\') print('\n Video2X Video Enlarger') - spaces = ((44 - len("Version {}".format(VERSION))) // 2) * " " - print('{}\n{} Version {}\n{}'.format(Avalon.FM.BD, spaces, VERSION, Avalon.FM.RST)) + spaces = ((44 - len(f'Version {VERSION}')) // 2) * ' ' + print(f'{Avalon.FM.BD}\n{spaces} Version {VERSION}\n{Avalon.FM.RST}') def check_memory(): @@ -141,17 +141,17 @@ def check_memory(): # if user doesn't even have enough memory to run even one thread if memory_available < mem_per_thread: - Avalon.warning('You might have insufficient amount of {} memory available to run this program ({} GB)'.format(memory_type, memory_available)) + Avalon.warning(f'You might have insufficient amount of {memory_type} memory available to run this program ({memory_available} GB)') Avalon.warning('Proceed with caution') if args.threads > 1: if Avalon.ask('Reduce number of threads to avoid crashing?', default=True, batch=args.batch): args.threads = 1 # if memory available is less than needed, warn the user elif memory_available < (mem_per_thread * args.threads): - Avalon.warning('Each waifu2x-caffe thread will require up to 2.5 GB of system memory') - Avalon.warning('You demanded {} threads to be created, but you only have {} GB {} memory available'.format(args.threads, round(memory_available, 4), memory_type)) - Avalon.warning('{} GB of {} memory is recommended for {} threads'.format(mem_per_thread * args.threads, memory_type, args.threads)) - Avalon.warning('With your current amount of {} memory available, {} threads is recommended'.format(memory_type, int(memory_available // mem_per_thread))) + Avalon.warning(f'Each waifu2x-caffe thread will require up to {SYS_MEM_PER_THREAD} GB of system memory') + Avalon.warning(f'You demanded {args.threads} threads to be created, but you only have {round(memory_available, 4)} GB {memory_type} memory available') + Avalon.warning(f'{mem_per_thread * args.threads} GB of {memory_type} memory is recommended for {args.threads} threads') + Avalon.warning(f'With your current amount of {memory_type} memory available, {int(memory_available // mem_per_thread)} threads is recommended') # ask the user if he / she wants to change to the recommended # number of threads @@ -187,20 +187,20 @@ def absolutify_paths(config): # check waifu2x-caffe path if not re.match('^[a-z]:', config['waifu2x_caffe']['waifu2x_caffe_path'], re.IGNORECASE): - config['waifu2x_caffe']['waifu2x_caffe_path'] = '{}\\{}'.format(current_folder, config['waifu2x_caffe']['waifu2x_caffe_path']) + config['waifu2x_caffe']['waifu2x_caffe_path'] = f'{current_folder}\\{config["waifu2x_caffe"]["waifu2x_caffe_path"]}' # check waifu2x-converter-cpp path if not re.match('^[a-z]:', config['waifu2x_converter']['waifu2x_converter_path'], re.IGNORECASE): - config['waifu2x_converter']['waifu2x_converter_path'] = '{}\\{}'.format(current_folder, config['waifu2x_converter']['waifu2x_converter_path']) + config['waifu2x_converter']['waifu2x_converter_path'] = f'{current_folder}\\{config["waifu2x_converter"]["waifu2x_converter_path"]}' # check ffmpeg path if not re.match('^[a-z]:', config['ffmpeg']['ffmpeg_path'], re.IGNORECASE): - config['ffmpeg']['ffmpeg_path'] = '{}\\{}'.format(current_folder, config['ffmpeg']['ffmpeg_path']) + config['ffmpeg']['ffmpeg_path'] = f'{current_folder}\\{config["ffmpeg"]["ffmpeg_path"]}' # check video2x cache path if config['video2x']['video2x_cache_folder']: if not re.match('^[a-z]:', config['video2x']['video2x_cache_folder'], re.IGNORECASE): - config['video2x']['video2x_cache_folder'] = '{}\\{}'.format(current_folder, config['video2x']['video2x_cache_folder']) + config['video2x']['video2x_cache_folder'] = f'{current_folder}\\{config["video2x"]["video2x_cache_folder"]}' return config @@ -210,7 +210,7 @@ def absolutify_paths(config): # this is not a library if __name__ != '__main__': Avalon.error('This file cannot be imported') - raise ImportError('{} cannot be imported'.format(__file__)) + raise ImportError(f'{__file__} cannot be imported') print_logo() @@ -262,16 +262,16 @@ preserve_frames = config['video2x']['preserve_frames'] # create temp directories if they don't exist if not video2x_cache_folder: - video2x_cache_folder = '{}\\video2x'.format(tempfile.gettempdir()) + video2x_cache_folder = f'{tempfile.gettempdir()}\\video2x' if video2x_cache_folder and not os.path.isdir(video2x_cache_folder): if not os.path.isfile(video2x_cache_folder) and not os.path.islink(video2x_cache_folder): - Avalon.warning('Specified cache folder/directory {} does not exist'.format(video2x_cache_folder)) + Avalon.warning(f'Specified cache folder/directory {video2x_cache_folder} does not exist') if Avalon.ask('Create folder/directory?', default=True, batch=args.batch): if os.mkdir(video2x_cache_folder) is None: - Avalon.info('{} created'.format(video2x_cache_folder)) + Avalon.info(f'{video2x_cache_folder} created') else: - Avalon.error('Unable to create {}'.format(video2x_cache_folder)) + Avalon.error(f'Unable to create {video2x_cache_folder}') Avalon.error('Aborting...') exit(1) else: @@ -288,7 +288,7 @@ try: # if input specified is a single file if os.path.isfile(args.input): """ Upscale single video file """ - Avalon.info('Upscaling single video file: {}'.format(args.input)) + Avalon.info(f'Upscaling single video file: {args.input}') # check for input output format mismatch if os.path.isdir(args.output): @@ -321,9 +321,9 @@ try: # if input specified is a folder elif os.path.isdir(args.input): """ Upscale videos in a folder/directory """ - Avalon.info('Upscaling videos in folder/directory: {}'.format(args.input)) + Avalon.info(f'Upscaling videos in folder/directory: {args.input}') for input_video in [f for f in os.listdir(args.input) if os.path.isfile(os.path.join(args.input, f))]: - output_video = '{}\\{}'.format(args.output, input_video) + output_video = f'{args.output}\\{input_video}' upscaler = Upscaler(input_video=os.path.join(args.input, input_video), output_video=output_video, method=args.method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings) # set optional options @@ -343,13 +343,12 @@ try: upscaler.cleanup() else: Avalon.error('Input path is neither a file nor a folder/directory') - raise FileNotFoundError('{} is neither file nor folder/directory'.format(args.input)) + raise FileNotFoundError(f'{args.input} is neither file nor folder/directory') - Avalon.info('Program completed, taking {} seconds'.format(round((time.time() - begin_time), 5))) + Avalon.info(f'Program completed, taking {round((time.time() - begin_time), 5)} seconds') except Exception: Avalon.error('An exception has occurred') traceback.print_exc() - Avalon.warning('If you experience error \"cudaSuccess out of memory\", try reducing number of threads you\'re using') finally: # remove Video2X Cache folder try: diff --git a/bin/video2x_setup.py b/bin/video2x_setup.py index 6adb959..608757e 100644 --- a/bin/video2x_setup.py +++ b/bin/video2x_setup.py @@ -4,7 +4,7 @@ Name: Video2X Setup Script Author: K4YT3X Date Created: November 28, 2018 -Last Modified: March 31, 2019 +Last Modified: April 21, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -96,7 +96,7 @@ class Video2xSetup: """ for file in self.trash: try: - print('Deleting: {}'.format(file)) + print(f'Deleting: {file}') os.remove(file) except FileNotFoundError: pass @@ -110,7 +110,7 @@ class Video2xSetup: self.trash.append(ffmpeg_zip) with zipfile.ZipFile(ffmpeg_zip) as zipf: - zipf.extractall('{}\\video2x'.format(os.getenv('localappdata'))) + zipf.extractall(f'{os.getenv("localappdata")}\\video2x') def _install_waifu2x_caffe(self): """ Install waifu2x_caffe @@ -127,7 +127,7 @@ class Video2xSetup: self.trash.append(waifu2x_caffe_zip) with zipfile.ZipFile(waifu2x_caffe_zip) as zipf: - zipf.extractall('{}\\video2x'.format(os.getenv('localappdata'))) + zipf.extractall(f'{os.getenv("localappdata")}\\video2x') def _install_waifu2x_converter_cpp(self): """ Install waifu2x_caffe @@ -145,7 +145,7 @@ class Video2xSetup: self.trash.append(waifu2x_converter_cpp_zip) with zipfile.ZipFile(waifu2x_converter_cpp_zip) as zipf: - zipf.extractall('{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata'))) + zipf.extractall(f'{os.getenv("localappdata")}\\video2x\\waifu2x-converter-cpp') def _generate_config(self): """ Generate video2x config @@ -155,16 +155,18 @@ class Video2xSetup: template_dict = json.load(template) template.close() + local_app_data = os.getenv('localappdata') + # configure only the specified drivers if self.driver == 'all': - template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = '{}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe'.format(os.getenv('localappdata')) - template_dict['waifu2x_converter']['waifu2x_converter_path'] = '{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata')) + template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = f'{local_app_data}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe' + template_dict['waifu2x_converter']['waifu2x_converter_path'] = f'{local_app_data}\\video2x\\waifu2x-converter-cpp' elif self.driver == 'waifu2x_caffe': - template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = '{}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe'.format(os.getenv('localappdata')) + template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = f'{local_app_data}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe' elif self.driver == 'waifu2x_converter': - template_dict['waifu2x_converter']['waifu2x_converter_path'] = '{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata')) + template_dict['waifu2x_converter']['waifu2x_converter_path'] = f'{local_app_data}\\video2x\\waifu2x-converter-cpp' - template_dict['ffmpeg']['ffmpeg_path'] = '{}\\video2x\\ffmpeg-latest-win64-static\\bin'.format(os.getenv('localappdata')) + template_dict['ffmpeg']['ffmpeg_path'] = f'{local_app_data}\\video2x\\ffmpeg-latest-win64-static\\bin' template_dict['ffmpeg']['ffmpeg_hwaccel'] = 'auto' template_dict['ffmpeg']['extra_arguments'] = [] template_dict['video2x']['video2x_cache_folder'] = None @@ -182,10 +184,10 @@ def download(url, save_path, chunk_size=4096): from tqdm import tqdm import requests - output_file = '{}\\{}'.format(save_path, url.split('/')[-1]) - print('Downloading: {}'.format(url)) - print('Chunk size: {}'.format(chunk_size)) - print('Saving to: {}'.format(output_file)) + output_file = f'{save_path}\\{url.split("/")[-1]}' + print(f'Downloading: {url}') + print(f'Chunk size: {chunk_size}') + print(f'Saving to: {output_file}') stream = requests.get(url, stream=True) total_size = int(stream.headers['content-length']) @@ -214,7 +216,7 @@ if __name__ == "__main__": try: args = process_arguments() print('Video2X Setup Script') - print('Version: {}'.format(VERSION)) + print(f'Version: {VERSION}') # do not install pip modules if script # is packaged in exe format diff --git a/bin/waifu2x_caffe.py b/bin/waifu2x_caffe.py index f716422..c274f89 100644 --- a/bin/waifu2x_caffe.py +++ b/bin/waifu2x_caffe.py @@ -4,7 +4,7 @@ Name: Waifu2x Caffe Driver Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: March 30, 2019 +Last Modified: April 21, 2019 Description: This class is a high-level wrapper for waifu2x-caffe. @@ -58,7 +58,7 @@ class Waifu2xCaffe: # print thread start message self.print_lock.acquire() - Avalon.debug_info('[upscaler] Thread {} started'.format(threading.current_thread().name)) + Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} started') self.print_lock.release() # list to be executed @@ -74,17 +74,17 @@ class Waifu2xCaffe: continue else: if len(key) == 1: - execute.append('-{}'.format(key)) + execute.append(f'-{key}') else: - execute.append('--{}'.format(key)) + execute.append(f'--{key}') execute.append(str(value)) - Avalon.debug_info('Executing: {}'.format(execute)) + Avalon.debug_info(f'Executing: {execute}') completed_command = subprocess.run(execute, check=True) # print thread exiting message self.print_lock.acquire() - Avalon.debug_info('[upscaler] Thread {} exiting'.format(threading.current_thread().name)) + Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} exiting') self.print_lock.release() # return command execution return code diff --git a/bin/waifu2x_converter.py b/bin/waifu2x_converter.py index 97f8245..7136439 100644 --- a/bin/waifu2x_converter.py +++ b/bin/waifu2x_converter.py @@ -4,7 +4,7 @@ Name: Waifu2x Converter CPP Driver Author: K4YT3X Date Created: February 8, 2019 -Last Modified: March 30, 2019 +Last Modified: April 21, 2019 Description: This class is a high-level wrapper for waifu2x-converter-cpp. @@ -59,11 +59,11 @@ class Waifu2xConverter: # models_rgb must be specified manually for waifu2x-converter-cpp # if it's not specified in the arguments, create automatically if self.waifu2x_settings['model-dir'] is None: - self.waifu2x_settings['model-dir'] = '{}\\models_rgb'.format(self.waifu2x_settings['waifu2x_converter_path']) + self.waifu2x_settings['model-dir'] = f'{self.waifu2x_settings["waifu2x_converter_path"]}\\models_rgb' # print thread start message self.print_lock.acquire() - Avalon.debug_info('[upscaler] Thread {} started'.format(threading.current_thread().name)) + Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} started') self.print_lock.release() # list to be executed @@ -75,16 +75,16 @@ class Waifu2xConverter: # the key doesn't need to be passed in this case if key == 'waifu2x_converter_path': - execute.append('{}\\waifu2x-converter-cpp.exe'.format(str(value))) + execute.append(f'{str(value)}\\waifu2x-converter-cpp.exe') # null or None means that leave this option out (keep default) elif value is None or value is False: continue else: if len(key) == 1: - execute.append('-{}'.format(key)) + execute.append(f'-{key}') else: - execute.append('--{}'.format(key)) + execute.append(f'--{key}') # true means key is an option if value is True: @@ -92,7 +92,7 @@ class Waifu2xConverter: execute.append(str(value)) - Avalon.debug_info('Executing: {}'.format(execute)) + Avalon.debug_info(f'Executing: {execute}') return subprocess.run(execute, check=True).returncode except Exception as e: