Add Avisynth video encoding workflow
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user