Files
manage/archive/src/media_library_viewer/jobs.py
T
alex 51b10438a9 Restructure into backend/ and frontend/ subprojects
- backend/ uses proper Python src layout (src/media_library_viewer_api/)
  with pyproject.toml, hatchling build, and PYTHONPATH=src convention
- frontend/ is a Vite + React + TypeScript SPA
- archive/ preserves the original Streamlit prototype for reference
- Cleaned up root to only contain docs, license, and subproject dirs
- Updated README for the new dual-subproject architecture
2026-04-30 21:48:46 +02:00

60 lines
2.0 KiB
Python

"""Template-based remote jobs.
Remote jobs are intentionally explicit templates instead of free-form shell input.
This keeps the UI safer and makes future destructive operations easier to wrap in
confirmations/dry-runs.
"""
from __future__ import annotations
import shlex
from dataclasses import dataclass
from typing import Mapping
from media_library_viewer.clients.ssh import CommandResult, RemoteSSHClient
@dataclass(frozen=True)
class JobTemplate:
"""Description and command template for one remote job."""
name: str
description: str
command_template: str
destructive: bool = False
def render(self, values: Mapping[str, str]) -> str:
"""Render the command with shell-quoted template values.
This is what keeps paths with spaces safe when inserted into job commands.
"""
safe_values = {key: shlex.quote(value) for key, value in values.items()}
return self.command_template.format(**safe_values)
# Phase 1 jobs are intentionally conservative. Add your own templates here later.
JOB_TEMPLATES: dict[str, JobTemplate] = {
"disk_usage": JobTemplate(
name="Disk usage for selected path",
description="Runs du -sh on the selected remote path.",
command_template="du -sh {path}",
),
"ffprobe": JobTemplate(
name="ffprobe JSON",
description="Prints raw ffprobe stream/format metadata.",
command_template="ffprobe -v error -show_format -show_streams -print_format json {path}",
),
"dry_run_find_empty_dirs": JobTemplate(
name="Find empty directories dry-run",
description="Lists empty directories under the selected path. Does not delete anything.",
command_template="find {path} -type d -empty -print",
),
}
def run_job(ssh: RemoteSSHClient, job_key: str, path: str, timeout: int = 600) -> CommandResult:
"""Render and execute a configured job template for a selected remote path."""
template = JOB_TEMPLATES[job_key]
command = template.render({"path": path})
return ssh.run(command, timeout=timeout)