mvlog_worker: upload audio files to remote in-dir; update README

This commit is contained in:
hbrain 2026-05-29 08:05:23 +00:00
parent 0183b86031
commit a7d6a4e1ed
2 changed files with 49 additions and 2 deletions

View file

@ -164,6 +164,8 @@ The worker:
7. removes the old output if the regenerated filename changed 7. removes the old output if the regenerated filename changed
8. clears the web metadata cache 8. clears the web metadata cache
Note: The worker now also uploads audio files found in the local input directory back to the webserver's in-dir (the same job subdirectory) after rendering. It skips files that already exist on the server. Supported extensions include: .mp3, .wav, .m4a, .aac, .flac, .ogg, .opus, .wma, .aiff, .aif. When running against a remote host the worker uploads using scp (upload to a temporary filename then move into place); when run with --local it copies files into the remote path. Audio upload failures are reported as warnings and do not prevent publishing the generated MP4. After successful uploads the worker recomputes the job fingerprint and records it in the job state so subsequent runs avoid unnecessary re-rendering.
Run once: Run once:
```bash ```bash

View file

@ -185,6 +185,39 @@ def publish_output(host: str, remote_root: str, local_mp4: Path, old_output: str
return out_name return out_name
def upload_audio_files(host: str | None, job_dir: str, local_input: Path) -> list[str]:
"""Upload audio files found inside local_input to the remote job_dir/in-dir if they are missing.
Returns a list of uploaded relative paths.
"""
AUDIO_EXTS = {".mp3", ".wav", ".m4a", ".aac", ".flac", ".ogg", ".opus", ".wma", ".aiff", ".aif"}
uploaded: list[str] = []
for path in sorted(local_input.rglob("*")):
if not path.is_file():
continue
if path.suffix.lower() not in AUDIO_EXTS:
continue
rel = path.relative_to(local_input).as_posix()
remote_path = f"{job_dir}/{rel}"
if remote_file_exists(host, remote_path):
print(f"audio exists on server, skipping: {rel}")
continue
parent = str(Path(remote_path).parent)
if host is None:
Path(parent).mkdir(parents=True, exist_ok=True)
shutil.copy2(str(path), remote_path)
else:
# Ensure parent directory exists on the remote host
ssh(host, f"mkdir -p {remote_q(parent)}")
remote_tmp = f"{remote_path}.tmp.$$"
# Upload to a temporary file then move into place for (some) atomicity
subprocess.run(["scp", str(path), f"{host}:{remote_tmp}"], check=True)
ssh(host, f"mv {remote_q(remote_tmp)} {remote_q(remote_path)}")
uploaded.append(rel)
print(f"uploaded audio: {rel}")
return uploaded
def render_job(local_input: Path, local_out: Path, extra_args: list[str]) -> Path: def render_job(local_input: Path, local_out: Path, extra_args: list[str]) -> Path:
local_out.mkdir(parents=True, exist_ok=True) local_out.mkdir(parents=True, exist_ok=True)
intro_logo = INTRO_LOGO if INTRO_LOGO.exists() else None intro_logo = INTRO_LOGO if INTRO_LOGO.exists() else None
@ -293,11 +326,18 @@ def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_arg
if preview_mode: if preview_mode:
preview_output = publish_output(host, remote_root, local_mp4, old_preview_output) preview_output = publish_output(host, remote_root, local_mp4, old_preview_output)
# Upload any audio files present in the local input directory to the remote in-dir if missing.
try:
upload_audio_files(host, job_dir, local_input)
except Exception as exc: # Upload failure should not prevent publishing the preview
print(f"warning: audio upload failed: {exc}")
# Recompute fingerprint now that audio files may have been uploaded.
final_fingerprint = remote_fingerprint(host, job_dir)
new_state = dict(state) new_state = dict(state)
new_state.update({ new_state.update({
"status": "done", "status": "done",
"mode": "preview", "mode": "preview",
"preview_fingerprint": fingerprint_after, "preview_fingerprint": final_fingerprint,
"preview_output": preview_output, "preview_output": preview_output,
"preview_generated_at": now(), "preview_generated_at": now(),
}) })
@ -306,11 +346,16 @@ def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_arg
return return
output = publish_output(host, remote_root, local_mp4, old_output) output = publish_output(host, remote_root, local_mp4, old_output)
try:
upload_audio_files(host, job_dir, local_input)
except Exception as exc:
print(f"warning: audio upload failed: {exc}")
final_fingerprint = remote_fingerprint(host, job_dir)
new_state = dict(state) new_state = dict(state)
new_state.update({ new_state.update({
"status": "done", "status": "done",
"mode": "full", "mode": "full",
"fingerprint": fingerprint_after, "fingerprint": final_fingerprint,
"output": output, "output": output,
"generated_at": now(), "generated_at": now(),
}) })