fix(render): real AE render — pass -comp, fix export insert, ensure exports bucket

Three bugs surfaced bringing up a real After Effects node (verified: AE 2026
claimed + ran, but produced no usable output):

1. aerender got no -comp/-rqindex → "output argument ignored", nothing rendered.
   - Claim now returns comp_name from content.projects.render_aep_comp (e.g. "frfinal")
     via new Store.GetTemplateCompName; threaded through ClaimedJob → runner.Job →
     aerender args (`-comp <name>`, or `-rqindex 1` fallback when unknown).

2. CreateExportForJob INSERT passed render_quality as a bare param into an enum
   column → 500 ("output-upload-url HTTP 500"), so completed renders had no export.
   - Cast $8::render.render_quality (+ explicit casts for file_type/create_type enums).

3. flatrender-exports bucket didn't exist → uploads would fail anyway.
   - render-svc now MakeBucket(exports, templates) idempotently at startup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-05 22:40:20 +03:30
parent d8d0f6c363
commit 9d499a89de
7 changed files with 51 additions and 5 deletions
+10
View File
@@ -60,6 +60,16 @@ func main() {
if err != nil {
log.Fatalf("minio client: %v", err)
}
// Ensure the render output bucket exists (node agents PUT exports here).
for _, b := range []string{minioBucket, minioTemplatesBucket} {
if exists, berr := mc.BucketExists(context.Background(), b); berr == nil && !exists {
if merr := mc.MakeBucket(context.Background(), b, minio.MakeBucketOptions{}); merr != nil {
log.Printf("warning: could not create bucket %q: %v", b, merr)
} else {
log.Printf("created bucket %q", b)
}
}
}
// ── Store + handlers ──────────────────────────────────────────────────────
store := db.NewStore(pool)
+18 -2
View File
@@ -626,6 +626,22 @@ func (s *Store) ClaimJob(ctx context.Context, nodeID uuid.UUID, region string) (
// The export starts with a placeholder path `exports/{export_id}/output.mp4`.
// The node agent uploads the MP4 to that MinIO path, then calls CompleteJob
// with the returned export_id.
// GetTemplateCompName returns the After Effects composition to render for a
// template (content.projects.render_aep_comp), e.g. "frfinal". aerender needs
// this via -comp; without it AE opens the project but renders nothing.
func (s *Store) GetTemplateCompName(ctx context.Context, originalProjectID uuid.UUID) (string, error) {
var comp *string
err := s.pool.QueryRow(ctx,
`SELECT render_aep_comp FROM content.projects WHERE id = $1`, originalProjectID).Scan(&comp)
if err != nil {
return "", err
}
if comp == nil {
return "", nil
}
return *comp, nil
}
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)
@@ -646,8 +662,8 @@ func (s *Store) CreateExportForJob(ctx context.Context, jobID uuid.UUID) (*model
delete_notified, created_at)
VALUES
($1, $2, $3, $4, $5,
$6, $7, 'mp4', 'video', $8,
'render', 0, $9, $10,
$6, $7, 'mp4', 'video'::render.export_file_type, $8::render.render_quality,
'render'::render.export_create_type, 0, $9, $10,
false, $9)`,
exportID, job.TenantID, job.UserID, job.SavedProjectID, job.OriginalProjectID,
job.ID, path, job.Quality,
@@ -289,6 +289,9 @@ func (h *InternalHandler) Claim(c *gin.Context) {
}
}
// Composition to render (-comp). Non-fatal: empty → node uses the render queue.
compName, _ := h.store.GetTemplateCompName(c.Request.Context(), job.OriginalProjectID)
c.JSON(http.StatusOK, models.ClaimedJob{
JobID: job.ID,
SavedProjectID: job.SavedProjectID,
@@ -300,6 +303,7 @@ func (h *InternalHandler) Claim(c *gin.Context) {
AEPDownloadURL: aepURL,
IsBundle: isBundle,
BundleMD5: bundleMD5,
CompName: compName,
})
}
@@ -428,6 +428,10 @@ type ClaimedJob struct {
// BundleMD5 is the stored object's ETag/MD5 — the node uses it as a cache key so
// repeated renders of the same template download + extract the bundle only once.
BundleMD5 string `json:"bundle_md5,omitempty"`
// CompName is the After Effects composition to render (-comp). From the
// template's render_aep_comp (e.g. "frfinal"). Empty → node falls back to the
// project's render queue.
CompName string `json:"comp_name,omitempty"`
}
// OutputUploadURLResponse is returned by POST .../output-upload-url.