changed file handling method from os to pathlib

This commit is contained in:
k4yt3x 2019-07-27 17:29:33 -04:00
parent 89dfd21f97
commit a27bdb4b63
6 changed files with 190 additions and 127 deletions

View File

@ -11,8 +11,9 @@ Description: This class handles all FFmpeg related operations.
# built-in imports # built-in imports
import json import json
import subprocess
import os import os
import pathlib
import subprocess
# third-party imports # third-party imports
from avalon_framework import Avalon from avalon_framework import Avalon
@ -29,12 +30,12 @@ class Ffmpeg:
def __init__(self, ffmpeg_settings, image_format): def __init__(self, ffmpeg_settings, image_format):
self.ffmpeg_settings = ffmpeg_settings self.ffmpeg_settings = ffmpeg_settings
self.ffmpeg_path = self.ffmpeg_settings['ffmpeg_path'] self.ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
self.ffmpeg_binary = os.path.join(self.ffmpeg_path, 'ffmpeg.exe') self.ffmpeg_binary = self.ffmpeg_path / 'ffmpeg.exe'
self.ffmpeg_probe_binary = os.path.join(self.ffmpeg_path, 'ffprobe.exe') self.ffmpeg_probe_binary = self.ffmpeg_path / 'ffprobe.exe'
self.image_format = image_format self.image_format = image_format
self.pixel_format = None self.pixel_format = None
def get_pixel_formats(self): def get_pixel_formats(self):
""" Get a dictionary of supported pixel formats """ Get a dictionary of supported pixel formats
@ -51,6 +52,9 @@ class Ffmpeg:
'-pix_fmts' '-pix_fmts'
] ]
# turn elements into str
execute = [str(e) for e in execute]
Avalon.debug_info(f'Executing: {" ".join(execute)}') Avalon.debug_info(f'Executing: {" ".join(execute)}')
# initialize dictionary to store pixel formats # initialize dictionary to store pixel formats
@ -95,6 +99,9 @@ class Ffmpeg:
input_video input_video
] ]
# turn elements into str
execute = [str(e) for e in execute]
Avalon.debug_info(f'Executing: {" ".join(execute)}') Avalon.debug_info(f'Executing: {" ".join(execute)}')
json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout
return json.loads(json_str.decode('utf-8')) return json.loads(json_str.decode('utf-8'))
@ -120,7 +127,7 @@ class Ffmpeg:
execute.extend(self._read_configuration(phase='video_to_frames', section='output_options')) execute.extend(self._read_configuration(phase='video_to_frames', section='output_options'))
execute.extend([ execute.extend([
os.path.join(extracted_frames, f'extracted_%0d.{self.image_format}') extracted_frames / f'extracted_%0d.{self.image_format}'
]) ])
execute.extend(self._read_configuration(phase='video_to_frames')) execute.extend(self._read_configuration(phase='video_to_frames'))
@ -149,17 +156,19 @@ class Ffmpeg:
execute.extend(self._read_configuration(phase='frames_to_video', section='input_options')) execute.extend(self._read_configuration(phase='frames_to_video', section='input_options'))
# WORKAROUND FOR WAIFU2X-NCNN-VULKAN # WORKAROUND FOR WAIFU2X-NCNN-VULKAN
# Dev: SAT3LL
# rename all .png.png suffixes to .png
import re import re
import shutil import shutil
regex = re.compile(r'\.png\.png$') regex = re.compile(r'\.png\.png$')
for raw_frame in os.listdir(upscaled_frames): for frame_name in upscaled_frames.iterdir():
shutil.move(os.path.join(upscaled_frames, raw_frame), os.path.join(upscaled_frames, regex.sub('.png', raw_frame))) (upscaled_frames / frame_name).rename(upscaled_frames / regex.sub('.png', str(frame_name)))
# END WORKAROUND # END WORKAROUND
# append input frames path into command # append input frames path into command
execute.extend([ execute.extend([
'-i', '-i',
os.path.join(upscaled_frames, f'extracted_%d.{self.image_format}') upscaled_frames / f'extracted_%d.{self.image_format}'
]) ])
# read FFmpeg output options # read FFmpeg output options
@ -170,7 +179,7 @@ class Ffmpeg:
# specify output file location # specify output file location
execute.extend([ execute.extend([
os.path.join(upscaled_frames, 'no_audio.mp4') upscaled_frames / 'no_audio.mp4'
]) ])
self._execute(execute) self._execute(execute)
@ -186,7 +195,7 @@ class Ffmpeg:
execute = [ execute = [
self.ffmpeg_binary, self.ffmpeg_binary,
'-i', '-i',
os.path.join(upscaled_frames, 'no_audio.mp4'), upscaled_frames / 'no_audio.mp4',
'-i', '-i',
input_video input_video
] ]
@ -248,7 +257,7 @@ class Ffmpeg:
if value is not True: if value is not True:
configuration.append(str(subvalue)) configuration.append(str(subvalue))
# otherwise the value is typical # otherwise the value is typical
else: else:
configuration.append(key) configuration.append(key)
@ -270,4 +279,8 @@ class Ffmpeg:
int -- execution return code int -- execution return code
""" """
Avalon.debug_info(f'Executing: {execute}') Avalon.debug_info(f'Executing: {execute}')
# turn all list elements into string to avoid errors
execute = [str(e) for e in execute]
return subprocess.run(execute, shell=True, check=True).returncode return subprocess.run(execute, shell=True, check=True).returncode

View File

@ -59,19 +59,19 @@ class ImageCleaner(threading.Thread):
""" """
# list all images in the extracted frames # list all images in the extracted frames
output_frames = [f for f in os.listdir(self.output_directory) if os.path.isfile(os.path.join(self.output_directory, f))] output_frames = [f for f in self.output_directory.iterdir() if f.is_file()]
# compare and remove frames downscaled images that finished being upscaled # compare and remove frames downscaled images that finished being upscaled
# within each thread's extracted frames directory # within each thread's extracted frames directory
for i in range(self.threads): for thread_id in range(self.threads):
dir_path = os.path.join(self.input_directory, str(i)) dir_path = self.input_directory / str(thread_id)
# for each file within all the directories # for each file within all the directories
for f in os.listdir(dir_path): for file in dir_path.iterdir():
file_path = os.path.join(dir_path, f) file_path = dir_path / file
# if file also exists in the output directory, then the file # if file also exists in the output directory, then the file
# has already been processed, thus not needed anymore # has already been processed, thus not needed anymore
if os.path.isfile(file_path) and f in output_frames: if file_path.is_file() and file in output_frames:
os.remove(file_path) file_path.unlink(file)
output_frames.remove(f) output_frames.remove(file)

View File

@ -25,12 +25,13 @@ from waifu2x_ncnn_vulkan import Waifu2xNcnnVulkan
# built-in imports # built-in imports
from fractions import Fraction from fractions import Fraction
import copy import copy
import os import pathlib
import re import re
import shutil import shutil
import tempfile import tempfile
import threading import threading
import time import time
import traceback
# third-party imports # third-party imports
from avalon_framework import Avalon from avalon_framework import Avalon
@ -61,16 +62,16 @@ class Upscaler:
self.scale_ratio = None self.scale_ratio = None
self.model_dir = None self.model_dir = None
self.threads = 5 self.threads = 5
self.video2x_cache_directory = os.path.join(tempfile.gettempdir(), 'video2x') self.video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
self.image_format = 'png' self.image_format = 'png'
self.preserve_frames = False self.preserve_frames = False
def create_temp_directories(self): def create_temp_directories(self):
"""create temporary directory """create temporary directory
""" """
self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_directory) self.extracted_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory))
Avalon.debug_info(f'Extracted frames are being saved to: {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_directory) self.upscaled_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory))
Avalon.debug_info(f'Upscaled frames are being saved to: {self.upscaled_frames}') Avalon.debug_info(f'Upscaled frames are being saved to: {self.upscaled_frames}')
def cleanup_temp_directories(self): def cleanup_temp_directories(self):
@ -84,7 +85,8 @@ class Upscaler:
print(f'Cleaning up cache directory: {directory}') print(f'Cleaning up cache directory: {directory}')
shutil.rmtree(directory) shutil.rmtree(directory)
except (OSError, FileNotFoundError): except (OSError, FileNotFoundError):
pass print(f'Unable to delete: {directory}')
traceback.print_exc()
def _check_arguments(self): def _check_arguments(self):
# check if arguments are valid / all necessary argument # check if arguments are valid / all necessary argument
@ -109,7 +111,7 @@ class Upscaler:
# get number of extracted frames # get number of extracted frames
total_frames = 0 total_frames = 0
for directory in extracted_frames_directories: for directory in extracted_frames_directories:
total_frames += len([f for f in os.listdir(directory) if f[-4:] == f'.{self.image_format}']) total_frames += len([f for f in directory.iterdir() if str(f)[-4:] == f'.{self.image_format}'])
with tqdm(total=total_frames, ascii=True, desc='Upscaling Progress') as progress_bar: with tqdm(total=total_frames, ascii=True, desc='Upscaling Progress') as progress_bar:
@ -120,7 +122,7 @@ class Upscaler:
while not self.progress_bar_exit_signal: while not self.progress_bar_exit_signal:
try: try:
total_frames_upscaled = len([f for f in os.listdir(self.upscaled_frames) if f[-4:] == f'.{self.image_format}']) total_frames_upscaled = len([f for f in self.upscaled_frames.iterdir() if str(f)[-4:] == f'.{self.image_format}'])
delta = total_frames_upscaled - previous_cycle_frames delta = total_frames_upscaled - previous_cycle_frames
previous_cycle_frames = total_frames_upscaled previous_cycle_frames = total_frames_upscaled
@ -166,9 +168,9 @@ class Upscaler:
progress_bar.start() progress_bar.start()
w2.upscale(self.extracted_frames, self.upscaled_frames, self.scale_ratio, self.threads, self.image_format, self.upscaler_exceptions) 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))]: for image in [f for f in self.upscaled_frames.iterdir() if f.is_file()]:
renamed = re.sub(f'_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{self.image_format}', f'.{self.image_format}', image) renamed = re.sub(f'_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{self.image_format}', f'.{self.image_format}', image)
shutil.move(os.path.join(self.upscaled_frames, image), os.path.join(self.upscaled_frames, renamed)) (self.upscaled_frames / image).rename(self.upscaled_frames / renamed)
self.progress_bar_exit_signal = True self.progress_bar_exit_signal = True
progress_bar.join() progress_bar.join()
@ -178,7 +180,7 @@ class Upscaler:
upscaler_threads = [] upscaler_threads = []
# list all images in the extracted frames # list all images in the extracted frames
frames = [os.path.join(self.extracted_frames, f) for f in os.listdir(self.extracted_frames) if os.path.isfile(os.path.join(self.extracted_frames, f))] frames = [(self.extracted_frames / f) for f in self.extracted_frames.iterdir() if f.is_file]
# if we have less images than threads, # if we have less images than threads,
# create only the threads necessary # create only the threads necessary
@ -191,13 +193,13 @@ class Upscaler:
thread_pool = [] thread_pool = []
thread_directories = [] thread_directories = []
for thread_id in range(self.threads): for thread_id in range(self.threads):
thread_directory = os.path.join(self.extracted_frames, str(thread_id)) thread_directory = self.extracted_frames / str(thread_id)
thread_directories.append(thread_directory) thread_directories.append(thread_directory)
# delete old directories and create new directories # delete old directories and create new directories
if os.path.isdir(thread_directory): if thread_directory.is_dir():
shutil.rmtree(thread_directory) shutil.rmtree(thread_directory)
os.mkdir(thread_directory) thread_directory.mkdir(parents=True, exist_ok=True)
# append directory path into list # append directory path into list
thread_pool.append((thread_directory, thread_id)) thread_pool.append((thread_directory, thread_id))
@ -206,7 +208,7 @@ class Upscaler:
# until there is none left in the directory # until there is none left in the directory
for image in frames: for image in frames:
# move image # move image
shutil.move(image, thread_pool[0][0]) image.rename(thread_pool[0][0] / image.name)
# rotate list # rotate list
thread_pool = thread_pool[-1:] + thread_pool[:-1] thread_pool = thread_pool[-1:] + thread_pool[:-1]
@ -219,23 +221,23 @@ class Upscaler:
if self.scale_ratio: if self.scale_ratio:
thread = threading.Thread(target=w2.upscale, thread = threading.Thread(target=w2.upscale,
args=(thread_info[0], args=(thread_info[0],
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio, self.scale_ratio,
False, False,
False, False,
self.image_format, self.image_format,
self.upscaler_exceptions)) self.upscaler_exceptions))
else: else:
thread = threading.Thread(target=w2.upscale, thread = threading.Thread(target=w2.upscale,
args=(thread_info[0], args=(thread_info[0],
self.upscaled_frames, self.upscaled_frames,
False, False,
self.scale_width, self.scale_width,
self.scale_height, self.scale_height,
self.image_format, self.image_format,
self.upscaler_exceptions)) self.upscaler_exceptions))
# if the driver being used is waifu2x_ncnn_vulkan # if the driver being used is waifu2x_ncnn_vulkan
elif self.waifu2x_driver == 'waifu2x_ncnn_vulkan': elif self.waifu2x_driver == 'waifu2x_ncnn_vulkan':
w2 = Waifu2xNcnnVulkan(copy.deepcopy(self.waifu2x_settings)) w2 = Waifu2xNcnnVulkan(copy.deepcopy(self.waifu2x_settings))
thread = threading.Thread(target=w2.upscale, thread = threading.Thread(target=w2.upscale,
@ -276,7 +278,6 @@ class Upscaler:
if len(self.upscaler_exceptions) != 0: if len(self.upscaler_exceptions) != 0:
raise(self.upscaler_exceptions[0]) raise(self.upscaler_exceptions[0])
def run(self): def run(self):
"""Main controller for Video2X """Main controller for Video2X
@ -289,8 +290,8 @@ class Upscaler:
self._check_arguments() self._check_arguments()
# convert paths to absolute paths # convert paths to absolute paths
self.input_video = os.path.abspath(self.input_video) self.input_video = self.input_video.absolute()
self.output_video = os.path.abspath(self.output_video) self.output_video = self.output_video.absolute()
# initialize objects for ffmpeg and waifu2x-caffe # initialize objects for ffmpeg and waifu2x-caffe
fm = Ffmpeg(self.ffmpeg_settings, self.image_format) fm = Ffmpeg(self.ffmpeg_settings, self.image_format)

View File

@ -47,8 +47,10 @@ from upscaler import Upscaler
# built-in imports # built-in imports
import argparse import argparse
import glob
import json import json
import os import os
import pathlib
import re import re
import shutil import shutil
import sys import sys
@ -61,7 +63,7 @@ from avalon_framework import Avalon
import GPUtil import GPUtil
import psutil import psutil
VERSION = '2.8.1' VERSION = '2.9.0'
# each thread might take up to 2.5 GB during initialization. # each thread might take up to 2.5 GB during initialization.
# (system memory, not to be confused with GPU memory) # (system memory, not to be confused with GPU memory)
@ -80,16 +82,16 @@ def process_arguments():
# video options # video options
file_options = parser.add_argument_group('File Options') file_options = parser.add_argument_group('File Options')
file_options.add_argument('-i', '--input', help='source video file/directory', action='store') file_options.add_argument('-i', '--input', type=pathlib.Path, help='source video file/directory', action='store')
file_options.add_argument('-o', '--output', help='output video file/directory', action='store') file_options.add_argument('-o', '--output', type=pathlib.Path, help='output video file/directory', action='store')
# upscaler options # upscaler options
upscaler_options = parser.add_argument_group('Upscaler Options') upscaler_options = parser.add_argument_group('Upscaler Options')
upscaler_options.add_argument('-m', '--method', help='upscaling method', action='store', default='gpu', choices=['cpu', 'gpu', 'cudnn']) upscaler_options.add_argument('-m', '--method', help='upscaling method', action='store', default='gpu', choices=['cpu', 'gpu', 'cudnn'])
upscaler_options.add_argument('-d', '--driver', help='waifu2x driver', action='store', default='waifu2x_caffe', choices=['waifu2x_caffe', 'waifu2x_converter', 'waifu2x_ncnn_vulkan']) upscaler_options.add_argument('-d', '--driver', help='waifu2x driver', action='store', default='waifu2x_caffe', choices=['waifu2x_caffe', 'waifu2x_converter', 'waifu2x_ncnn_vulkan'])
upscaler_options.add_argument('-y', '--model_dir', help='directory containing model JSON files', action='store') upscaler_options.add_argument('-y', '--model_dir', type=pathlib.Path, help='directory 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=1) upscaler_options.add_argument('-t', '--threads', help='number of threads to use for upscaling', action='store', type=int, default=1)
upscaler_options.add_argument('-c', '--config', help='video2x config file location', action='store', default=os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'video2x.json')) upscaler_options.add_argument('-c', '--config', type=pathlib.Path, help='video2x config file location', action='store', default=pathlib.Path(sys.argv[0]).parent.absolute() / 'video2x.json')
upscaler_options.add_argument('-b', '--batch', help='enable batch mode (select all default values to questions)', action='store_true') upscaler_options.add_argument('-b', '--batch', help='enable batch mode (select all default values to questions)', action='store_true')
# scaling options # scaling options
@ -107,12 +109,16 @@ def process_arguments():
def print_logo(): def print_logo():
print('__ __ _ _ ___ __ __') """print video2x logo"""
print('\\ \\ / / (_) | | |__ \\ \\ \\ / /') logo = r'''
print(' \\ \\ / / _ __| | ___ ___ ) | \\ V /') __ __ _ _ ___ __ __
print(' \\ \\/ / | | / _` | / _ \\ / _ \\ / / > <') \ \ / / (_) | | |__ \ \ \ / /
print(' \\ / | | | (_| | | __/ | (_) | / /_ / . \\') \ \ / / _ __| | ___ ___ ) | \ V /
print(' \\/ |_| \\__,_| \\___| \\___/ |____| /_/ \\_\\') \ \/ / | | / _` | / _ \ / _ \ / / > <
\ / | | | (_| | | __/ | (_) | / /_ / . \
\/ |_| \__,_| \___| \___/ |____| /_/ \_\
'''
print(logo)
print('\n Video2X Video Enlarger') print('\n Video2X Video Enlarger')
spaces = ((44 - len(f'Version {VERSION}')) // 2) * ' ' spaces = ((44 - len(f'Version {VERSION}')) // 2) * ' '
print(f'{Avalon.FM.BD}\n{spaces} Version {VERSION}\n{Avalon.FM.RST}') print(f'{Avalon.FM.BD}\n{spaces} Version {VERSION}\n{Avalon.FM.RST}')
@ -133,7 +139,7 @@ def check_memory():
# GPUtil requires nvidia-smi.exe to interact with GPU # GPUtil requires nvidia-smi.exe to interact with GPU
if args.method == 'gpu' or args.method == 'cudnn': if args.method == 'gpu' or args.method == 'cudnn':
if not (shutil.which('nvidia-smi') or if not (shutil.which('nvidia-smi') or
os.path.isfile('C:\\Program Files\\NVIDIA Corporation\\NVSMI\\nvidia-smi.exe')): pathlib.Path(r'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe').is_file()):
# Nvidia System Management Interface not available # Nvidia System Management Interface not available
Avalon.warning('Nvidia-smi not available, skipping available memory check') Avalon.warning('Nvidia-smi not available, skipping available memory check')
Avalon.warning('If you experience error \"cudaSuccess out of memory\", try reducing number of threads you\'re using') Avalon.warning('If you experience error \"cudaSuccess out of memory\", try reducing number of threads you\'re using')
@ -197,28 +203,28 @@ def absolutify_paths(config):
Returns: Returns:
dict -- configuration file dictionary dict -- configuration file dictionary
""" """
current_directory = os.path.dirname(os.path.abspath(sys.argv[0])) current_directory = pathlib.Path(sys.argv[0]).parent.absolute()
# check waifu2x-caffe path # check waifu2x-caffe path
if not re.match('^[a-z]:', config['waifu2x_caffe']['waifu2x_caffe_path'], re.IGNORECASE): if not re.match('^[a-z]:', config['waifu2x_caffe']['waifu2x_caffe_path'], re.IGNORECASE):
config['waifu2x_caffe']['waifu2x_caffe_path'] = os.path.join(current_directory, config['waifu2x_caffe']['waifu2x_caffe_path']) config['waifu2x_caffe']['waifu2x_caffe_path'] = current_directory / config['waifu2x_caffe']['waifu2x_caffe_path']
# check waifu2x-converter-cpp path # check waifu2x-converter-cpp path
if not re.match('^[a-z]:', config['waifu2x_converter']['waifu2x_converter_path'], re.IGNORECASE): if not re.match('^[a-z]:', config['waifu2x_converter']['waifu2x_converter_path'], re.IGNORECASE):
config['waifu2x_converter']['waifu2x_converter_path'] = os.path.join(current_directory, config['waifu2x_converter']['waifu2x_converter_path']) config['waifu2x_converter']['waifu2x_converter_path'] = current_directory / config['waifu2x_converter']['waifu2x_converter_path']
# check waifu2x_ncnn_vulkan path # check waifu2x_ncnn_vulkan path
if not re.match('^[a-z]:', config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'], re.IGNORECASE): if not re.match('^[a-z]:', config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'], re.IGNORECASE):
config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = os.path.join(current_directory, config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path']) config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = current_directory / config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path']
# check ffmpeg path # check ffmpeg path
if not re.match('^[a-z]:', config['ffmpeg']['ffmpeg_path'], re.IGNORECASE): if not re.match('^[a-z]:', config['ffmpeg']['ffmpeg_path'], re.IGNORECASE):
config['ffmpeg']['ffmpeg_path'] = os.path.join(current_directory, config['ffmpeg']['ffmpeg_path']) config['ffmpeg']['ffmpeg_path'] = current_directory / config['ffmpeg']['ffmpeg_path']
# check video2x cache path # check video2x cache path
if config['video2x']['video2x_cache_directory']: if config['video2x']['video2x_cache_directory']:
if not re.match('^[a-z]:', config['video2x']['video2x_cache_directory'], re.IGNORECASE): if not re.match('^[a-z]:', config['video2x']['video2x_cache_directory'], re.IGNORECASE):
config['video2x']['video2x_cache_directory'] = os.path.join(current_directory, config['video2x']['video2x_cache_directory']) config['video2x']['video2x_cache_directory'] = current_directory / config['video2x']['video2x_cache_directory']
return config return config
@ -275,19 +281,19 @@ config = absolutify_paths(config)
# load waifu2x configuration # load waifu2x configuration
if args.driver == 'waifu2x_caffe': if args.driver == 'waifu2x_caffe':
waifu2x_settings = config['waifu2x_caffe'] waifu2x_settings = config['waifu2x_caffe']
if not os.path.isfile(waifu2x_settings['waifu2x_caffe_path']): if not pathlib.Path(waifu2x_settings['waifu2x_caffe_path']).is_file():
Avalon.error('Specified waifu2x-caffe directory doesn\'t exist') Avalon.error('Specified waifu2x-caffe directory doesn\'t exist')
Avalon.error('Please check the configuration file settings') Avalon.error('Please check the configuration file settings')
raise FileNotFoundError(waifu2x_settings['waifu2x_caffe_path']) raise FileNotFoundError(waifu2x_settings['waifu2x_caffe_path'])
elif args.driver == 'waifu2x_converter': elif args.driver == 'waifu2x_converter':
waifu2x_settings = config['waifu2x_converter'] waifu2x_settings = config['waifu2x_converter']
if not os.path.isdir(waifu2x_settings['waifu2x_converter_path']): if not pathlib.Path(waifu2x_settings['waifu2x_converter_path']).is_dir():
Avalon.error('Specified waifu2x-converter-cpp directory doesn\'t exist') Avalon.error('Specified waifu2x-converter-cpp directory doesn\'t exist')
Avalon.error('Please check the configuration file settings') Avalon.error('Please check the configuration file settings')
raise FileNotFoundError(waifu2x_settings['waifu2x_converter_path']) raise FileNotFoundError(waifu2x_settings['waifu2x_converter_path'])
elif args.driver == 'waifu2x_ncnn_vulkan': elif args.driver == 'waifu2x_ncnn_vulkan':
waifu2x_settings = config['waifu2x_ncnn_vulkan'] waifu2x_settings = config['waifu2x_ncnn_vulkan']
if not os.path.isfile(waifu2x_settings['waifu2x_ncnn_vulkan_path']): if not pathlib.Path(waifu2x_settings['waifu2x_ncnn_vulkan_path']).is_file():
Avalon.error('Specified waifu2x_ncnn_vulkan directory doesn\'t exist') Avalon.error('Specified waifu2x_ncnn_vulkan directory doesn\'t exist')
Avalon.error('Please check the configuration file settings') Avalon.error('Please check the configuration file settings')
raise FileNotFoundError(waifu2x_settings['waifu2x_ncnn_vulkan_path']) raise FileNotFoundError(waifu2x_settings['waifu2x_ncnn_vulkan_path'])
@ -296,28 +302,38 @@ elif args.driver == 'waifu2x_ncnn_vulkan':
ffmpeg_settings = config['ffmpeg'] ffmpeg_settings = config['ffmpeg']
# load video2x settings # load video2x settings
video2x_cache_directory = config['video2x']['video2x_cache_directory']
image_format = config['video2x']['image_format'].lower() image_format = config['video2x']['image_format'].lower()
preserve_frames = config['video2x']['preserve_frames'] preserve_frames = config['video2x']['preserve_frames']
# create temp directories if they don't exist # load cache directory
if not video2x_cache_directory: if isinstance(config['video2x']['video2x_cache_directory'], str):
video2x_cache_directory = os.path.join(tempfile.gettempdir(), 'video2x') video2x_cache_directory = pathlib.Path(config['video2x']['video2x_cache_directory'])
else:
video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
if video2x_cache_directory and not os.path.isdir(video2x_cache_directory): if video2x_cache_directory.exists() and not video2x_cache_directory.is_dir():
if not os.path.isfile(video2x_cache_directory) and not os.path.islink(video2x_cache_directory): Avalon.error('Specified cache directory is a file/link')
Avalon.warning(f'Specified cache directory {video2x_cache_directory} does not exist') raise FileExistsError('Specified cache directory is a file/link')
if Avalon.ask('Create directory?', default=True, batch=args.batch):
if os.mkdir(video2x_cache_directory) is None: elif not video2x_cache_directory.exists():
Avalon.info(f'{video2x_cache_directory} created') # if destination file is a file or a symbolic link
else: Avalon.warning(f'Specified cache directory {video2x_cache_directory} does not exist')
Avalon.error(f'Unable to create {video2x_cache_directory}')
Avalon.error('Aborting...') # try creating the cache directory
exit(1) if Avalon.ask('Create directory?', default=True, batch=args.batch):
try:
video2x_cache_directory.mkdir(parents=True, exist_ok=True)
Avalon.info(f'{video2x_cache_directory} created')
# there can be a number of exceptions here
# PermissionError, FileExistsError, etc.
# therefore, we put a catch-them-all here
except Exception as e:
Avalon.error(f'Unable to create {video2x_cache_directory}')
Avalon.error('Aborting...')
raise e
else: else:
Avalon.error('Specified cache directory is a file/link') raise FileNotFoundError('Could not create cache directory')
Avalon.error('Unable to continue, exiting...')
exit(1)
# start execution # start execution
@ -326,17 +342,17 @@ try:
begin_time = time.time() begin_time = time.time()
# if input specified is a single file # if input specified is a single file
if os.path.isfile(args.input): if args.input.is_file():
# upscale single video file # upscale single video file
Avalon.info(f'Upscaling single video file: {args.input}') Avalon.info(f'Upscaling single video file: {args.input}')
# check for input output format mismatch # check for input output format mismatch
if os.path.isdir(args.output): if args.output.is_dir():
Avalon.error('Input and output path type mismatch') Avalon.error('Input and output path type mismatch')
Avalon.error('Input is single file but output is directory') Avalon.error('Input is single file but output is directory')
raise Exception('input output path type mismatch') raise Exception('input output path type mismatch')
if not re.search('.*\..*$', args.output): if not re.search('.*\..*$', str(args.output)):
Avalon.error('No suffix found in output file path') Avalon.error('No suffix found in output file path')
Avalon.error('Suffix must be specified for FFmpeg') Avalon.error('Suffix must be specified for FFmpeg')
raise Exception('No suffix specified') raise Exception('No suffix specified')
@ -360,12 +376,12 @@ try:
upscaler.cleanup_temp_directories() upscaler.cleanup_temp_directories()
# if input specified is a directory # if input specified is a directory
elif os.path.isdir(args.input): elif args.input.is_dir():
# upscale videos in a directory # upscale videos in a directory
Avalon.info(f'Upscaling videos in directory: {args.input}') Avalon.info(f'Upscaling videos in 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))]: for input_video in [f for f in args.input.iterdir() if f.is_file()]:
output_video = os.path.join(args.output, input_video) output_video = 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) upscaler = Upscaler(input_video=args.input / input_video, output_video=output_video, method=args.method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings)
# set optional options # set optional options
upscaler.waifu2x_driver = args.driver upscaler.waifu2x_driver = args.driver

View File

@ -29,11 +29,14 @@ Installation Details:
import argparse import argparse
import json import json
import os import os
import pathlib
import re
import shutil import shutil
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import traceback import traceback
import urllib
import zipfile import zipfile
# Requests doesn't come with windows, therefore # Requests doesn't come with windows, therefore
@ -43,6 +46,9 @@ import zipfile
VERSION = '1.3.0' VERSION = '1.3.0'
# global static variables
LOCALAPPDATA = pathlib.Path(os.getenv('localappdata'))
def process_arguments(): def process_arguments():
"""Processes CLI arguments """Processes CLI arguments
@ -104,14 +110,15 @@ class Video2xSetup:
""" """
for file in self.trash: for file in self.trash:
try: try:
if os.path.isfile(file): if file.is_dir():
print(f'Deleting: {file}') print(f'Deleting directory: {file}')
os.remove(file)
else:
print(f'Deleting: {file}')
shutil.rmtree(file) shutil.rmtree(file)
except FileNotFoundError: else:
pass print(f'Deleting file: {file}')
file.unlink()
except Exception:
print(f'Error deleting: {file}')
traceback.print_exc()
def _install_ffmpeg(self): def _install_ffmpeg(self):
""" Install FFMPEG """ Install FFMPEG
@ -122,7 +129,7 @@ class Video2xSetup:
self.trash.append(ffmpeg_zip) self.trash.append(ffmpeg_zip)
with zipfile.ZipFile(ffmpeg_zip) as zipf: with zipfile.ZipFile(ffmpeg_zip) as zipf:
zipf.extractall(os.path.join(os.getenv('localappdata'), 'video2x')) zipf.extractall(LOCALAPPDATA / 'video2x')
def _install_waifu2x_caffe(self): def _install_waifu2x_caffe(self):
""" Install waifu2x_caffe """ Install waifu2x_caffe
@ -139,7 +146,7 @@ class Video2xSetup:
self.trash.append(waifu2x_caffe_zip) self.trash.append(waifu2x_caffe_zip)
with zipfile.ZipFile(waifu2x_caffe_zip) as zipf: with zipfile.ZipFile(waifu2x_caffe_zip) as zipf:
zipf.extractall(os.path.join(os.getenv('localappdata'), 'video2x')) zipf.extractall(LOCALAPPDATA / 'video2x')
def _install_waifu2x_converter_cpp(self): def _install_waifu2x_converter_cpp(self):
""" Install waifu2x_caffe """ Install waifu2x_caffe
@ -157,7 +164,7 @@ class Video2xSetup:
self.trash.append(waifu2x_converter_cpp_zip) self.trash.append(waifu2x_converter_cpp_zip)
with zipfile.ZipFile(waifu2x_converter_cpp_zip) as zipf: with zipfile.ZipFile(waifu2x_converter_cpp_zip) as zipf:
zipf.extractall(os.path.join(os.getenv('localappdata'), 'video2x', 'waifu2x-converter-cpp')) zipf.extractall(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp')
def _install_waifu2x_ncnn_vulkan(self): def _install_waifu2x_ncnn_vulkan(self):
""" Install waifu2x-ncnn-vulkan """ Install waifu2x-ncnn-vulkan
@ -176,12 +183,11 @@ class Video2xSetup:
# extract then move (to remove the top level directory) # extract then move (to remove the top level directory)
with zipfile.ZipFile(waifu2x_ncnn_vulkan_zip) as zipf: with zipfile.ZipFile(waifu2x_ncnn_vulkan_zip) as zipf:
extraction_path = os.path.join(tempfile.gettempdir(), 'waifu2x-ncnn-vulkan-ext') extraction_path = pathlib.Path(tempfile.gettempdir()) / 'waifu2x-ncnn-vulkan-ext'
zipf.extractall(extraction_path) zipf.extractall(extraction_path)
shutil.move(os.path.join(extraction_path, os.listdir(extraction_path)[0]), os.path.join(os.getenv('localappdata'), 'video2x', 'waifu2x-ncnn-vulkan')) shutil.move(extraction_path / os.listdir(extraction_path)[0], LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan')
self.trash.append(extraction_path) self.trash.append(extraction_path)
def _generate_config(self): def _generate_config(self):
""" Generate video2x config """ Generate video2x config
""" """
@ -190,21 +196,19 @@ class Video2xSetup:
template_dict = json.load(template) template_dict = json.load(template)
template.close() template.close()
local_app_data = os.getenv('localappdata')
# configure only the specified drivers # configure only the specified drivers
if self.driver == 'all': if self.driver == 'all':
template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = os.path.join(local_app_data, 'video2x', 'waifu2x-caffe', 'waifu2x-caffe-cui.exe') template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui.exe'
template_dict['waifu2x_converter']['waifu2x_converter_path'] = os.path.join(local_app_data, 'video2x', 'waifu2x-converter-cpp') template_dict['waifu2x_converter']['waifu2x_converter_path'] = LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp'
template_dict['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = os.path.join(local_app_data, 'video2x', 'waifu2x-ncnn-vulkan', 'waifu2x-ncnn-vulkan.exe') template_dict['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan.exe'
elif self.driver == 'waifu2x_caffe': elif self.driver == 'waifu2x_caffe':
template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = os.path.join(local_app_data, 'video2x', 'waifu2x-caffe', 'waifu2x-caffe-cui.exe') template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui.exe'
elif self.driver == 'waifu2x_converter': elif self.driver == 'waifu2x_converter':
template_dict['waifu2x_converter']['waifu2x_converter_path'] = os.path.join(local_app_data, 'video2x', 'waifu2x-converter-cpp') template_dict['waifu2x_converter']['waifu2x_converter_path'] = LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp'
elif self.driver == 'waifu2x_ncnn_vulkan': elif self.driver == 'waifu2x_ncnn_vulkan':
template_dict['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = os.path.join(local_app_data, 'video2x', 'waifu2x-ncnn-vulkan', 'waifu2x-ncnn-vulkan.exe') template_dict['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan.exe'
template_dict['ffmpeg']['ffmpeg_path'] = os.path.join(local_app_data, 'video2x', 'ffmpeg-latest-win64-static', 'bin') template_dict['ffmpeg']['ffmpeg_path'] = LOCALAPPDATA / 'video2x' / 'ffmpeg-latest-win64-static' / 'bin'
template_dict['video2x']['video2x_cache_directory'] = None template_dict['video2x']['video2x_cache_directory'] = None
template_dict['video2x']['preserve_frames'] = False template_dict['video2x']['preserve_frames'] = False
@ -220,14 +224,43 @@ def download(url, save_path, chunk_size=4096):
from tqdm import tqdm from tqdm import tqdm
import requests import requests
output_file = os.path.join(save_path, url.split('/')[-1]) save_path = pathlib.Path(save_path)
# create target folder if it doesn't exist
save_path.mkdir(parents=True, exist_ok=True)
# create requests stream for steaming file
stream = requests.get(url, stream=True, allow_redirects=True)
# get file name
file_name = None
if 'content-disposition' in stream.headers:
disposition = stream.headers['content-disposition']
try:
file_name = re.findall("filename=(.+)", disposition)[0].strip('"')
except IndexError:
pass
if file_name is None:
# output_file = f'{save_path}\\{stream.url.split("/")[-1]}'
output_file = save_path / stream.url.split('/')[-1]
else:
output_file = save_path / file_name
# decode url encoding
output_file = pathlib.Path(urllib.parse.unquote(str(output_file)))
# get total size for progress bar if provided in headers
total_size = 0
if 'content-length' in stream.headers:
total_size = int(stream.headers['content-length'])
# print download information summary
print(f'Downloading: {url}') print(f'Downloading: {url}')
print(f'Total size: {total_size}')
print(f'Chunk size: {chunk_size}') print(f'Chunk size: {chunk_size}')
print(f'Saving to: {output_file}') print(f'Saving to: {output_file}')
stream = requests.get(url, stream=True)
total_size = int(stream.headers['content-length'])
# Write content into file # Write content into file
with open(output_file, 'wb') as output: with open(output_file, 'wb') as output:
with tqdm(total=total_size, ascii=True) as progress_bar: with tqdm(total=total_size, ascii=True) as progress_bar:
@ -236,6 +269,7 @@ def download(url, save_path, chunk_size=4096):
output.write(chunk) output.write(chunk)
progress_bar.update(len(chunk)) progress_bar.update(len(chunk))
# return the full path of saved file
return output_file return output_file

View File

@ -11,7 +11,7 @@ for waifu2x-converter-cpp.
""" """
# built-in imports # built-in imports
import os import pathlib
import subprocess import subprocess
import threading import threading
@ -64,8 +64,7 @@ class Waifu2xConverter:
# models_rgb must be specified manually for waifu2x-converter-cpp # models_rgb must be specified manually for waifu2x-converter-cpp
# if it's not specified in the arguments, create automatically # if it's not specified in the arguments, create automatically
if self.waifu2x_settings['model-dir'] is None: if self.waifu2x_settings['model-dir'] is None:
self.waifu2x_settings['model-dir'] = os.path.join(self.waifu2x_settings['waifu2x_converter_path'], self.waifu2x_settings['model-dir'] = pathlib.Path(self.waifu2x_settings['waifu2x_converter_path']) / 'models_rgb'
'models_rgb')
# print thread start message # print thread start message
self.print_lock.acquire() self.print_lock.acquire()
@ -81,7 +80,7 @@ class Waifu2xConverter:
# the key doesn't need to be passed in this case # the key doesn't need to be passed in this case
if key == 'waifu2x_converter_path': if key == 'waifu2x_converter_path':
execute.append(os.path.join(str(value), 'waifu2x-converter-cpp.exe')) execute.append(pathlib.Path(str(value)) / 'waifu2x-converter-cpp.exe')
# null or None means that leave this option out (keep default) # null or None means that leave this option out (keep default)
elif value is None or value is False: elif value is None or value is False: