Add Avisynth video encoding workflow

This commit is contained in:
ajp_anton
2026-07-21 00:41:00 +00:00
parent f50f465465
commit 49296d6f45
37 changed files with 9487 additions and 76 deletions
+111
View File
@@ -0,0 +1,111 @@
from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
VIDEO_EXTS = {".mp4", ".mov", ".mkv", ".mts", ".m2ts", ".avi"}
@dataclass(frozen=True)
class VideoInput:
path: Path
drag_relative: Path
collapsed_relative: Path
@dataclass(frozen=True)
class _CollectedVideo:
path: Path
drag_relative: Path
def _sort_key(path: Path) -> str:
return str(path).lower()
def _is_video(path: Path) -> bool:
return path.is_file() and path.suffix.lower() in VIDEO_EXTS
def _collect_from_arg(arg: str) -> list[_CollectedVideo]:
path = Path(arg).expanduser()
if not path.exists():
print(f"Skipping missing path: {arg}")
return []
resolved = path.resolve()
if _is_video(path):
return [_CollectedVideo(resolved, Path(path.name))]
if path.is_dir():
videos: list[_CollectedVideo] = []
for child in sorted(path.rglob("*"), key=_sort_key):
if _is_video(child):
relative = Path(path.name) / child.relative_to(path)
videos.append(_CollectedVideo(child.resolve(), relative))
return videos
return []
def _relative_depth(path: Path) -> int:
return len(path.parts)
def collect_video_inputs(args: list[str]) -> list[VideoInput]:
collected: dict[Path, _CollectedVideo] = {}
for arg in args:
for item in _collect_from_arg(arg):
existing = collected.get(item.path)
if existing is None:
collected[item.path] = item
continue
if _relative_depth(item.drag_relative) > _relative_depth(existing.drag_relative):
collected[item.path] = item
items = sorted(collected.values(), key=lambda item: _sort_key(item.drag_relative))
if not items:
return []
collapsed = _collapse_drag_tree(items)
return [
VideoInput(
path=item.path,
drag_relative=item.drag_relative,
collapsed_relative=collapsed[item.path],
)
for item in items
]
def _collapse_drag_tree(items: list[_CollectedVideo]) -> dict[Path, Path]:
if len(items) == 1:
return {items[0].path: Path(items[0].path.name)}
parent_names = [str(item.drag_relative.parent) for item in items]
common_parent = Path(os.path.commonpath(parent_names))
if str(common_parent) in {"", "."}:
common_parent = Path(".")
collapsed: dict[Path, Path] = {}
used: dict[Path, Path] = {}
for item in items:
if common_parent == Path("."):
relative = item.drag_relative
else:
relative = item.drag_relative.relative_to(common_parent)
conflict = used.get(relative)
if conflict is not None and conflict != item.path:
raise RuntimeError(
"Two different videos collapse to the same workspace path: "
f"{relative}"
)
used[relative] = item.path
collapsed[item.path] = relative
return collapsed