Propagate .mvlog-id into worker state and exclude from fingerprints

This commit is contained in:
hbrain 2026-05-31 14:32:40 +00:00
parent 765daa150c
commit 2ae7c75f91

View file

@ -12,6 +12,7 @@ import logging
import hashlib import hashlib
import json import json
import os import os
import re
import shutil import shutil
import subprocess import subprocess
import sys import sys
@ -28,6 +29,7 @@ LOCK_NAME = ".movmaker-lock"
ENABLED_NAME = ".movmaker-enabled" ENABLED_NAME = ".movmaker-enabled"
PREVIEW_NAME = ".movmaker-preview" PREVIEW_NAME = ".movmaker-preview"
HIDDEN_NAME = ".mvlog-hidden" HIDDEN_NAME = ".mvlog-hidden"
ARTICLE_ID_NAME = ".mvlog-id"
INTRO_LOGO_ALPHA = "0.5" INTRO_LOGO_ALPHA = "0.5"
MVLOG_WORKER_RECOMMENDED_INTERVAL_MINUTES = 5 MVLOG_WORKER_RECOMMENDED_INTERVAL_MINUTES = 5
@ -60,6 +62,23 @@ def remote_file_exists(host: str | None, path: str) -> bool:
return ssh(host, f"test -f {remote_q(path)}", check=False).returncode == 0 return ssh(host, f"test -f {remote_q(path)}", check=False).returncode == 0
def read_remote_text(host: str | None, path: str) -> str:
if host is None:
try:
return Path(path).read_text(encoding="utf-8")
except Exception:
return ""
res = ssh(host, f"cat {remote_q(path)} 2>/dev/null", check=False)
return res.stdout if res.returncode == 0 else ""
def read_article_id(host: str | None, job_dir: str) -> str | None:
raw = read_remote_text(host, f"{job_dir}/{ARTICLE_ID_NAME}").strip().lower()
if re.fullmatch(r"\d{14}[a-f0-9]{16}", raw):
return raw
return None
def acquire_lock(host: str, job_dir: str) -> bool: def acquire_lock(host: str, job_dir: str) -> bool:
lock = f"{job_dir}/{LOCK_NAME}" lock = f"{job_dir}/{LOCK_NAME}"
cmd = f"mkdir {remote_q(lock)} 2>/dev/null && date -Iseconds > {remote_q(lock + '/started_at')}" cmd = f"mkdir {remote_q(lock)} 2>/dev/null && date -Iseconds > {remote_q(lock + '/started_at')}"
@ -78,7 +97,7 @@ entries = []
for dirpath, dirnames, filenames in os.walk(root): for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d != '.movmaker-lock'] dirnames[:] = [d for d in dirnames if d != '.movmaker-lock']
for name in filenames: for name in filenames:
if name in {'.movmaker-state.json', '.movmaker-enabled', '.movmaker-preview', '.mvlog-hidden'}: if name in {'.movmaker-state.json', '.movmaker-enabled', '.movmaker-preview', '.mvlog-hidden', '.mvlog-id'}:
continue continue
path = os.path.join(dirpath, name) path = os.path.join(dirpath, name)
rel = os.path.relpath(path, root) rel = os.path.relpath(path, root)
@ -253,6 +272,7 @@ def render_job(local_input: Path, local_out: Path, extra_args: list[str]) -> Pat
def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_args: list[str], force: bool) -> None: def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_args: list[str], force: bool) -> None:
job_dir = f"{remote_root}/in-dir/{job}" job_dir = f"{remote_root}/in-dir/{job}"
article_id = read_article_id(host, job_dir)
state = read_state(host, job_dir) state = read_state(host, job_dir)
full_requested = remote_file_exists(host, f"{job_dir}/{ENABLED_NAME}") full_requested = remote_file_exists(host, f"{job_dir}/{ENABLED_NAME}")
preview_requested = remote_file_exists(host, f"{job_dir}/{PREVIEW_NAME}") preview_requested = remote_file_exists(host, f"{job_dir}/{PREVIEW_NAME}")
@ -284,6 +304,8 @@ def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_arg
"mode": "preview" if preview_mode else "full", "mode": "preview" if preview_mode else "full",
"started_at": now(), "started_at": now(),
}) })
if article_id:
processing_state["article_id"] = article_id
if preview_mode: if preview_mode:
processing_state["preview_fingerprint"] = fingerprint_before processing_state["preview_fingerprint"] = fingerprint_before
else: else:
@ -313,6 +335,8 @@ def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_arg
"updated_at": now(), "updated_at": now(),
"message": "Input changed during render; will regenerate on next run.", "message": "Input changed during render; will regenerate on next run.",
}) })
if article_id:
stale_state["article_id"] = article_id
if preview_mode: if preview_mode:
stale_state["preview_fingerprint"] = fingerprint_after stale_state["preview_fingerprint"] = fingerprint_after
stale_state["previous_preview_fingerprint"] = fingerprint_before stale_state["previous_preview_fingerprint"] = fingerprint_before
@ -341,6 +365,8 @@ def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_arg
"preview_output": preview_output, "preview_output": preview_output,
"preview_generated_at": now(), "preview_generated_at": now(),
}) })
if article_id:
new_state["article_id"] = article_id
write_state(host, job_dir, new_state) write_state(host, job_dir, new_state)
print(f"published preview: {job} -> {preview_output}") print(f"published preview: {job} -> {preview_output}")
@ -365,6 +391,8 @@ def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_arg
"output": output, "output": output,
"generated_at": now(), "generated_at": now(),
}) })
if article_id:
new_state["article_id"] = article_id
write_state(host, job_dir, new_state) write_state(host, job_dir, new_state)
print(f"published: {job} -> {output}") print(f"published: {job} -> {output}")
@ -376,11 +404,14 @@ def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_arg
return return
except Exception as exc: except Exception as exc:
try: try:
write_state(host, job_dir, { error_state = {
"status": "error", "status": "error",
"updated_at": now(), "updated_at": now(),
"message": str(exc), "message": str(exc),
}) }
if article_id:
error_state["article_id"] = article_id
write_state(host, job_dir, error_state)
finally: finally:
raise raise
finally: finally: