111 lines
3.5 KiB
Python
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"}
|