diff --git a/pdm.lock b/pdm.lock index 4b66696..24ca6a8 100644 --- a/pdm.lock +++ b/pdm.lock @@ -26,6 +26,11 @@ name = "commonmark" version = "0.9.1" summary = "Python parser for the CommonMark Markdown spec" +[[package]] +name = "evdev" +version = "1.4.0" +summary = "Bindings to the Linux input handling subsystem" + [[package]] name = "ffmpeg-python" version = "0.2.0" @@ -115,12 +120,68 @@ version = "2.11.2" requires_python = ">=3.5" summary = "Pygments is a syntax highlighting package written in Python." +[[package]] +name = "pynput" +version = "1.7.6" +summary = "Monitor and control user input devices" +dependencies = [ + "evdev>=1.3; \"linux\" in sys_platform", + "pyobjc-framework-ApplicationServices>=8.0; sys_platform == \"darwin\"", + "pyobjc-framework-Quartz>=8.0; sys_platform == \"darwin\"", + "python-xlib>=0.17; \"linux\" in sys_platform", + "six", +] + +[[package]] +name = "pyobjc-core" +version = "8.4.1" +requires_python = ">=3.6" +summary = "Python<->ObjC Interoperability Module" + +[[package]] +name = "pyobjc-framework-applicationservices" +version = "8.4.1" +requires_python = ">=3.6" +summary = "Wrappers for the framework ApplicationServices on macOS" +dependencies = [ + "pyobjc-core>=8.4.1", + "pyobjc-framework-Cocoa>=8.4.1", + "pyobjc-framework-Quartz>=8.4.1", +] + +[[package]] +name = "pyobjc-framework-cocoa" +version = "8.4.1" +requires_python = ">=3.6" +summary = "Wrappers for the Cocoa frameworks on macOS" +dependencies = [ + "pyobjc-core>=8.4.1", +] + +[[package]] +name = "pyobjc-framework-quartz" +version = "8.4.1" +requires_python = ">=3.6" +summary = "Wrappers for the Quartz frameworks on macOS" +dependencies = [ + "pyobjc-core>=8.4.1", + "pyobjc-framework-Cocoa>=8.4.1", +] + [[package]] name = "pyparsing" version = "3.0.7" requires_python = ">=3.6" summary = "Python parsing module" +[[package]] +name = "python-xlib" +version = "0.31" +summary = "Python X Library" +dependencies = [ + "six>=1.10.0", +] + [[package]] name = "realcugan-ncnn-vulkan-python" version = "1.0.0" @@ -176,6 +237,12 @@ dependencies = [ "tomli>=1.0.0", ] +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" + [[package]] name = "smmap" version = "5.0.0" @@ -222,7 +289,7 @@ summary = "A small Python utility to set file creation time on Windows" [metadata] lock_version = "3.1" -content_hash = "sha256:248e178bd4147998c85a6996a6dfc8738f2f697b1f84ca620c581ee0dcf24203" +content_hash = "sha256:f72fb9417f87c737f6a94710af1039611dc2d723d056e2479172fc6b695cab7c" [metadata.files] "cmake 3.22.3" = [ @@ -255,6 +322,9 @@ content_hash = "sha256:248e178bd4147998c85a6996a6dfc8738f2f697b1f84ca620c581ee0d {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] +"evdev 1.4.0" = [ + {file = "evdev-1.4.0.tar.gz", hash = "sha256:8782740eb1a86b187334c07feb5127d3faa0b236e113206dfe3ae8f77fb1aaf1"}, +] "ffmpeg-python 0.2.0" = [ {file = "ffmpeg_python-0.2.0-py3-none-any.whl", hash = "sha256:ac441a0404e053f8b6a1113a77c0f452f1cfc62f6344a769475ffdc0f56c23c5"}, {file = "ffmpeg-python-0.2.0.tar.gz", hash = "sha256:65225db34627c578ef0e11c8b1eb528bb35e024752f6f10b78c011f6f64c4127"}, @@ -366,10 +436,54 @@ content_hash = "sha256:248e178bd4147998c85a6996a6dfc8738f2f697b1f84ca620c581ee0d {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, ] +"pynput 1.7.6" = [ + {file = "pynput-1.7.6-py2.py3-none-any.whl", hash = "sha256:19861b2a0c430d646489852f89500e0c9332e295f2c020e7c2775e7046aa2e2f"}, + {file = "pynput-1.7.6.tar.gz", hash = "sha256:3a5726546da54116b687785d38b1db56997ce1d28e53e8d22fc656d8b92e533c"}, +] +"pyobjc-core 8.4.1" = [ + {file = "pyobjc_core-8.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9a89cac910fbd64728fe7ec0c7a3a7cf20959bc1d7e2f41db4d7800556e47745"}, + {file = "pyobjc_core-8.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2cf1d4348cb99fcba04fa38199a46e35263b2fe7bb66e6dfbd4f19bc2602998d"}, + {file = "pyobjc_core-8.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a130324b25c0f5f4cfe30b6a28b8e70865d6e1eee158caababb603906ef431d2"}, + {file = "pyobjc_core-8.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c31195d1a8f00da99abf79643f902d09c709dbbe9c9b6feb6b585303c57d720c"}, + {file = "pyobjc_core-8.4.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:e7fd2aefb53a96a8f688c8bb36c6ebd5446250a7251bfa6b688a045e05afc60b"}, + {file = "pyobjc_core-8.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b3191173ce268e23c84d84f036fc94c3a8749a6726fc7fe73baf27dbac14f7d0"}, + {file = "pyobjc-core-8.4.1.tar.gz", hash = "sha256:df98669e957adb33566d9ef46773a5ac876a81afe8849c282d6a80448e35dd74"}, +] +"pyobjc-framework-applicationservices 8.4.1" = [ + {file = "pyobjc_framework_ApplicationServices-8.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:362d178c624a617fc60c3a35397550193d82da9a59272b215cf1dc6fb68c011c"}, + {file = "pyobjc_framework_ApplicationServices-8.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:41f0f6be6d343f91de92cab053be4a983e3936b364ca267a94c6e06bc34ff7fe"}, + {file = "pyobjc_framework_ApplicationServices-8.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:73287b758a70a037b6e74a60036f4adb1677407dfeaddee94102074d92539e6e"}, + {file = "pyobjc_framework_ApplicationServices-8.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:287b541b66c5c931a11dde90d7be69ddd2e5c3624c2e980e679f5242ec3ee0da"}, + {file = "pyobjc_framework_ApplicationServices-8.4.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:780fbe2e3b02237f79e2f5780094dd95b7308ecdb265c062b78a507b9564eb4d"}, + {file = "pyobjc_framework_ApplicationServices-8.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:092c4835e73e5dab1f3c498511b28b2e96503671fc7c1bc25f732c5e687b7214"}, + {file = "pyobjc-framework-ApplicationServices-8.4.1.tar.gz", hash = "sha256:b058466266412d2bf24b0303785c91538961367f26db616bf2f9f45c498a83a3"}, +] +"pyobjc-framework-cocoa 8.4.1" = [ + {file = "pyobjc_framework_Cocoa-8.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cfbe038a0108ae1b45f8f7067af70af5811b8352d2dc3d86a7bcb4484ff5d56e"}, + {file = "pyobjc_framework_Cocoa-8.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:118225562064d991bafb41d0913899d6b3d723984d1888cb7181e4dba63b22c2"}, + {file = "pyobjc_framework_Cocoa-8.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d117a1eb24fd317e9f63792ac6a8703ed899de5d42e8a861c7bf885625668c31"}, + {file = "pyobjc_framework_Cocoa-8.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3af8577acbd6b980d3b9270ec99bc0164f66ef8397351a72fcdee527f23c1a34"}, + {file = "pyobjc_framework_Cocoa-8.4.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:91fdc964acb4dee4d37ae81fb603d48397739dbbfcc1eadbe0cdafaa8144b6e6"}, + {file = "pyobjc_framework_Cocoa-8.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:197dd28668e786b55d7d4139afd85c285f780564ebbccc166e84a24be31de34f"}, + {file = "pyobjc-framework-Cocoa-8.4.1.tar.gz", hash = "sha256:dc596bac0f5d424f67944e95b2d0d7c94a07c4166359d7b4a4d4ae4f8e112822"}, +] +"pyobjc-framework-quartz 8.4.1" = [ + {file = "pyobjc_framework_Quartz-8.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a82d9eab3b2a6fca46602465f731815026d06e4fd0ba65b5b5211f1a0472b861"}, + {file = "pyobjc_framework_Quartz-8.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1bd0a506385141b81330464b6e2537a7056cdca56deb98222b6926a04f72a3e6"}, + {file = "pyobjc_framework_Quartz-8.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:64cc616386b7a387dce53d3adb8c12a8461c2720861ab36aeeb53768cd0ba7e4"}, + {file = "pyobjc_framework_Quartz-8.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a2dff998c4d1286998390f58a3131b99bdc9dbf5c3d881562b33f168098bbe2e"}, + {file = "pyobjc_framework_Quartz-8.4.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:a98c58e1b265d5b413f5ecc6ec186b9e305fb6e37909d8b4b97fd681176f5f1c"}, + {file = "pyobjc_framework_Quartz-8.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:39f55426ef883cbe12a53969f90886f71e2a94513c98cf3730efdf404cdc5c83"}, + {file = "pyobjc-framework-Quartz-8.4.1.tar.gz", hash = "sha256:f8acebf0b526f0687e79ea6946e8f2528ced4ef02d0ed3fbf7116124b2749e52"}, +] "pyparsing 3.0.7" = [ {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, ] +"python-xlib 0.31" = [ + {file = "python_xlib-0.31-py2.py3-none-any.whl", hash = "sha256:1ec6ce0de73d9e6592ead666779a5732b384e5b8fb1f1886bd0a81cafa477759"}, + {file = "python-xlib-0.31.tar.gz", hash = "sha256:74d83a081f532bc07f6d7afcd6416ec38403d68f68b9b9dc9e1f28fbf2d799e9"}, +] "realcugan-ncnn-vulkan-python 1.0.0" = [ {file = "realcugan-ncnn-vulkan-python-1.0.0.tar.gz", hash = "sha256:f72764ffe0a0c53b13cdbe1ae328ff9b0eb5582b9d5d15a0ca0c0ac254d17410"}, ] @@ -391,6 +505,10 @@ content_hash = "sha256:248e178bd4147998c85a6996a6dfc8738f2f697b1f84ca620c581ee0d {file = "setuptools_scm-6.4.2-py3-none-any.whl", hash = "sha256:acea13255093849de7ccb11af9e1fb8bde7067783450cee9ef7a93139bddf6d4"}, {file = "setuptools_scm-6.4.2.tar.gz", hash = "sha256:6833ac65c6ed9711a4d5d2266f8024cfa07c533a0e55f4c12f6eff280a5a9e30"}, ] +"six 1.16.0" = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] "smmap 5.0.0" = [ {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, diff --git a/pyproject.toml b/pyproject.toml index 7c7691e..a8bc10e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ dependencies = [ "realsr-ncnn-vulkan-python>=1.0.4", "rife-ncnn-vulkan-python>=1.1.2.post3", "realcugan-ncnn-vulkan-python>=1.0.0", + "pynput>=1.7.6", ] dynamic = ["version"] diff --git a/video2x/decoder.py b/video2x/decoder.py index fc9a991..56192f7 100755 --- a/video2x/decoder.py +++ b/video2x/decoder.py @@ -19,7 +19,7 @@ along with this program. If not, see . Name: Video Decoder Author: K4YT3X Date Created: June 17, 2021 -Last Modified: March 20, 2022 +Last Modified: March 21, 2022 """ import contextlib @@ -83,6 +83,7 @@ class VideoDecoder(threading.Thread): .output("pipe:1", format="rawvideo", pix_fmt="rgb24", vsync="cfr") .global_args("-hide_banner") .global_args("-nostats") + .global_args("-nostdin") .global_args( "-loglevel", LOGURU_FFMPEG_LOGLEVELS.get( @@ -92,6 +93,7 @@ class VideoDecoder(threading.Thread): overwrite_output=True, ), env={"AV_LOG_FORCE_COLOR": "TRUE"}, + stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) diff --git a/video2x/video2x.py b/video2x/video2x.py index c76a545..979bc4e 100755 --- a/video2x/video2x.py +++ b/video2x/video2x.py @@ -27,7 +27,7 @@ __ __ _ _ ___ __ __ Name: Video2X Creator: K4YT3X Date Created: February 24, 2018 -Last Modified: March 20, 2022 +Last Modified: March 21, 2022 Editor: BrianPetkovsek Last Modified: June 17, 2019 @@ -48,11 +48,9 @@ import pathlib import signal import sys import time -from typing import Union import cv2 import ffmpeg -import pynput from loguru import logger from rich import print from rich.console import Console @@ -73,6 +71,15 @@ from .encoder import VideoEncoder from .interpolator import Interpolator from .upscaler import Upscaler +# for desktop environments only +# if pynput can be loaded, enable global pause hotkey support +try: + import pynput +except ImportError: + ENABLE_HOTKEY = False +else: + ENABLE_HOTKEY = True + LEGAL_INFO = """Video2X\t\t{} Author:\t\tK4YT3X License:\tGNU AGPL v3 @@ -255,6 +262,7 @@ class Video2X: "<", TimeRemainingColumn(), console=console, + speed_estimate_period=300.0, disable=True, ) @@ -264,21 +272,26 @@ class Video2X: # allow sending SIGUSR1 to pause/resume processing signal.signal(signal.SIGUSR1, self._toggle_pause) - # create global pause hotkey - pause_hotkey = pynput.keyboard.HotKey( - pynput.keyboard.HotKey.parse("++v"), self._toggle_pause - ) + # enable global pause hotkey if it's supported + if ENABLE_HOTKEY is True: - # create global keyboard input listener - keyboard_listener = pynput.keyboard.Listener( - on_press=(lambda key: pause_hotkey.press(keyboard_listener.canonical(key))), - on_release=( - lambda key: pause_hotkey.release(keyboard_listener.canonical(key)) - ), - ) + # create global pause hotkey + pause_hotkey = pynput.keyboard.HotKey( + pynput.keyboard.HotKey.parse("++v"), self._toggle_pause + ) - # start monitoring global key presses - keyboard_listener.start() + # create global keyboard input listener + keyboard_listener = pynput.keyboard.Listener( + on_press=( + lambda key: pause_hotkey.press(keyboard_listener.canonical(key)) + ), + on_release=( + lambda key: pause_hotkey.release(keyboard_listener.canonical(key)) + ), + ) + + # start monitoring global key presses + keyboard_listener.start() # a temporary variable that stores the exception exception = [] @@ -329,9 +342,11 @@ class Video2X: exception.append(e) finally: + # stop keyboard listener - keyboard_listener.stop() - keyboard_listener.join() + if ENABLE_HOTKEY is True: + keyboard_listener.stop() + keyboard_listener.join() # stop progress display self.progress.stop()