Files
media-batch-tools/tools/avisynth_runner.py
T

111 lines
3.5 KiB
Python

from __future__ import annotations
import os
import sys
from dataclasses import dataclass
from pathlib import Path
@dataclass(frozen=True)
class AvisynthRunnerSet:
default: Path
runner64: Path | None = None
runner32: Path | None = None
def for_script(self, script: Path) -> Path:
if sys.platform != "win32":
return self.default
if script_requests_32bit(script):
if self.runner32 is None:
raise RuntimeError(
f"{script} requests 32-bit AviSynth with a trailing #32bit "
"marker, but no 32-bit runner was found. Set "
"MBT_AVS_RUNNER32 or provide tools/avisynth_runner/"
"mbt_avs_runner32.exe."
)
return self.runner32
return self.default
def bundled_runner_path(name: str | None = None) -> Path | None:
if name is None:
name = "mbt_avs_runner.exe" if sys.platform == "win32" else "mbt_avs_runner"
runner = _runner_dir() / name
return runner if runner.is_file() else None
def runner_path_from_env_or_bundled() -> Path | None:
runners = runner_set_from_env_or_bundled()
return runners.default if runners is not None else None
def runner_set_from_env_or_bundled() -> AvisynthRunnerSet | None:
if sys.platform == "win32":
return _windows_runner_set_from_env_or_bundled()
env_path = os.environ.get("MBT_AVS_RUNNER", "").strip()
if env_path:
runner = validate_runner_path(env_path)
return AvisynthRunnerSet(default=runner, runner64=runner)
bundled = bundled_runner_path()
if bundled is not None:
return AvisynthRunnerSet(default=bundled, runner64=bundled)
return None
def _windows_runner_set_from_env_or_bundled() -> AvisynthRunnerSet | None:
runner64 = _optional_runner_from_env("MBT_AVS_RUNNER64")
if runner64 is None:
runner64 = _optional_runner_from_env("MBT_AVS_RUNNER")
if runner64 is None:
runner64 = bundled_runner_path("mbt_avs_runner.exe")
runner32 = _optional_runner_from_env("MBT_AVS_RUNNER32")
if runner32 is None:
runner32 = bundled_runner_path("mbt_avs_runner32.exe")
default = runner64 or runner32
if default is None:
return None
return AvisynthRunnerSet(default=default, runner64=runner64, runner32=runner32)
def _optional_runner_from_env(name: str) -> Path | None:
raw = os.environ.get(name, "").strip()
if not raw:
return None
return validate_runner_path(raw)
def validate_runner_path(raw_path: str) -> Path:
runner_path = Path(raw_path.strip().strip('"')).expanduser()
for candidate in _runner_candidates(runner_path):
if candidate.is_file():
return candidate.resolve()
raise RuntimeError(
f"Avisynth runner file not found: {runner_path}. "
"Use the full path, set MBT_AVS_RUNNER, or leave blank to use the "
"bundled runner when present."
)
def _runner_candidates(path: Path) -> list[Path]:
candidates = [path]
if len(path.parts) == 1:
candidates.append(_runner_dir() / path.name)
return candidates
def _runner_dir() -> Path:
return Path(__file__).resolve().parent / "avisynth_runner"
def script_requests_32bit(script: Path) -> bool:
lines = script.read_text(encoding="utf-8").splitlines()
while lines and not lines[-1].strip():
lines.pop()
if not lines:
return False
normalized = "".join(lines[-1].split()).lower()
return normalized in {"#32bit", "#32-bit"}