Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions helpers/_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Shared helpers for command-line entry points."""

from __future__ import annotations

import sys


def configure_stdout() -> None:
"""Keep progress output safe when the locale encoding is not UTF-8."""
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
6 changes: 6 additions & 0 deletions helpers/grade.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
import tempfile
from pathlib import Path

try:
from _util import configure_stdout
except ModuleNotFoundError:
from helpers._util import configure_stdout


PRESETS: dict[str, str] = {
# Subtle baseline — barely perceptible cleanup. No color shift.
Expand Down Expand Up @@ -292,6 +297,7 @@ def apply_grade(input_path: Path, output_path: Path, filter_string: str) -> None


def main() -> None:
configure_stdout()
ap = argparse.ArgumentParser(description="Apply a color grade via ffmpeg filter chain")
ap.add_argument("input", type=Path, nargs="?", help="Input video")
ap.add_argument("-o", "--output", type=Path, help="Output video")
Expand Down
6 changes: 6 additions & 0 deletions helpers/pack_transcripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
import sys
from pathlib import Path

try:
from _util import configure_stdout
except ModuleNotFoundError:
from helpers._util import configure_stdout


def format_time(seconds: float) -> str:
"""Format a time in seconds as "NNN.NN" with fixed 6-char width for alignment."""
Expand Down Expand Up @@ -163,6 +168,7 @@ def render_markdown(entries: list[tuple[str, float, list[dict]]], silence_thresh


def main() -> None:
configure_stdout()
ap = argparse.ArgumentParser(description="Pack Scribe transcripts into takes_packed.md")
ap.add_argument("--edit-dir", type=Path, required=True, help="Edit directory containing transcripts/")
ap.add_argument(
Expand Down
7 changes: 7 additions & 0 deletions helpers/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
from fractions import Fraction
from pathlib import Path

try:
from _util import configure_stdout
except ModuleNotFoundError:
from helpers._util import configure_stdout


try:
from grade import get_preset, auto_grade_for_clip # same directory
except Exception:
Expand Down Expand Up @@ -677,6 +683,7 @@ def build_final_composite(


def main() -> None:
configure_stdout()
ap = argparse.ArgumentParser(description="Render a video from an EDL")
ap.add_argument("edl", type=Path, help="Path to edl.json")
ap.add_argument("-o", "--output", type=Path, required=True, help="Output video path")
Expand Down
5 changes: 5 additions & 0 deletions helpers/transcribe_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path

try:
from _util import configure_stdout
except ModuleNotFoundError:
from helpers._util import configure_stdout
from transcribe import load_api_key, transcribe_one, transcript_path


Expand All @@ -35,6 +39,7 @@ def find_videos(videos_dir: Path) -> list[Path]:


def main() -> None:
configure_stdout()
ap = argparse.ArgumentParser(description="Parallel batch transcription of a videos directory")
ap.add_argument("videos_dir", type=Path, help="Directory containing source videos")
ap.add_argument(
Expand Down
44 changes: 44 additions & 0 deletions tests/test_stdout_encoding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import os
import subprocess
import sys
import unittest
from pathlib import Path


REPO_ROOT = Path(__file__).parents[1]
HELPERS = (
"pack_transcripts.py",
"render.py",
"grade.py",
"transcribe_batch.py",
)


class StdoutEncodingTests(unittest.TestCase):
def test_helpers_handle_ascii_redirected_stdout(self):
probe = (
"import atexit, runpy, sys; "
"helper = sys.argv[1]; "
"atexit.register(lambda: print('→')); "
"sys.argv = [helper, '--help']; "
"runpy.run_path(helper, run_name='__main__')"
)
for helper in HELPERS:
with self.subTest(helper=helper):
env = os.environ.copy()
env["PYTHONIOENCODING"] = "ascii"
result = subprocess.run(
[sys.executable, "-c", probe, helper],
cwd=REPO_ROOT / "helpers",
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)

self.assertEqual(result.returncode, 0, result.stderr.decode("utf-8"))
self.assertTrue(result.stdout.decode("utf-8").endswith("→\n"))


if __name__ == "__main__":
unittest.main()