feat(render B2): render binder writes user edits into AE before render
Build backend images / build content-svc (push) Failing after 52s
Build backend images / build file-svc (push) Failing after 56s
Build backend images / build gateway (push) Failing after 53s
Build backend images / build identity-svc (push) Failing after 1m29s
Build backend images / build notification-svc (push) Failing after 1m38s
Build backend images / build render-svc (push) Failing after 1m53s
Build backend images / build studio-svc (push) Failing after 56s

Edits previously never reached the MP4 (the node rendered template defaults). Now:
- render-svc claim includes the saved input values as bindings (GetRenderBindings →
  saved_scene_contents with non-empty value).
- node-agent: new binder.go emits a JSON bind-spec + downloads media locally, runs the
  pre-existing data-driven bind.jsx via afterfx (sets text layers' Source Text, replaces
  media footage), saves a bound.aep next to the template, then aerender renders THAT.
- 12-min timeout + fresh-AE + done-marker polling (mirrors scan). Non-fatal: on bind
  failure the job still renders template defaults.

Verified binding data flows (edited frl_c1t1/frl_c1t2 → claim bindings). Live MP4
verification needs the updated node-agent.exe re-run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-07 01:22:20 +03:30
parent a69bc62724
commit 47a4ced973
8 changed files with 280 additions and 0 deletions
+25
View File
@@ -646,6 +646,31 @@ func (s *Store) GetTemplateCompName(ctx context.Context, originalProjectID uuid.
return *comp, nil
}
// GetRenderBindings returns the user's edited input values for a saved project so the
// node can write them into the AE project before rendering (the render binder). Only
// inputs with a non-empty value are returned (defaults are already in the template).
func (s *Store) GetRenderBindings(ctx context.Context, savedProjectID uuid.UUID) ([]models.RenderBinding, error) {
rows, err := s.pool.Query(ctx, `
SELECT c.key, c.type, COALESCE(c.value, '')
FROM studio.saved_scene_contents c
JOIN studio.saved_scenes s ON s.id = c.saved_scene_id
WHERE s.saved_project_id = $1 AND c.value IS NOT NULL AND c.value <> ''`,
savedProjectID)
if err != nil {
return nil, err
}
defer rows.Close()
var out []models.RenderBinding
for rows.Next() {
var b models.RenderBinding
if err := rows.Scan(&b.Key, &b.Type, &b.Value); err != nil {
return nil, err
}
out = append(out, b)
}
return out, rows.Err()
}
func (s *Store) CreateExportForJob(ctx context.Context, jobID uuid.UUID) (*models.Export, error) {
// Look up the job to get tenant/user/project context
job, err := s.getJobByIDInternal(ctx, jobID)