diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/FSRImageVideoUpscalerFrontend.iml b/.idea/FSRImageVideoUpscalerFrontend.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/FSRImageVideoUpscalerFrontend.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..dc9ea49 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..345cc4c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/bin/handler.py b/bin/handler.py new file mode 100644 index 0000000..985e688 --- /dev/null +++ b/bin/handler.py @@ -0,0 +1,217 @@ +########################################################### +# +# Handler for FSRImageVideoUpscalerFrontend +# +# This code is licensed under the GPL V3 License! +# Developed 2022 by Janis Hutz +# +########################################################### + + +import os +import sys +import ffmpeg +import configparser +import time + +# Loading the config file to get user preferred temp path +config = configparser.ConfigParser() +config.read('../config/settings.ini') + + +class Handler: + def __init__(self): + self.os_type = sys.platform + self.command = "" + self.tmppath = "" + self.videometa = {} + + def handler(self, fsrpath, filepath, quality_mode, quality_setting, output_path, ffmpegpath): + # Function to be called when using this class as this function automatically determines if file is video or image + if self.os_type == "linux": + self.tmppath = "/tmp/fsru/" # config["PathSettings"]["tmpPathLinux"] + elif self.os_type == "win32": + self.tmppath = config["PathSettings"]["tmpPathWindows"] + else: + print("OS CURRENTLY UNSUPPORTED!") + return False + # checking for spaces in filepath (for use with terminal commands) + self.filepath = "" + for self.letter in filepath: + if self.letter == " ": + self.filepath += "\ " + else: + self.filepath += self.letter + + # Determining filetype + if str(filepath)[len(filepath) - 4:] == ".mp4" or str(filepath)[len(filepath) - 4:] == ".mkv": + print("upscaling video") + self.video_scaling(ffmpegpath, fsrpath, filepath, quality_mode, quality_setting, output_path) + elif str(filepath)[len(filepath) - 4:] == ".JPG" or str(filepath)[len(filepath) - 4:] == ".png" or str(filepath)[len(filepath) - 4:] == ".jpg": + print("upscaling image") + self.photo_scaling(fsrpath, filepath, quality_mode, quality_setting, output_path) + else: + print("not supported") + + def photo_scaling(self, fsrpath, filepath, quality_mode, quality_setting, output_path): + # DO NOT CALL THIS! Use Handler().handler() instead! + if quality_mode == "default": + if self.os_type == "linux": + self.command = f"wine {fsrpath} -QualityMode {quality_setting} {self.filepath} {output_path}" + elif self.os_type == "win32": + self.command = f"{fsrpath} -QualityMode {quality_setting} {self.filepath} {output_path}" + else: + print("OS CURRENTLY UNSUPPORTED!") + return False + + os.system(self.command) + print("photo upscaled") + else: + if self.os_type == "linux": + self.command = f"wine {fsrpath} -Scale {quality_setting} {self.filepath} {output_path}" + elif self.os_type == "win32": + self.command = f"{fsrpath} -Scale {quality_setting} {self.filepath} {output_path}" + else: + print("OS CURRENTLY UNSUPPORTED!") + return False + os.system(self.command) + print("photo upscaled") + + def video_scaling(self, ffmpegpath, fsrpath, filepath, quality_mode, quality_setting, output_path): + # DO NOT CALL THIS! Use Handler().handler() instead! + self.videometa = ffmpeg.probe(str(filepath))["streams"].pop(0) + # Retrieving Video metadata + self.duration = self.videometa.get("duration") + self.frames = self.videometa.get("nb_frames") + self.framerate = round(float(self.frames) / float(self.duration), 1) + + # Splitting video into frames + try: + os.mkdir(self.tmppath) + except FileExistsError: + pass + + if self.os_type == "linux": + print("linux") + self.command = f"ffmpeg -i {str(self.filepath)} {self.tmppath}thumb%04d.jpg -hide_banner" + elif self.os_type == "win32": + self.command = f"{ffmpegpath} -i {str(self.filepath)} {self.tmppath}thumb%04d.jpg -hide_banner" + else: + print("OS CURRENTLY UNSUPPORTED!") + return False + os.system(self.command) + print("video split") + + # Locate Images and assemble FSR-Command + self.files = "" + self.filelist = os.listdir(self.tmppath) + self.filelist.pop(0) + self.filelist.reverse() + self.number = 0 + for self.file in self.filelist: + self.number += 1 + self.files += f"{self.tmppath}{self.file} {self.tmppath}upscaled/USImage{str(self.number).zfill(4)}.jpg " + self.maxlength = 32000 + self.pos = 1 + + # Refactoring of commands that are longer than 32K characters + if len(self.files) > self.maxlength: + print("shrinking command length") + self.fileout = [] + + while self.files[self.maxlength - self.pos:self.maxlength - self.pos + 1] != " ": + self.pos += 1 + self.file_processing = self.files[:self.maxlength - self.pos] + if self.file_processing[len(self.file_processing) - 13:len(self.file_processing) - 8] == "thumb": + self.pos += 5 + else: + pass + while self.files[self.maxlength - self.pos:self.maxlength - self.pos + 1] != " ": + self.pos += 1 + self.fileout.append(self.files[:self.maxlength - self.pos]) + self.filesopt = self.files[self.maxlength - self.pos:] + self.posx = 0 + self.posy = self.maxlength + + # Command refactoring for commands that are longer than 64K characters + if len(self.filesopt) > self.maxlength: + while len(self.filesopt) > self.maxlength: + self.posx += self.maxlength - self.pos + self.posy += self.maxlength - self.pos + self.pos = 1 + while self.files[self.posy - self.pos:self.posy - self.pos + 1] != " ": + self.pos += 1 + self.file_processing = self.files[self.posx:self.posy - self.pos] + if self.file_processing[len(self.file_processing) - 13:len(self.file_processing) - 8] == "thumb": + self.pos += 5 + else: + pass + while self.files[self.posy - self.pos:self.posy - self.pos + 1] != " ": + self.pos += 1 + + self.file_processing = self.files[self.posx:self.posy - self.pos] + self.fileout.append(self.file_processing) + self.filesopt = self.files[self.posy - self.pos:] + self.fileout.append(self.filesopt) + else: + self.fileout.append(self.files[self.maxlength - self.pos:]) + else: + self.fileout.append(self.files) + print("filepath assembled") + + try: + os.mkdir(f"{self.tmppath}upscaled/") + except FileExistsError: + pass + + # Upscaling images + print("\n\n\nUpscaling images... \n\n\n") + while self.fileout != []: + self.files_handle = self.fileout.pop(0) + if quality_mode == "default": + if self.os_type == "linux": + self.command = f"wine {fsrpath} -QualityMode {quality_setting} {self.files_handle}" + elif self.os_type == "win32": + self.command = f"{fsrpath} -QualityMode {quality_setting} {self.files_handle}" + else: + print("OS CURRENTLY UNSUPPORTED!") + return False + else: + if quality_mode == "default": + if self.os_type == "linux": + self.command = f"wine {fsrpath} -Scale {quality_setting} {self.files_handle} {self.tmppath}" + elif self.os_type == "win32": + self.command = f"{fsrpath} -Scale {quality_setting} {self.files_handle} {self.tmppath}" + else: + print("OS CURRENTLY UNSUPPORTED!") + return False + print(self.command, "\n\n\nCOMMAND to EXECUTE\n\n\n") + os.system(self.command) + print("Finished upscaling this section.") + time.sleep(3) + + # get Video's audio + print("Retrieving Video's audio to append") + os.remove(f"{self.tmppath}audio.aac") + os.remove(f"{output_path}") + if self.os_type == "linux": + self.command = f"ffmpeg -i {self.filepath} -vn -acodec copy {self.tmppath}audio.aac" + elif self.os_type == "win32": + self.command = f"{ffmpegpath} -i {self.filepath} -vn -acodec copy {self.tmppath}audio.aac" + else: + print("OS CURRENTLY UNSUPPORTED!") + return False + os.system(self.command) + + # reassemble Video + print("Reassembling Video... with framerate @", self.framerate) + if self.os_type == "linux": + self.command = f"ffmpeg -framerate {self.framerate} -i {self.tmppath}upscaled/USImage%04d.jpg {output_path} -i {self.tmppath}audio.aac" + elif self.os_type == "win32": + self.command = f"{ffmpegpath} -framerate {self.framerate} -i {self.tmppath}upscaled/USImage%04d.jpg {output_path} -i {self.tmppath}audio.aac" + else: + print("OS CURRENTLY UNSUPPORTED!") + return False + os.system(self.command) + + print("\n\n\n DONE \n\n\n\n") diff --git a/bin/lib/FidelityFX_CLI.exe b/bin/lib/FidelityFX_CLI.exe new file mode 100644 index 0000000..166bd2e Binary files /dev/null and b/bin/lib/FidelityFX_CLI.exe differ diff --git a/config/settings.ini b/config/settings.ini new file mode 100644 index 0000000..a59de6b --- /dev/null +++ b/config/settings.ini @@ -0,0 +1,7 @@ +[PathSettings] +defaultOutputPath = $HOME/FSRImageVideoUpscaler/ +tmpPathLinux = /tmp/ +tmpPathWindows = %TEMP% + +[DevSettings] +loggerReqLevel = DEBUG \ No newline at end of file diff --git a/dev/get_metadata.py b/dev/get_metadata.py new file mode 100644 index 0000000..e1acb45 --- /dev/null +++ b/dev/get_metadata.py @@ -0,0 +1,17 @@ +import ffmpeg + + +class MetaDataParser: + def __init__(self): + pass + + def get_metadata(self, filepath): + return ffmpeg.probe(str(filepath))["streams"] + + +videometa = MetaDataParser().get_metadata("/mnt/storage/SORTED/Videos/OBS_Rec/Behalten/2019-12-19 18-21-36.mp4").pop(0) +duration = videometa.get("duration") +frames = videometa.get("nb_frames") +framerate = float(frames) / float(duration) +print(framerate) +print(videometa) diff --git a/dev/test.py b/dev/test.py new file mode 100644 index 0000000..2b8ced8 --- /dev/null +++ b/dev/test.py @@ -0,0 +1,4 @@ +import os + +test = f"ffmpeg" +os.system(test) diff --git a/fsrimagevideoupscaler.py b/fsrimagevideoupscaler.py new file mode 100644 index 0000000..ad673c0 --- /dev/null +++ b/fsrimagevideoupscaler.py @@ -0,0 +1,103 @@ +########################################################### +# +# FSRImageVideoUpscalerFrontend written in GTK+ +# +# This code is licensed under the GPL V3 License! +# Developed 2022 by Janis Hutz +# +########################################################### + +import gi +import bin.handler +import multiprocessing + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + + +handler = bin.handler.Handler() + + +class HomeWindow(Gtk.Window): + def __init__(self): + super().__init__(title="Test") + self.save_file = "" + self.open_file = "" + + # Spawn box + self.box = Gtk.Box(spacing=6) + self.add(self.box) + + # Create filechooser button + self.filechoosebutton = Gtk.Button(label="Choose Input File") + self.filechoosebutton.connect("clicked", self.filechooser_clicked) + self.box.pack_start(self.filechoosebutton, True, True, 10) + + # Create output filechooser button + self.opfchooserbutton = Gtk.Button(label="Choose Output File") + self.opfchooserbutton.connect("clicked", self.opfilechooser_clicked) + self.box.pack_start(self.opfchooserbutton, True, True, 10) + + # Create start button + self.start_button = Gtk.Button(label="Start upscaling") + self.start_button.connect("clicked", self.start_clicked) + self.box.pack_start(self.start_button, True, True, 10) + + def filechooser_clicked(self, widget): + self.filechooserdialog = Gtk.FileChooserDialog(title="Choose input file", action=Gtk.FileChooserAction.OPEN) + self.filechooserdialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, + ) + self.response = self.filechooserdialog.run() + if self.response == Gtk.ResponseType.OK: + print("ok, selected file:", self.filechooserdialog.get_filename()) + self.open_file = self.filechooserdialog.get_filename() + elif self.response == Gtk.ResponseType.CANCEL: + print("cancel") + self.filechooserdialog.destroy() + + def opfilechooser_clicked(self, widget): + self.filechooserdialog_save = Gtk.FileChooserDialog(title="Choose output file", action=Gtk.FileChooserAction.SAVE) + Gtk.FileChooser.set_do_overwrite_confirmation(self.filechooserdialog_save, True) + Gtk.FileChooser.set_current_name(self.filechooserdialog_save, "video.mp4") + self.filechooserdialog_save.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_SAVE, + Gtk.ResponseType.OK, + ) + self.response = self.filechooserdialog_save.run() + if self.response == Gtk.ResponseType.OK: + print("ok, selected file:", self.filechooserdialog_save.get_filename()) + self.save_file = self.filechooserdialog_save.get_filename() + elif self.response == Gtk.ResponseType.CANCEL: + print("cancel") + self.filechooserdialog_save.destroy() + + def start_clicked(self, widget): + if str(self.open_file) != "" and str(self.save_file) != "": + print("ok") + self.go = True + if self.go: + self.scaler = multiprocessing.Process(name="scaler", + target=handler.handler, + args=("./bin/lib/FidelityFX_CLI.exe", + self.open_file, + "default", + "Quality", + self.save_file, + "./bin/lib/ffmpeg.exe") + ) + self.scaler.start() + else: + print("no file specified") + + +win = HomeWindow() +win.set_default_size(800, 600) +win.connect("destroy", Gtk.main_quit) +win.show_all() +Gtk.main()