From fa70ddfb6533ce9f86d75e4eb3aecd16cfdaab4d Mon Sep 17 00:00:00 2001 From: "Alex H." <48185123+Vodes@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:34:07 +0100 Subject: [PATCH] CLI: Allow multiple files for directly passed video files --- vspreview/init.py | 113 +++++++++++++------ vspreview/plugins/builtins/vssource_load.ppy | 31 +++-- 2 files changed, 101 insertions(+), 43 deletions(-) diff --git a/vspreview/init.py b/vspreview/init.py index d56c37d9..dae188a4 100644 --- a/vspreview/init.py +++ b/vspreview/init.py @@ -13,26 +13,32 @@ from PyQt6.QtWidgets import QApplication from .core.logger import set_log_level, setup_logger + # import vsenv as early as possible: # This is so other modules cannot accidentally use and lock us into a different policy. from .core.vsenv import set_vsengine_loop from .main import MainWindow from .plugins import get_installed_plugins from .plugins.abstract import FileResolverPlugin, ResolvedScript -from .plugins.install import install_plugins, plugins_commands, print_available_plugins, uninstall_plugins +from .plugins.install import ( + install_plugins, + plugins_commands, + print_available_plugins, + uninstall_plugins, +) -__all__ = [ - 'main' -] +__all__ = ["main"] -def get_resolved_script(filepath: Path) -> tuple[ResolvedScript, FileResolverPlugin | None] | int: +def get_resolved_script( + filepath: Path, +) -> tuple[ResolvedScript, FileResolverPlugin | None] | int: for plugin in get_installed_plugins(FileResolverPlugin, False).values(): if plugin.can_run_file(filepath): return plugin.resolve_path(filepath), plugin if not filepath.exists(): - logging.error('Script or file path is invalid.') + logging.error("Script or file path is invalid.") return 1 return ResolvedScript(filepath, str(filepath)), None @@ -41,37 +47,53 @@ def get_resolved_script(filepath: Path) -> tuple[ResolvedScript, FileResolverPlu def main(_args: Sequence[str] | None = None, no_exit: bool = False) -> int: from .utils import exit_func - parser = ArgumentParser(prog='VSPreview') + parser = ArgumentParser(prog="VSPreview") parser.add_argument( - 'script_path_or_command', type=str, nargs='?', - help=f'Path to Vapoursynth script or plugins command {",".join(plugins_commands)}' + "script_path_or_command", + type=str, + nargs="?", + help=f'Path to Vapoursynth script, video file(s) or plugins command {", ".join(plugins_commands)}', ) parser.add_argument( - 'plugins', type=str, nargs='*', - help=f'Plugins to {"/".join(plugins_commands[:-1])} or arguments to pass to the script environment.' + "plugins", + type=str, + nargs="*", + help=f'Plugins to {"/".join(plugins_commands[:-1])} or arguments to pass to the script environment.', ) + parser.add_argument("--version", "-v", action="version", version="%(prog)s 0.2b") parser.add_argument( - '--version', '-v', action='version', version='%(prog)s 0.2b' + "--preserve-cwd", + "-c", + action="store_true", + help="do not chdir to script parent directory", ) parser.add_argument( - '--preserve-cwd', '-c', action='store_true', help='do not chdir to script parent directory' + "-f", "--frame", type=int, help="Frame to load initially (defaults to 0)" ) - parser.add_argument('-f', '--frame', type=int, help='Frame to load initially (defaults to 0)') parser.add_argument( - '--vscode-setup', type=str, choices=['override', 'append', 'ignore'], nargs='?', const='append', - help='Installs launch settings in cwd\'s .vscode' + "--vscode-setup", + type=str, + choices=["override", "append", "ignore"], + nargs="?", + const="append", + help="Installs launch settings in cwd's .vscode", ) parser.add_argument( "--verbose", help="Set the logging to verbose.", action="store_true" ) parser.add_argument( - "--force", help="Force the install of a plugin even if it exists already.", action="store_true" + "--force", + help="Force the install of a plugin even if it exists already.", + action="store_true", ) parser.add_argument( "--no-deps", help="Ignore downloading dependencies.", action="store_true" ) parser.add_argument( - "--force-storage", help="Force override or local/global storage.", action="store_true", default=False + "--force-storage", + help="Force override or local/global storage.", + action="store_true", + default=False, ) args = parser.parse_args(_args) @@ -80,6 +102,7 @@ def main(_args: Sequence[str] | None = None, no_exit: bool = False) -> int: if args.verbose: from vstools import VSDebug + set_log_level(logging.DEBUG, logging.DEBUG) VSDebug(use_logging=True) else: @@ -94,32 +117,34 @@ def main(_args: Sequence[str] | None = None, no_exit: bool = False) -> int: script_path_or_command = args.script_path_or_command - if not script_path_or_command and not (args.plugins and (script_path_or_command := next(iter(args.plugins)))): - logging.error('Script path required.') + if not script_path_or_command and not ( + args.plugins and (script_path_or_command := next(iter(args.plugins))) + ): + logging.error("Script/Video path required.") return exit_func(1, no_exit) - if script_path_or_command.startswith('--') and args.plugins: + if script_path_or_command.startswith("--") and args.plugins: script_path_or_command = args.plugins.pop() args.plugins = [args.script_path_or_command, *args.plugins] if (command := script_path_or_command) in plugins_commands: - if command == 'available': + if command == "available": print_available_plugins() return exit_func(0, no_exit) if not args.plugins: - logging.error('You must provide at least one plugin!') + logging.error("You must provide at least one plugin!") return exit_func(1, no_exit) set_log_level(logging.INFO) plugins = list(args.plugins) - if command == 'install': + if command == "install": install_plugins(plugins, args.force, args.no_deps) - elif command == 'uninstall': + elif command == "uninstall": uninstall_plugins(plugins) - elif command == 'update': + elif command == "update": uninstall_plugins(plugins, True) install_plugins(plugins, True, args.no_deps) @@ -132,16 +157,24 @@ def main(_args: Sequence[str] | None = None, no_exit: bool = False) -> int: script, file_resolve_plugin = script_or_err + if ( + file_resolve_plugin + and hasattr(file_resolve_plugin, "_config") + and file_resolve_plugin._config.namespace == "dev.setsugen.vssource_load" + ): + setattr(args, "preserve_cwd", True) + if not args.preserve_cwd: os.chdir(script.path.parent) - first_run = not hasattr(main, 'app') + first_run = not hasattr(main, "app") if first_run: main.app = QApplication(sys.argv) set_vsengine_loop() else: from .core.vsenv import get_current_environment, make_environment + make_environment() get_current_environment().use() @@ -151,7 +184,7 @@ def main(_args: Sequence[str] | None = None, no_exit: bool = False) -> int: def _parse_arg(kv: str) -> tuple[str, str | int | float]: v: str | int | float - k, v = kv.split('=', maxsplit=1) + k, v = kv.split("=", maxsplit=1) try: v = int(v) @@ -161,16 +194,30 @@ def _parse_arg(kv: str) -> tuple[str, str | int | float]: except ValueError: ... - return k.strip('--'), v + return k.strip("--"), v if args.plugins: - arguments |= {k: v for k, v in map(_parse_arg, args.plugins)} + if file_resolve_plugin._config.namespace == "dev.setsugen.vssource_load": + additional_files = list[Path]( + Path(filepath).resolve() for filepath in args.plugins + ) + arguments.update(additional_files=additional_files) + else: + arguments |= {k: v for k, v in map(_parse_arg, args.plugins)} main.main_window = MainWindow( - Path(os.getcwd()) if args.preserve_cwd else script.path.parent, no_exit, script.reload_enabled, args.force_storage + Path(os.getcwd()) if args.preserve_cwd else script.path.parent, + no_exit, + script.reload_enabled, + args.force_storage, ) main.main_window.load_script( - script.path, list(arguments.items()), False, args.frame or None, script.display_name, file_resolve_plugin + script.path, + list(arguments.items()), + False, + args.frame or None, + script.display_name, + file_resolve_plugin, ) ret_code = main.app.exec() @@ -185,5 +232,5 @@ def _parse_arg(kv: str) -> tuple[str, str | int | float]: return exit_func(ret_code, no_exit) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/vspreview/plugins/builtins/vssource_load.ppy b/vspreview/plugins/builtins/vssource_load.ppy index 24043167..e324bea4 100644 --- a/vspreview/plugins/builtins/vssource_load.ppy +++ b/vspreview/plugins/builtins/vssource_load.ppy @@ -3,15 +3,19 @@ from __future__ import annotations from pathlib import Path from typing import TYPE_CHECKING -if __name__ != '__vspreview__': - from vspreview.plugins import FileResolvePluginConfig, FileResolverPlugin, ResolvedScript +if __name__ != "__vspreview__": + from vspreview.plugins import ( + FileResolvePluginConfig, + FileResolverPlugin, + ResolvedScript, + ) - __all__ = [ - 'FileResolveWithVSSource' - ] + __all__ = ["FileResolveWithVSSource"] class FileResolveWithVSSource(FileResolverPlugin): - _config = FileResolvePluginConfig('dev.setsugen.vssource_load', 'VSSource Loader') + _config = FileResolvePluginConfig( + "dev.setsugen.vssource_load", "VSSource Loader" + ) def can_run_file(self, filepath: Path) -> bool: if filepath.is_dir(): @@ -30,17 +34,20 @@ if __name__ != '__vspreview__': return False def resolve_path(self, filepath: Path) -> ResolvedScript: - return ResolvedScript(Path(__file__), str(filepath), dict(filepath=filepath), False) + return ResolvedScript( + Path(__file__), str(filepath), dict(filepath=filepath), False + ) else: from vssource import parse_video_filepath, source from vstools import set_output if TYPE_CHECKING: filepath: Path = Path() - pattern: str = '' + pattern: str = "" + additional_files: list[Path] | None = None else: - if 'pattern' not in locals(): - pattern = '*' + if "pattern" not in locals(): + pattern = "*" if filepath.is_dir(): for file in filepath.glob(pattern): @@ -51,3 +58,7 @@ else: ... else: set_output(source(filepath), filepath.name) + + if "additional_files" in locals(): + for file in additional_files: + set_output(source(file), file.name)