From c9e02f666d65d73cd27cdb69cfb92c52dde9ce47 Mon Sep 17 00:00:00 2001 From: kevin314 Date: Fri, 23 Oct 2026 09:27:21 +0000 Subject: [PATCH] Add reference image previews --- apps/fastvideo_studio/mock_server.py | 103 ++++++++++++++++-- apps/fastvideo_studio/reference_preview.py | 28 +++++ apps/fastvideo_studio/server.py | 10 ++ .../components/jobs/CreateJobModal.test.tsx | 45 +++++++- .../src/components/jobs/CreateJobModal.tsx | 27 ++++- .../components/jobs/ReferenceThumbnail.tsx | 35 ++++++ apps/fastvideo_studio/src/lib/api.ts | 5 + .../tests/test_mock_server_jobs.py | 56 ++++++++++ 8 files changed, 300 insertions(+), 9 deletions(-) create mode 100644 apps/fastvideo_studio/reference_preview.py create mode 100644 apps/fastvideo_studio/src/components/jobs/ReferenceThumbnail.tsx diff --git a/apps/fastvideo_studio/mock_server.py b/apps/fastvideo_studio/mock_server.py index 02402202f..a6989c556 100644 --- a/apps/fastvideo_studio/mock_server.py +++ b/apps/fastvideo_studio/mock_server.py @@ -20,10 +20,17 @@ snapshot taken once at startup: nothing created or changed while the mock runs is ever written back to that database, so it's safe to point at the same ``--data-dir`` a real ``server.py`` is using. +Pass ``--state-file`` to keep the mock's own job changes across restarts: +after every job create/edit/queue/delete the mock writes the jobs it created +or changed (and the snapshot jobs it deleted) to that JSON file, and applies +it on top of the snapshot at the next startup. The SQLite database is still +never written; delete the file to reset the mock. + Usage (from the ``apps/`` directory):: PYTHONPATH=.. python -m fastvideo_studio.mock_server --port 8190 - PYTHONPATH=.. python -m fastvideo_studio.mock_server --port 8190 --data-dir ../outputs/ui_data + PYTHONPATH=.. python -m fastvideo_studio.mock_server --port 8190 --data-dir ../outputs/ui_data \ + --state-file ../outputs/ui_data/mock_state.json """ from __future__ import annotations @@ -51,12 +58,12 @@ from fastapi.responses import FileResponse, PlainTextResponse from fastvideo_studio.database import Database, default_settings_dict from fastvideo_studio.job_queue import circular_dependency, dependencies, plan_dispatch from fastvideo_studio.merge import MergeError, merge_jobs, merged_path +from fastvideo_studio.reference_preview import reference_image_response from fastvideo_studio.scene_writer.routes import router as scene_writer_router from fastvideo_studio.scene_writer.runner import use_server_data_dir from fastvideo_studio.trim import BRIGHTNESS_RANGE, CONTRAST_RANGE, SATURATION_RANGE from fastvideo_studio.models import (CreateDatasetRequest, CreateJobRequest, GradeSegment, MergeSceneRequest, - QueueJobsRequest, SettingsUpdate, TrimRequest, UpdateCaptionRequest, - model_label) + QueueJobsRequest, SettingsUpdate, TrimRequest, UpdateCaptionRequest, model_label) logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s") logger = logging.getLogger("fastvideo.studio.mock") @@ -122,6 +129,9 @@ _jobs: dict[str, dict[str, Any]] = {} _datasets: dict[str, dict[str, Any]] = {} # dataset_id -> {"file_names": [...], "captions": {file_name: caption}} _dataset_files: dict[str, dict[str, Any]] = {} +# Each snapshot job as loaded, so --state-file saves only what the mock changed. +_snapshot_originals: dict[str, dict[str, Any]] = {} +_state_file: Path | None = None # Lazily-built, cached media clips (shared by every job/dataset media response), # keyed by extension: "mp4" (1s testsrc video) or "png" (single testsrc frame). @@ -161,7 +171,7 @@ def _build_mock_media_with_pyav(kind: str, path: str) -> None: codec_name = "h264" except Exception: logger.warning("PyAV has no h264 encoder available; falling back to mpeg4, which most " - "browsers cannot play in