feat(snapshots): AE scene-snapshot pipeline + admin trigger (Epic C, C1)
Build backend images / build content-svc (push) Failing after 31s
Build backend images / build file-svc (push) Failing after 30s
Build backend images / build gateway (push) Failing after 30s
Build backend images / build identity-svc (push) Failing after 30s
Build backend images / build notification-svc (push) Failing after 31s
Build backend images / build render-svc (push) Failing after 31s
Build backend images / build studio-svc (push) Failing after 31s
Build backend images / build content-svc (push) Failing after 31s
Build backend images / build file-svc (push) Failing after 30s
Build backend images / build gateway (push) Failing after 30s
Build backend images / build identity-svc (push) Failing after 30s
Build backend images / build notification-svc (push) Failing after 31s
Build backend images / build render-svc (push) Failing after 31s
Build backend images / build studio-svc (push) Failing after 31s
Per-scene preview thumbnails for templates. Admin clicks "ساخت پیشنمایش صحنهها" → one single-frame AE render per scene → content.scenes.snapshot_url → shown as a thumbnail in the admin scene list (and available to the studio). - migration 30_render_snapshot_jobs.sql: render.snapshot_jobs (queued|running| done|error, per scene, image_url). - render-svc: db/snapshotjobs.go (EnqueueSceneSnapshots, List, Claim, SetResult -> writes content.scenes.snapshot_url cross-schema, SetError); handlers/ snapshotjobs.go (admin POST/GET /v1/scene-snapshots/:project_id + node-facing internal claim/result/fail); main.go routes; gateway route. - devworker: RunSnapshots — fulfils snapshot jobs with a generated placeholder PNG (data: URL, scene-key-tinted) so the flow is verifiable without an AE node. Gated by RENDER_DEV_SNAPSHOTS (default off; never hijacks real render jobs). - admin UI: ProjectScenes "generate snapshots" button (enqueue + poll + reload) and a thumbnail (snapshot_url || image) per scene row. Verified e2e via the dev mock: enqueue -> jobs run -> content.scenes.snapshot_url populated -> scenes API returns it -> admin renders the thumbnail. Remaining (C2): node-agent real-AE runner — claim snapshot, aerender -s0 -e0 -> ffmpeg still -> upload to a PERMANENT URL (mirror file-svc, not the time-limited export presign) -> post result. Needs a live AE node to build + verify. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,7 @@ func main() {
|
||||
fontH := handlers.NewFontHandler(store)
|
||||
bundleH := handlers.NewTemplateBundleHandler(mc, minioTemplatesBucket)
|
||||
scanH := handlers.NewScanHandler(store, mc, minioTemplatesBucket)
|
||||
snapJobH := handlers.NewSnapshotJobHandler(store, mc, minioTemplatesBucket)
|
||||
internalH := handlers.NewInternalHandler(store, notifyClient, mc, minioTemplatesBucket, minioBucket)
|
||||
|
||||
// ── Dev mock worker (no AE node needed) ────────────────────────────────────
|
||||
@@ -90,6 +91,11 @@ func main() {
|
||||
if devWorker {
|
||||
go devworker.New(store).Run(context.Background())
|
||||
}
|
||||
// Snapshot-only dev mock: fulfils scene-snapshot jobs with a generated
|
||||
// placeholder (no AE), gated separately so it never hijacks real render jobs.
|
||||
if getEnv("RENDER_DEV_SNAPSHOTS", "false") == "true" {
|
||||
go devworker.New(store).RunSnapshots(context.Background())
|
||||
}
|
||||
|
||||
// ── Router ────────────────────────────────────────────────────────────────
|
||||
r := gin.Default()
|
||||
@@ -174,6 +180,10 @@ func main() {
|
||||
v1.POST("/template-bundles/:project_id", auth, admin, bundleH.Set)
|
||||
|
||||
// ── Template scans (admin: read scenes/colours/configs from the AEP) ───────
|
||||
// ── Scene snapshots (admin: render one frame per scene from AE) ────────────
|
||||
v1.POST("/scene-snapshots/:project_id", auth, admin, snapJobH.Enqueue)
|
||||
v1.GET("/scene-snapshots/:project_id", auth, admin, snapJobH.List)
|
||||
|
||||
v1.POST("/template-scans/:project_id/quick", auth, admin, scanH.QuickScan) // headless Go quick-scan
|
||||
v1.POST("/template-scans/:project_id/jobs", auth, admin, scanH.CreateJob) // queue an AE full scan
|
||||
v1.GET("/template-scan-jobs/:id", auth, admin, scanH.GetJob)
|
||||
@@ -205,6 +215,11 @@ func main() {
|
||||
internal.POST("/render/jobs/:job_id/crash", internalH.Crash)
|
||||
internal.POST("/render/jobs/:job_id/replica-ready", internalH.ReplicaReady)
|
||||
|
||||
// AE scene snapshots (node claims, renders one frame, posts the image URL)
|
||||
internal.POST("/snapshot/claim", snapJobH.Claim)
|
||||
internal.POST("/snapshot/:id/result", snapJobH.Result)
|
||||
internal.POST("/snapshot/:id/fail", snapJobH.Fail)
|
||||
|
||||
// AE scan jobs (node claims, runs scan.jsx, posts the ScanResult back)
|
||||
internal.POST("/scan/claim", scanH.Claim)
|
||||
internal.GET("/scan/:id/status", scanH.Status) // node watchdog (cancel detection)
|
||||
|
||||
Reference in New Issue
Block a user