diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 7ebae25..3fe5181 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -438,7 +438,7 @@ type JobAsset struct { // Params Optional parameters for function assets. For supported NX layer functions (`nx:layer-autoscale`, `nx:layer-duration-set`, `nx:layer-start-set`, `nx:layer-state-set`, `nx:solid-color-set`, `nx:text-params-set`), `params.layerName` accepts a single string or an array of strings. When an array is provided, the API expands the function asset into multiple per-layer assets while preserving the original `assets` order. Params *JobAsset_Params `json:"params,omitempty"` - // Src Source URL, script data or file path for the asset (supports HTTP/HTTPS URLs or local paths) + // Src Public HTTP(S), native s3:/gs:, inline data:, or a file: URL inside the current job work directory. The worker rejects local paths and symlinks escaping that directory. Other schemes and non-public network destinations are rejected. Src *string `json:"src,omitempty"` // Type Asset type (e.g., image, video, audio, text, data, script, job) @@ -486,6 +486,9 @@ type JobAssetNested struct { // Frames Frame specification for image output Frames *JobAssetNested_Settings_Frames `json:"frames,omitempty"` + // MultiFrame Turn After Effects Multi-Frame Rendering on (true) or off (false) for the child job. If omitted, the worker's own setting applies + MultiFrame *bool `json:"multiFrame,omitempty"` + // Quality Render quality Quality *JobAssetNestedSettingsQuality `json:"quality,omitempty"` @@ -563,6 +566,9 @@ type JobCreation struct { // Frames Frame specification - single frame number or [start, end] range. Required for image type. Frames *JobCreation_Settings_Frames `json:"frames,omitempty"` + // MultiFrame Turn After Effects Multi-Frame Rendering on (true) or off (false) for this job. If omitted, the worker's own setting applies + MultiFrame *bool `json:"multiFrame,omitempty"` + // Quality Render quality setting - 'draft' for faster low-quality preview, 'full' for high-quality output Quality *JobCreationSettingsQuality `json:"quality,omitempty"` @@ -592,7 +598,7 @@ type JobCreation struct { // Bucket Target bucket name for file uploads (required) Bucket string `json:"bucket"` - // Endpoint Custom S3-compatible endpoint URL + // Endpoint Public HTTP(S) S3-compatible endpoint URL. Explicit customer credentials are required; worker credentials are not used for arbitrary endpoints. Endpoint *string `json:"endpoint,omitempty"` // Region AWS region or storage region identifier (required) @@ -722,9 +728,12 @@ type JoinJobAsset struct { Codec *string `json:"codec,omitempty"` // Engine After Effects render engine version for the child job - Engine *JoinJobAssetSettingsEngine `json:"engine,omitempty"` - Frames *JoinJobAsset_Settings_Frames `json:"frames,omitempty"` - Quality *JoinJobAssetSettingsQuality `json:"quality,omitempty"` + Engine *JoinJobAssetSettingsEngine `json:"engine,omitempty"` + Frames *JoinJobAsset_Settings_Frames `json:"frames,omitempty"` + + // MultiFrame Turn After Effects Multi-Frame Rendering on (true) or off (false) for the child job. If omitted, the worker's own setting applies + MultiFrame *bool `json:"multiFrame,omitempty"` + Quality *JoinJobAssetSettingsQuality `json:"quality,omitempty"` } `json:"settings,omitempty"` // Template Template configuration for the child job @@ -765,7 +774,7 @@ type JoinJobAssetType string // JoinJobCreation Configuration for creating a join/stitch job that combines multiple videos into one type JoinJobCreation struct { - // Assets Ordered list of video or job assets to stitch together + // Assets Ordered list of video or job assets to stitch together. A join needs at least two inputs. Assets []JoinJobCreation_Assets_Item `json:"assets"` // Settings Settings for the stitch/join operation diff --git a/internal/validate/job_test.go b/internal/validate/job_test.go index 6b6a8e4..dca64da 100644 --- a/internal/validate/job_test.go +++ b/internal/validate/job_test.go @@ -1,6 +1,10 @@ package validate -import "testing" +import ( + "encoding/json" + "strings" + "testing" +) func TestJobPayloadRules(t *testing.T) { valid := []byte(`{"template":{"id":"tpl","composition":"Main"},"settings":{"type":"video","codec":"video_h264_vbr_15mbps"}}`) @@ -46,3 +50,40 @@ func TestJobPayloadTemplateSource(t *testing.T) { } } } + +func TestJobPayloadKeepsMultiFrame(t *testing.T) { + for _, tc := range []struct { + name string + payload string + want string + }{ + {"off", `{"template":{"id":"tpl","composition":"Main"},"settings":{"multiFrame":false}}`, `"multiFrame":false`}, + {"on", `{"template":{"id":"tpl","composition":"Main"},"settings":{"multiFrame":true}}`, `"multiFrame":true`}, + } { + t.Run(tc.name, func(t *testing.T) { + payload, err := JobPayload([]byte(tc.payload)) + if err != nil { + t.Fatalf("payload failed: %v", err) + } + encoded, err := json.Marshal(payload) + if err != nil { + t.Fatalf("marshal payload: %v", err) + } + if !strings.Contains(string(encoded), tc.want) { + t.Fatalf("submitted payload %s does not contain %s", encoded, tc.want) + } + }) + } + + omitted, err := JobPayload([]byte(`{"template":{"id":"tpl","composition":"Main"},"settings":{"type":"video"}}`)) + if err != nil { + t.Fatalf("payload failed: %v", err) + } + encoded, err := json.Marshal(omitted) + if err != nil { + t.Fatalf("marshal payload: %v", err) + } + if strings.Contains(string(encoded), "multiFrame") { + t.Fatalf("submitted payload %s should leave multiFrame out when it is not set", encoded) + } +} diff --git a/openapi/openapi.json b/openapi/openapi.json index 74923e9..1516611 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -54,7 +54,7 @@ "properties": { "src": { "type": "string", - "description": "Source URL, script data or file path for the asset (supports HTTP/HTTPS URLs or local paths)" + "description": "Public HTTP(S), native s3:/gs:, inline data:, or a file: URL inside the current job work directory. The worker rejects local paths and symlinks escaping that directory. Other schemes and non-public network destinations are rejected." }, "type": { "type": "string", @@ -178,6 +178,10 @@ "enum": ["ae2025", "ae2026"], "default": "ae2026", "description": "After Effects render engine version for the child job" + }, + "multiFrame": { + "type": "boolean", + "description": "Turn After Effects Multi-Frame Rendering on (true) or off (false) for the child job. If omitted, the worker's own setting applies" } } } @@ -263,6 +267,10 @@ "enum": ["ae2025", "ae2026"], "default": "ae2026", "description": "After Effects render engine version for the child job" + }, + "multiFrame": { + "type": "boolean", + "description": "Turn After Effects Multi-Frame Rendering on (true) or off (false) for the child job. If omitted, the worker's own setting applies" } } } @@ -289,7 +297,7 @@ }, "assets": { "type": "array", - "description": "Ordered list of video or job assets to stitch together", + "description": "Ordered list of video or job assets to stitch together. A join needs at least two inputs.", "items": { "oneOf": [ { "$ref": "#/components/schemas/JoinVideoAsset" }, @@ -303,7 +311,7 @@ } } }, - "minItems": 1 + "minItems": 2 }, "upload": { "type": "object", @@ -528,6 +536,10 @@ "enum": ["ae2025", "ae2026"], "default": "ae2026", "description": "After Effects render engine version used when the job is picked up by a worker" + }, + "multiFrame": { + "type": "boolean", + "description": "Turn After Effects Multi-Frame Rendering on (true) or off (false) for this job. If omitted, the worker's own setting applies" } }, "additionalProperties": false @@ -557,7 +569,7 @@ "endpoint": { "type": "string", "default": "https://s3.amazonaws.com", - "description": "Custom S3-compatible endpoint URL" + "description": "Public HTTP(S) S3-compatible endpoint URL. Explicit customer credentials are required; worker credentials are not used for arbitrary endpoints." }, "region": { "type": "string", @@ -714,6 +726,7 @@ }, "displayName": { "type": "string", + "maxLength": 255, "description": "Human-readable name for the template shown in UI and listings" }, "src": { @@ -732,6 +745,7 @@ "properties": { "displayName": { "type": "string", + "maxLength": 255, "description": "Updated human-readable name for the template" } } diff --git a/skills/nexrender/references/operations.md b/skills/nexrender/references/operations.md index 9d824c1..175b0e0 100644 --- a/skills/nexrender/references/operations.md +++ b/skills/nexrender/references/operations.md @@ -5,6 +5,7 @@ - Layer names are case-sensitive. Use the `name` returned by `template layers` as `layerName`. - Codecs can be given by their short name (`h264_vbr_15mbps`, `prores_422`, `png`, `gif`) or in full (`video_h264_vbr_15mbps`, `image_png`). The API rejects unknown codecs and lists the supported ones. - `preview: true` and `settings` are mutually exclusive. Preview mode ignores normal output settings. +- `settings.multiFrame` turns After Effects Multi-Frame Rendering on (`true`) or off (`false`) for one job. Leave it out to keep the render node's default. Set `false` when a template renders incorrectly with Multi-Frame Rendering. - `${secrets.NAME}` is the secret reference form. - `missingFonts` is a visual-correctness warning. A render may finish with fallback fonts and still be wrong.