From 205af96c19cc12dc68ceb50d1b60f00e94ffaac9 Mon Sep 17 00:00:00 2001 From: bradautomates Date: Sat, 9 May 2026 08:21:39 +1000 Subject: [PATCH] Read/write config files as UTF-8 on Windows (#4) Path.read_text()/write_text() default to the platform encoding (cp1252 on Windows), which mangles or crashes on UTF-8 files. yt-dlp's video.info.json is UTF-8 and routinely contains non-ASCII bytes, so on Windows the parse raised UnicodeDecodeError, was swallowed by a bare except, and the /watch report came back missing Title/Uploader. - download.py: read video.info.json as UTF-8; log parse failures to stderr instead of swallowing them silently. - whisper.py, setup.py: same UTF-8 fix on .env reads and writes for symmetry across platforms. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/download.py | 5 +++-- scripts/setup.py | 10 +++++----- scripts/whisper.py | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/download.py b/scripts/download.py index 589123c..afa1ef4 100755 --- a/scripts/download.py +++ b/scripts/download.py @@ -98,14 +98,15 @@ def download_url(url: str, out_dir: Path) -> dict: info: dict = {} if info_path.exists(): try: - raw = json.loads(info_path.read_text()) + raw = json.loads(info_path.read_text(encoding="utf-8")) info = { "title": raw.get("title"), "uploader": raw.get("uploader") or raw.get("channel"), "duration": raw.get("duration"), "url": raw.get("webpage_url") or url, } - except Exception: + except Exception as exc: + print(f"[watch] info.json parse failed: {exc}", file=sys.stderr) info = {"url": url} return { diff --git a/scripts/setup.py b/scripts/setup.py index 308f6b3..b82d4c1 100755 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -79,7 +79,7 @@ def _read_env_key(name: str) -> str | None: return None _check_file_permissions(CONFIG_FILE) try: - for line in CONFIG_FILE.read_text().splitlines(): + for line in CONFIG_FILE.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue @@ -113,7 +113,7 @@ def _scaffold_env() -> bool: if CONFIG_FILE.exists(): return False CONFIG_DIR.mkdir(parents=True, exist_ok=True) - CONFIG_FILE.write_text(ENV_TEMPLATE) + CONFIG_FILE.write_text(ENV_TEMPLATE, encoding="utf-8") try: CONFIG_FILE.chmod(0o600) except OSError: @@ -130,15 +130,15 @@ def _write_setup_complete() -> None: CONFIG_DIR.mkdir(parents=True, exist_ok=True) existing = "" if CONFIG_FILE.exists(): - existing = CONFIG_FILE.read_text() + existing = CONFIG_FILE.read_text(encoding="utf-8") for line in existing.splitlines(): if line.strip().startswith("SETUP_COMPLETE="): return if existing and not existing.endswith("\n"): existing += "\n" - CONFIG_FILE.write_text(existing + "SETUP_COMPLETE=true\n") + CONFIG_FILE.write_text(existing + "SETUP_COMPLETE=true\n", encoding="utf-8") else: - CONFIG_FILE.write_text(ENV_TEMPLATE + "\nSETUP_COMPLETE=true\n") + CONFIG_FILE.write_text(ENV_TEMPLATE + "\nSETUP_COMPLETE=true\n", encoding="utf-8") try: CONFIG_FILE.chmod(0o600) except OSError: diff --git a/scripts/whisper.py b/scripts/whisper.py index 318b449..bbdcc8f 100755 --- a/scripts/whisper.py +++ b/scripts/whisper.py @@ -45,7 +45,7 @@ def load_api_key(preferred: str | None = None) -> tuple[str, str] | tuple[None, if not path.exists(): return None try: - for line in path.read_text().splitlines(): + for line in path.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue