9 Commits

Author SHA1 Message Date
k4yt3x
6378a36d91 remove encoded images from memory to prevent exhaustion 2022-02-13 03:47:27 +00:00
k4yt3x
0d0fd70a24 redirected STDOUT and STDERR to prevent output from breaking the progress bar 2022-02-12 23:51:30 +00:00
K4YT3X
7c0e9c45d8 Merge pull request #598 from mayiflex/master
Update README.md
2022-02-12 20:24:07 +00:00
k4yt3x
7b60041529 replaced tqdm with rich.progress; enhanced error handling 2022-02-12 09:08:28 +00:00
k4yt3x
6ffd6282e0 removed tqdm files from project 2022-02-12 09:08:05 +00:00
mayiflex
c37e7f0d72 Update README.md
Fixed hyperlink to the Collab Notebook with instructions
2022-02-12 09:58:54 +01:00
k4yt3x
ca1e593874 let setup read version from init 2022-02-12 06:45:59 +00:00
k4yt3x
bfb0f339e2 fixed log format issue 2022-02-12 06:45:33 +00:00
k4yt3x
3690337092 fixed FFmpeg memory exhaustion issue by limiting the queue size 2022-02-12 05:14:00 +00:00
13 changed files with 199 additions and 143 deletions

View File

@@ -1,7 +1,7 @@
# Name: Video2X Dockerfile (CUDA) # Name: Video2X Dockerfile (CUDA)
# Creator: K4YT3X # Creator: K4YT3X
# Date Created: February 3, 2022 # Date Created: February 3, 2022
# Last Modified: February 4, 2022 # Last Modified: February 12, 2022
# stage 1: build the python components into wheels # stage 1: build the python components into wheels
FROM docker.io/nvidia/cuda:11.6.0-runtime-ubuntu20.04 AS builder FROM docker.io/nvidia/cuda:11.6.0-runtime-ubuntu20.04 AS builder
@@ -11,7 +11,7 @@ COPY . /video2x
WORKDIR /video2x WORKDIR /video2x
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
python3-pip python3-opencv python3-pil python3-tqdm \ python3-pip python3-opencv python3-pil \
python3-dev libvulkan-dev glslang-dev glslang-tools \ python3-dev libvulkan-dev glslang-dev glslang-tools \
build-essential swig git \ build-essential swig git \
&& git config --global http.postBuffer 1048576000 \ && git config --global http.postBuffer 1048576000 \
@@ -33,7 +33,7 @@ COPY . /video2x
WORKDIR /video2x WORKDIR /video2x
RUN apt-get install -y --no-install-recommends \ RUN apt-get install -y --no-install-recommends \
python3-pip python3-dev \ python3-pip python3-dev \
python3-opencv python3-pil python3-tqdm \ python3-opencv python3-pil \
mesa-vulkan-drivers ffmpeg \ mesa-vulkan-drivers ffmpeg \
&& pip install --no-cache-dir --no-index -f /wheels . \ && pip install --no-cache-dir --no-index -f /wheels . \
&& apt-get clean \ && apt-get clean \

5
NOTICE
View File

@@ -41,8 +41,3 @@ https://github.com/python-pillow/Pillow.
This product depends on Rich, which is available under This product depends on Rich, which is available under
the MIT License. The source code can be found at the MIT License. The source code can be found at
https://github.com/Textualize/rich. https://github.com/Textualize/rich.
This product depends on tqdm, which is available under
the Mozilla Public License Version 2.0 and the MIT License.
The source code can be found at
https://github.com/tqdm/tqdm.

View File

@@ -18,7 +18,7 @@ The latest Windows update is built based on version 4.8.1. GUI is not available
## [📔 Google Colab](https://colab.research.google.com/drive/1gWEwcA9y57EsxwOjmLNmNMXPsafw0kGo) ## [📔 Google Colab](https://colab.research.google.com/drive/1gWEwcA9y57EsxwOjmLNmNMXPsafw0kGo)
You can use Video2X on [Google Colab](https://colab.research.google.com/) **for free** if you don't have a powerful GPU of your own. You can borrow a powerful GPU (Tesla K80, T4, P4, or P100) on Google's server for free for a maximum of 12 hours per session. **Please use the free resource fairly** and do not create sessions back-to-back and run upscaling 24/7. This might result in you getting banned. You can get [Colab Pro/Pro+](https://colab.research.google.com/signup/pricing) if you'd like to use better GPUs and get longer runtimes. Usage instructions are embedded in the [Colab Notebook](https://github.com/k4yt3x/video2x/actions/workflows/ci.yml). You can use Video2X on [Google Colab](https://colab.research.google.com/) **for free** if you don't have a powerful GPU of your own. You can borrow a powerful GPU (Tesla K80, T4, P4, or P100) on Google's server for free for a maximum of 12 hours per session. **Please use the free resource fairly** and do not create sessions back-to-back and run upscaling 24/7. This might result in you getting banned. You can get [Colab Pro/Pro+](https://colab.research.google.com/signup/pricing) if you'd like to use better GPUs and get longer runtimes. Usage instructions are embedded in the [Colab Notebook](https://colab.research.google.com/drive/1gWEwcA9y57EsxwOjmLNmNMXPsafw0kGo).
## [🌙 Download Nightly Releases](https://github.com/k4yt3x/video2x/actions/workflows/ci.yml) ## [🌙 Download Nightly Releases](https://github.com/k4yt3x/video2x/actions/workflows/ci.yml)
@@ -82,29 +82,29 @@ Copyright (c) 2018-2022 K4YT3X and contributors.
This project includes or depends on these following projects: This project includes or depends on these following projects:
| Project | License | | Project | License |
| ------------------------------------------------------------------- | -------------------- | | ------------------------------------------------------------------- | --------------- |
| [FFmpeg](https://www.ffmpeg.org/) | LGPLv2.1, GPLv2 | | [FFmpeg](https://www.ffmpeg.org/) | LGPLv2.1, GPLv2 |
| [waifu2x-ncnn-vulkan](https://github.com/nihui/waifu2x-ncnn-vulkan) | MIT License | | [waifu2x-ncnn-vulkan](https://github.com/nihui/waifu2x-ncnn-vulkan) | MIT License |
| [srmd-ncnn-vulkan](https://github.com/nihui/srmd-ncnn-vulkan) | MIT License | | [srmd-ncnn-vulkan](https://github.com/nihui/srmd-ncnn-vulkan) | MIT License |
| [realsr-ncnn-vulkan](https://github.com/nihui/realsr-ncnn-vulkan) | MIT License | | [realsr-ncnn-vulkan](https://github.com/nihui/realsr-ncnn-vulkan) | MIT License |
| [rife-ncnn-vulkan](https://github.com/nihui/rife-ncnn-vulkan) | MIT License | | [rife-ncnn-vulkan](https://github.com/nihui/rife-ncnn-vulkan) | MIT License |
| [ffmpeg-python](https://github.com/kkroening/ffmpeg-python) | Apache-2.0 | | [ffmpeg-python](https://github.com/kkroening/ffmpeg-python) | Apache-2.0 |
| [Loguru](https://github.com/Delgan/loguru) | MIT License | | [Loguru](https://github.com/Delgan/loguru) | MIT License |
| [opencv-python](https://github.com/opencv/opencv-python) | MIT License | | [opencv-python](https://github.com/opencv/opencv-python) | MIT License |
| [Pillow](https://github.com/python-pillow/Pillow) | HPND License | | [Pillow](https://github.com/python-pillow/Pillow) | HPND License |
| [Rich](https://github.com/Textualize/rich) | MIT License | | [Rich](https://github.com/Textualize/rich) | MIT License |
| [tqdm](https://github.com/tqdm/tqdm) | MPLv2.0, MIT License |
Legacy versions of this project includes or depends on these following projects: Legacy versions of this project includes or depends on these following projects:
| Project | License | | Project | License |
| --------------------------------------------------------------------------- | ----------- | | --------------------------------------------------------------------------- | -------------------- |
| [waifu2x-caffe](https://github.com/lltcggie/waifu2x-caffe) | MIT License | | [waifu2x-caffe](https://github.com/lltcggie/waifu2x-caffe) | MIT License |
| [waifu2x-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp) | MIT License | | [waifu2x-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp) | MIT License |
| [Anime4K](https://github.com/bloc97/Anime4K) | MIT License | | [Anime4K](https://github.com/bloc97/Anime4K) | MIT License |
| [Anime4KCPP](https://github.com/TianZerL/Anime4KCPP) | MIT License | | [Anime4KCPP](https://github.com/TianZerL/Anime4KCPP) | MIT License |
| [Gifski](https://github.com/ImageOptim/gifski) | AGPLv3 | | [Gifski](https://github.com/ImageOptim/gifski) | AGPLv3 |
| [tqdm](https://github.com/tqdm/tqdm) | MPLv2.0, MIT License |
More licensing information can be found in the [NOTICES](NOTICES) file. More licensing information can be found in the [NOTICES](NOTICES) file.

View File

@@ -1,49 +0,0 @@
`tqdm` is a product of collaborative work.
Unless otherwise stated, all authors (see commit logs) retain copyright
for their respective work, and release the work under the MIT licence
(text below).
Exceptions or notable authors are listed below
in reverse chronological order:
* files: *
MPLv2.0 2015-2021 (c) Casper da Costa-Luis
[casperdcl](https://github.com/casperdcl).
* files: tqdm/_tqdm.py
MIT 2016 (c) [PR #96] on behalf of Google Inc.
* files: tqdm/_tqdm.py setup.py README.rst MANIFEST.in .gitignore
MIT 2013 (c) Noam Yorav-Raphael, original author.
[PR #96]: https://github.com/tqdm/tqdm/pull/96
Mozilla Public Licence (MPL) v. 2.0 - Exhibit A
-----------------------------------------------
This Source Code Form is subject to the terms of the
Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this project,
You can obtain one at https://mozilla.org/MPL/2.0/.
MIT License (MIT)
-----------------
Copyright (c) 2013 noamraph
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,3 +1,3 @@
[build-system] [build-system]
requires = ["setuptools>=44", "wheel", "setuptools_scm[toml]>=3.4.3"] requires = ["setuptools>=46.4", "wheel", "setuptools_scm[toml]>=3.4.3"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"

View File

@@ -1,7 +1,7 @@
# Name: Video2X PyPI setup file # Name: Video2X PyPI setup file
# Creator: K4YT3X # Creator: K4YT3X
# Date Created: June 17, 2021 # Date Created: June 17, 2021
# Last Modified: February 11, 2022 # Last Modified: February 12, 2022
# build & publish commands # build & publish commands
# pip install --user -U setuptools wheel twine build # pip install --user -U setuptools wheel twine build
@@ -10,7 +10,7 @@
[metadata] [metadata]
name = video2x name = video2x
version = 5.0.0-beta1 version = attr: video2x.__version__
author = K4YT3X author = K4YT3X
author_email = i@k4yt3x.com author_email = i@k4yt3x.com
license = GNU Affero General Public License v3.0 license = GNU Affero General Public License v3.0
@@ -39,7 +39,6 @@ install_requires =
opencv-python opencv-python
pillow pillow
rich rich
tqdm
realsr-ncnn-vulkan-python realsr-ncnn-vulkan-python
rife-ncnn-vulkan-python rife-ncnn-vulkan-python
srmd-ncnn-vulkan-python srmd-ncnn-vulkan-python

View File

@@ -22,6 +22,11 @@ Date Created: July 3, 2021
Last Modified: February 11, 2022 Last Modified: February 11, 2022
""" """
# version assignment has to precede imports to
# prevent setup.cfg from producing import errors
__version__ = "5.0.0-beta2"
# local imports
from .video2x import Video2X from .video2x import Video2X
from .upscaler import Upscaler from .upscaler import Upscaler
from .interpolator import Interpolator from .interpolator import Interpolator

View File

@@ -22,6 +22,7 @@ Date Created: July 3, 2021
Last Modified: February 11, 2022 Last Modified: February 11, 2022
""" """
# local imports
from .video2x import main from .video2x import main

View File

@@ -19,13 +19,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
Name: Video Decoder Name: Video Decoder
Author: K4YT3X Author: K4YT3X
Date Created: June 17, 2021 Date Created: June 17, 2021
Last Modified: June 17, 2021 Last Modified: February 12, 2022
""" """
# built-in imports # built-in imports
import contextlib
import os import os
import pathlib import pathlib
import queue import queue
import signal
import subprocess import subprocess
import threading import threading
@@ -119,20 +121,29 @@ class VideoDecoder(threading.Thread):
"RGB", (self.input_width, self.input_height), buffer "RGB", (self.input_width, self.input_height), buffer
) )
self.processing_queue.put( # keep checking if the running flag is set to False
( # while waiting to put the next image into the queue
frame_index, while self.running:
(previous_image, image), with contextlib.suppress(queue.Full):
self.processing_settings, self.processing_queue.put(
) (
) frame_index,
previous_image = image (previous_image, image),
self.processing_settings,
),
timeout=0.1,
)
break
previous_image = image
frame_index += 1 frame_index += 1
# most likely "not enough image data" # most likely "not enough image data"
except ValueError as e: except ValueError as e:
logger.exception(e)
# ignore queue closed
if not "is closed" in str(e):
logger.exception(e)
break break
# send exceptions into the client connection pipe # send exceptions into the client connection pipe
@@ -141,9 +152,14 @@ class VideoDecoder(threading.Thread):
logger.exception(e) logger.exception(e)
break break
# send SIGINT (2) to FFmpeg
# this instructs it to finalize and exit
if self.decoder.poll() is None:
self.decoder.send_signal(signal.SIGTERM)
# ensure the decoder has exited # ensure the decoder has exited
self.decoder.wait() self.decoder.wait()
logger.debug("Decoder thread exiting") logger.info("Decoder thread exiting")
self.running = False self.running = False
return super().run() return super().run()

View File

@@ -133,8 +133,12 @@ class VideoEncoder(threading.Thread):
time.sleep(0.1) time.sleep(0.1)
continue continue
# send the image to FFmpeg for encoding
self.encoder.stdin.write(image.tobytes()) self.encoder.stdin.write(image.tobytes())
# remove the image from memory
self.processed_frames[frame_index] = None
with self.processed.get_lock(): with self.processed.get_lock():
self.processed.value += 1 self.processed.value += 1
@@ -156,7 +160,7 @@ class VideoEncoder(threading.Thread):
# wait for process to terminate # wait for process to terminate
self.encoder.wait() self.encoder.wait()
logger.debug("Encoder thread exiting") logger.info("Encoder thread exiting")
self.running = False self.running = False
return super().run() return super().run()

View File

@@ -1,13 +0,0 @@
# *-ncnn-vulkan driver backends
realsr-ncnn-vulkan-python
rife-ncnn-vulkan-python
srmd-ncnn-vulkan-python
waifu2x-ncnn-vulkan-python
# regular Python packages from PyPI
ffmpeg-python
loguru
opencv-python
pillow
rich
tqdm

View File

@@ -66,7 +66,9 @@ class Upscaler(multiprocessing.Process):
def run(self): def run(self):
self.running = True self.running = True
logger.info(f"Upscaler process {self.name} initiating") logger.opt(colors=True).info(
f"Upscaler process <blue>{self.name}</blue> initiating"
)
driver_objects = {} driver_objects = {}
while self.running: while self.running:
try: try:
@@ -185,7 +187,9 @@ class Upscaler(multiprocessing.Process):
logger.exception(e) logger.exception(e)
break break
logger.info(f"Upscaler process {self.name} terminating") logger.opt(colors=True).info(
f"Upscaler process <blue>{self.name}</blue> terminating"
)
self.running = False self.running = False
return super().run() return super().run()

View File

@@ -27,7 +27,7 @@ __ __ _ _ ___ __ __
Name: Video2X Name: Video2X
Creator: K4YT3X Creator: K4YT3X
Date Created: Feb 24, 2018 Date Created: Feb 24, 2018
Last Modified: February 8, 2022 Last Modified: February 12, 2022
Editor: BrianPetkovsek Editor: BrianPetkovsek
Last Modified: June 17, 2019 Last Modified: June 17, 2019
@@ -40,6 +40,7 @@ Last Modified: March 23, 2020
""" """
# local imports # local imports
from . import __version__
from .decoder import VideoDecoder from .decoder import VideoDecoder
from .encoder import VideoEncoder from .encoder import VideoEncoder
from .interpolator import Interpolator from .interpolator import Interpolator
@@ -57,18 +58,27 @@ import time
# third-party imports # third-party imports
from loguru import logger from loguru import logger
from rich import print from rich import print
from tqdm import tqdm from rich.console import Console
from rich.file_proxy import FileProxy
from rich.progress import (
BarColumn,
Progress,
ProgressColumn,
Task,
TimeElapsedColumn,
TimeRemainingColumn,
)
from rich.text import Text
import cv2 import cv2
import ffmpeg import ffmpeg
VERSION = "5.0.0"
LEGAL_INFO = """Video2X {} LEGAL_INFO = """Video2X {}
Author: K4YT3X Author: K4YT3X
License: GNU GPL v3 License: GNU GPL v3
Github Page: https://github.com/k4yt3x/video2x Github Page: https://github.com/k4yt3x/video2x
Contact: k4yt3x@k4yt3x.com""".format( Contact: k4yt3x@k4yt3x.com""".format(
VERSION __version__
) )
UPSCALING_DRIVERS = [ UPSCALING_DRIVERS = [
@@ -87,6 +97,27 @@ DRIVER_FIXED_SCALING_RATIOS = {
"realsr": [4], "realsr": [4],
} }
# progress bar labels for different modes
MODE_LABELS = {"upscale": "Upscaling", "interpolate": "Interpolating"}
# format string for Loguru loggers
LOGURU_FORMAT = (
"<green>{time:HH:mm:ss.SSSSSS!UTC}</green> | "
"<level>{level: <8}</level> | "
"<level>{message}</level>"
)
class ProcessingSpeedColumn(ProgressColumn):
"""Custom progress bar column that displays the processing speed"""
def render(self, task: Task) -> Text:
speed = task.finished_speed or task.speed
return Text(
f"{round(speed, 2) if isinstance(speed, float) else '?'} FPS",
style="progress.data.speed",
)
class Video2X: class Video2X:
""" """
@@ -143,9 +174,25 @@ class Video2X:
processes: int, processes: int,
processing_settings: tuple, processing_settings: tuple,
): ):
# record original STDOUT and STDERR for restoration
original_stdout = sys.stdout
original_stderr = sys.stderr
# create console for rich's Live display
console = Console()
# redirect STDOUT and STDERR to console
sys.stdout = FileProxy(console, sys.stdout)
sys.stderr = FileProxy(console, sys.stderr)
# re-add Loguru to point to the new STDERR
logger.remove()
logger.add(sys.stderr, colorize=True, format=LOGURU_FORMAT)
# initialize values # initialize values
self.processor_processes = [] self.processor_processes = []
self.processing_queue = multiprocessing.Queue() self.processing_queue = multiprocessing.Queue(maxsize=processes * 10)
processed_frames = multiprocessing.Manager().list([None] * total_frames) processed_frames = multiprocessing.Manager().list([None] * total_frames)
self.processed = multiprocessing.Value("I", 0) self.processed = multiprocessing.Value("I", 0)
@@ -161,19 +208,11 @@ class Video2X:
) )
self.decoder.start() self.decoder.start()
# in interpolate mode, the frame rate is doubled
if mode == "upscale":
progress = tqdm(total=total_frames, desc=f"UPSC {input_path.name}")
# elif mode == "interpolate":
else:
frame_rate *= 2
progress = tqdm(total=total_frames, desc=f"INTERP {input_path.name}")
# set up and start encoder thread # set up and start encoder thread
logger.info("Starting video encoder") logger.info("Starting video encoder")
self.encoder = VideoEncoder( self.encoder = VideoEncoder(
input_path, input_path,
frame_rate, frame_rate * 2 if mode == "interpolate" else frame_rate,
output_path, output_path,
output_width, output_width,
output_height, output_height,
@@ -183,7 +222,7 @@ class Video2X:
) )
self.encoder.start() self.encoder.start()
# create upscaler processes # create processor processes
for process_name in range(processes): for process_name in range(processes):
process = Processor(self.processing_queue, processed_frames) process = Processor(self.processing_queue, processed_frames)
process.name = str(process_name) process.name = str(process_name)
@@ -191,25 +230,54 @@ class Video2X:
process.start() process.start()
self.processor_processes.append(process) self.processor_processes.append(process)
# create progress bar # a temporary variable that stores the exception
try: exception = []
# wait for jobs in queue to deplete
while self.encoder.is_alive() is True:
for process in self.processor_processes:
if not process.is_alive():
raise Exception("process died unexpectedly")
progress.n = self.processed.value
progress.refresh()
time.sleep(0.1)
logger.info("Encoding has completed") try:
progress.n = self.processed.value # create progress bar
progress.refresh() with Progress(
"[progress.description]{task.description}",
BarColumn(finished_style="green"),
"[progress.percentage]{task.percentage:>3.0f}%",
"[color(240)]({task.completed}/{task.total})",
ProcessingSpeedColumn(),
TimeElapsedColumn(),
"<",
TimeRemainingColumn(),
console=console,
disable=True,
) as progress:
task = progress.add_task(
f"[cyan]{MODE_LABELS.get(mode, 'Unknown')}", total=total_frames
)
# wait for jobs in queue to deplete
while self.processed.value < total_frames - 1:
time.sleep(0.5)
for process in self.processor_processes:
if not process.is_alive():
raise Exception("process died unexpectedly")
# show progress bar when upscale starts
if progress.disable is True and self.processed.value > 0:
progress.disable = False
progress.start()
# update progress
progress.update(task, completed=self.processed.value)
progress.update(task, completed=total_frames)
logger.info("Processing has completed")
# if SIGTERM is received or ^C is pressed # if SIGTERM is received or ^C is pressed
# TODO: pause and continue here # TODO: pause and continue here
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt) as e:
logger.warning("Exit signal received, terminating") logger.warning("Exit signal received, terminating")
exception.append(e)
except Exception as e:
logger.exception(e)
exception.append(e)
finally: finally:
# mark processing queue as closed # mark processing queue as closed
@@ -230,6 +298,18 @@ class Video2X:
self.decoder.join() self.decoder.join()
self.encoder.join() self.encoder.join()
# raise the error if there is any
if len(exception) > 0:
raise exception[0]
# restore original STDOUT and STDERR
sys.stdout = original_stdout
sys.stderr = original_stderr
# re-add Loguru to point to the restored STDERR
logger.remove()
logger.add(sys.stderr, colorize=True, format=LOGURU_FORMAT)
def upscale( def upscale(
self, self,
input_path: pathlib.Path, input_path: pathlib.Path,
@@ -414,17 +494,23 @@ def main():
if os.environ.get("LOGURU_LEVEL") is None: if os.environ.get("LOGURU_LEVEL") is None:
os.environ["LOGURU_LEVEL"] = args.loglevel.upper() os.environ["LOGURU_LEVEL"] = args.loglevel.upper()
# set logger format # remove default handler
if os.environ.get("LOGURU_FORMAT") is None: logger.remove()
os.environ[
"LOGURU_FORMAT" # add new sink with custom handler
] = "<fg 240>{time:HH:mm:ss!UTC}</fg 240> | <level>{level: <8}</level> | <level>{message}</level>" logger.add(sys.stderr, colorize=True, format=LOGURU_FORMAT)
# display version and lawful informaition # display version and lawful informaition
if args.version: if args.version:
print(LEGAL_INFO) print(LEGAL_INFO)
sys.exit(0) sys.exit(0)
# print package version and copyright notice
logger.opt(colors=True).info(f"<magenta>Video2X {__version__}</magenta>")
logger.opt(colors=True).info(
"<magenta>Copyright (C) 2018-2022 K4YT3X and contributors.</magenta>"
)
# initialize upscaler object # initialize upscaler object
video2x = Video2X() video2x = Video2X()
@@ -448,5 +534,13 @@ def main():
args.threshold, args.threshold,
args.driver, args.driver,
) )
logger.success("Processing completed successfully")
# don't print the traceback for manual terminations
except (SystemExit, KeyboardInterrupt) as e:
raise SystemExit(e)
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
raise SystemExit(e)