transloadit 6.2.1 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/alphalib/types/robots/_html-pdf.d.ts +3 -0
- package/dist/alphalib/types/robots/_html-pdf.d.ts.map +1 -0
- package/dist/alphalib/types/robots/_html-pdf.js +3 -0
- package/dist/alphalib/types/robots/_html-pdf.js.map +1 -0
- package/dist/alphalib/types/robots/_video-generate-description.d.ts +3 -0
- package/dist/alphalib/types/robots/_video-generate-description.d.ts.map +1 -0
- package/dist/alphalib/types/robots/_video-generate-description.js +3 -0
- package/dist/alphalib/types/robots/_video-generate-description.js.map +1 -0
- package/dist/alphalib/types/robots/cloudflare-store.d.ts +12 -0
- package/dist/alphalib/types/robots/cloudflare-store.d.ts.map +1 -1
- package/dist/alphalib/types/robots/cloudflare-store.js +20 -0
- package/dist/alphalib/types/robots/cloudflare-store.js.map +1 -1
- package/dist/alphalib/types/robots/document-convert.d.ts.map +1 -1
- package/dist/alphalib/types/robots/document-convert.js +3 -0
- package/dist/alphalib/types/robots/document-convert.js.map +1 -1
- package/dist/alphalib/types/robots/html-convert.d.ts.map +1 -1
- package/dist/alphalib/types/robots/html-convert.js +3 -0
- package/dist/alphalib/types/robots/html-convert.js.map +1 -1
- package/dist/alphalib/types/robots/s3-store.d.ts +8 -8
- package/dist/alphalib/types/robots/s3-store.d.ts.map +1 -1
- package/dist/alphalib/types/robots/s3-store.js +27 -1
- package/dist/alphalib/types/robots/s3-store.js.map +1 -1
- package/dist/alphalib/types/robots/text-speak.d.ts.map +1 -1
- package/dist/alphalib/types/robots/text-speak.js +0 -24
- package/dist/alphalib/types/robots/text-speak.js.map +1 -1
- package/dist/alphalib/types/robots/text-translate.d.ts.map +1 -1
- package/dist/alphalib/types/robots/text-translate.js +0 -13
- package/dist/alphalib/types/robots/text-translate.js.map +1 -1
- package/dist/alphalib/types/robots/video-encode.d.ts.map +1 -1
- package/dist/alphalib/types/robots/video-encode.js +2 -0
- package/dist/alphalib/types/robots/video-encode.js.map +1 -1
- package/dist/alphalib/types/robots/video-generate.d.ts.map +1 -1
- package/dist/alphalib/types/robots/video-generate.js +15 -25
- package/dist/alphalib/types/robots/video-generate.js.map +1 -1
- package/dist/alphalib/types/robots/video-subtitle.d.ts.map +1 -1
- package/dist/alphalib/types/robots/video-subtitle.js +2 -0
- package/dist/alphalib/types/robots/video-subtitle.js.map +1 -1
- package/dist/alphalib/types/robots/video-thumbs.d.ts.map +1 -1
- package/dist/alphalib/types/robots/video-thumbs.js +2 -0
- package/dist/alphalib/types/robots/video-thumbs.js.map +1 -1
- package/dist/cli/commands/assemblies.d.ts.map +1 -1
- package/dist/cli/commands/assemblies.js +24 -14
- package/dist/cli/commands/assemblies.js.map +1 -1
- package/package.json +1 -1
- package/src/alphalib/types/robots/_html-pdf.ts +3 -0
- package/src/alphalib/types/robots/_video-generate-description.ts +2 -0
- package/src/alphalib/types/robots/cloudflare-store.ts +20 -0
- package/src/alphalib/types/robots/document-convert.ts +3 -0
- package/src/alphalib/types/robots/html-convert.ts +3 -0
- package/src/alphalib/types/robots/s3-store.ts +27 -1
- package/src/alphalib/types/robots/text-speak.ts +0 -24
- package/src/alphalib/types/robots/text-translate.ts +0 -13
- package/src/alphalib/types/robots/video-encode.ts +2 -0
- package/src/alphalib/types/robots/video-generate.ts +15 -27
- package/src/alphalib/types/robots/video-subtitle.ts +2 -0
- package/src/alphalib/types/robots/video-thumbs.ts +2 -0
- package/src/cli/commands/assemblies.ts +23 -14
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Shared English contract for the HTML-to-PDF capture used by both conversion Robots. */
|
|
2
|
+
export const htmlPdfReadinessDescription =
|
|
3
|
+
'For HTML-to-PDF output, printing starts once sampled visible frames, their documents, and their fonts have stayed ready for 500 ms. After navigation, PDF readiness has a 30-second cap; a page that does not become ready fails conversion. Hidden frames do not extend the ready interval. Content that first appears during the final sample window may be missed. Prepare dynamic content before capture.'
|
|
@@ -24,6 +24,20 @@ export const meta: RobotMetaInput = {
|
|
|
24
24
|
'Export uploaded files to `my_target_folder` in a Cloudflare R2 bucket:',
|
|
25
25
|
extended_description: `
|
|
26
26
|
The URL to the result file will be returned in the <dfn>Assembly Status JSON</dfn>.
|
|
27
|
+
|
|
28
|
+
## Temporary credentials and permissions
|
|
29
|
+
|
|
30
|
+
You can use [temporary R2 credentials](https://developers.cloudflare.com/r2/api/s3/temporary-credentials/) by supplying the matching temporary \`key\`, \`secret\`, and \`session_token\`. Use the temporary secret returned or derived with the token, not the parent secret used to mint it. These values can also be stored together in Cloudflare <dfn>Template Credentials</dfn>. For per-Assembly credentials, send all three from your trusted backend; do not expose the parent credentials in browser instructions.
|
|
31
|
+
|
|
32
|
+
Credentials must remain valid through queuing, processing, upload retries, and verification. The Robot does not refresh them. Signed download URLs cannot outlive the credentials that sign them, even when \`sign_urls_for\` is longer.
|
|
33
|
+
|
|
34
|
+
When overriding a stored \`key\` or \`secret\` with a different value, supply the matching \`session_token\` inline too; the stored token will not be inherited. When updating Cloudflare Template Credentials, replacing either key or secret without a new token removes the old token. Omitting the token retains it only when the key and secret stay unchanged. You can also explicitly remove it with an empty string or \`null\`; empty tokens are treated as absent on creation too.
|
|
35
|
+
|
|
36
|
+
For locally signed credentials using explicit R2 \`actions\`, grant \`PutObject\` for small uploads and \`HeadObject\` for the Robot’s mandatory signed HEAD verification. Uploads larger than the default 5 MiB part size use multipart requests: also grant \`CreateMultipartUpload\`, \`UploadPart\`, \`CompleteMultipartUpload\`, and \`AbortMultipartUpload\` for failed-upload cleanup. Grant \`GetObject\` if consumers will use signed download URLs. These are R2 action names, not AWS IAM policy actions.
|
|
37
|
+
|
|
38
|
+
Scope \`paths.objectPaths\` to the exact destination object key and set \`path\` to that same key. A path ending in \`/\` appends a generated filename and will not match an exact-object grant. Use one output per exact-object key to avoid overwrites. No bucket listing or region-discovery permission is needed.
|
|
39
|
+
|
|
40
|
+
Strictly write-only credentials cannot pass this Robot’s HEAD verification. For uploads without read verification, use [/s3/store](/docs/robots/s3-store/) with the R2 configuration documented there. A presigned PUT URL is a different credential format and cannot be used as this Robot’s \`host\`.
|
|
27
41
|
`,
|
|
28
42
|
has_small_icon: true,
|
|
29
43
|
purpose_sentence: 'exports encoding results to Cloudflare R2 buckets',
|
|
@@ -39,6 +53,12 @@ export const robotCloudflareStoreInstructionsSchema = robotBase
|
|
|
39
53
|
.extend({
|
|
40
54
|
robot: z.literal('/cloudflare/store'),
|
|
41
55
|
path: storeFilePath,
|
|
56
|
+
session_token: z
|
|
57
|
+
.string()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe(`
|
|
60
|
+
The session token belonging to the temporary Cloudflare R2 access key ID and secret access key supplied for this upload. It can also be supplied inside Template Credentials. The credentials must remain valid through upload and signed HEAD verification; the Robot does not refresh them.
|
|
61
|
+
`),
|
|
42
62
|
headers: z
|
|
43
63
|
.record(z.string())
|
|
44
64
|
.default({ 'Content-Type': '${file.mime}' })
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
documentConvertLoadInternalErrorReason,
|
|
12
12
|
documentConvertLoadUserErrorReason,
|
|
13
13
|
} from '../responseErrors/DocumentConvertRobot.ts'
|
|
14
|
+
import { htmlPdfReadinessDescription } from './_html-pdf.ts'
|
|
14
15
|
import {
|
|
15
16
|
createProcessingExample,
|
|
16
17
|
defineRobot,
|
|
@@ -119,6 +120,8 @@ export const robotDocumentConvertInstructionsSchema = robotBase
|
|
|
119
120
|
.merge(robotUse)
|
|
120
121
|
.extend({
|
|
121
122
|
robot: z.literal('/document/convert').describe(`
|
|
123
|
+
${htmlPdfReadinessDescription}
|
|
124
|
+
|
|
122
125
|
> [!Note]
|
|
123
126
|
> This Robot can convert files to PDF, but cannot convert PDFs to different formats. If you want to convert PDFs to say, JPEG or TIFF, use [🤖/image/resize](/docs/robots/image-resize/). If you want to turn them into text files or recognize (OCR) them to make them searchable, reach out, as we have a new Robot in the works for this.
|
|
124
127
|
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
|
|
7
7
|
import { z } from 'zod'
|
|
8
8
|
|
|
9
|
+
import { htmlPdfReadinessDescription } from './_html-pdf.ts'
|
|
9
10
|
import {
|
|
10
11
|
defineRobotWithHiddenFields,
|
|
11
12
|
robotBase,
|
|
@@ -50,6 +51,8 @@ export const robotHtmlConvertInstructionsSchema = robotBase
|
|
|
50
51
|
A URL can be provided instead of an input HTML file, to capture a screenshot from the website referenced by the URL.
|
|
51
52
|
|
|
52
53
|
Use [🤖/image/resize](/docs/robots/image-resize/) to resize or crop the screenshot as needed.
|
|
54
|
+
|
|
55
|
+
${htmlPdfReadinessDescription}
|
|
53
56
|
`),
|
|
54
57
|
url: z
|
|
55
58
|
.string()
|
|
@@ -36,6 +36,32 @@ export const meta: RobotMetaInput = {
|
|
|
36
36
|
...robotStoreMeta,
|
|
37
37
|
example_code: createStorageStoreExample('/s3/store', 'YOUR_AWS_CREDENTIALS'),
|
|
38
38
|
example_code_description: 'Export uploaded files to `my_target_folder` in an S3 bucket:',
|
|
39
|
+
extended_description: `
|
|
40
|
+
## Export to R2 with temporary credentials
|
|
41
|
+
|
|
42
|
+
To upload one output to an exact R2 object key, send the matching temporary credentials from your trusted backend:
|
|
43
|
+
|
|
44
|
+
\`\`\`json
|
|
45
|
+
{
|
|
46
|
+
"robot": "/s3/store",
|
|
47
|
+
"use": ":original",
|
|
48
|
+
"host": "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
|
|
49
|
+
"bucket": "my-bucket",
|
|
50
|
+
"bucket_region": "auto",
|
|
51
|
+
"acl": "bucket-default",
|
|
52
|
+
"key": "TEMPORARY_ACCESS_KEY_ID",
|
|
53
|
+
"secret": "TEMPORARY_SECRET_ACCESS_KEY",
|
|
54
|
+
"session_token": "TEMPORARY_SESSION_TOKEN",
|
|
55
|
+
"path": "exports/result.jpg"
|
|
56
|
+
}
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
The HTTPS host enables TLS, \`bucket_region: "auto"\` avoids region discovery, and \`acl: "bucket-default"\` omits the generated ACL header. Do not add ACL or grant headers. Scope the [R2 temporary credentials](https://developers.cloudflare.com/r2/api/s3/temporary-credentials/) to the same bucket and exact object key, here \`exports/result.jpg\`. Do not end \`path\` with \`/\`: that appends a generated filename. Export one output per key to avoid overwrites.
|
|
60
|
+
|
|
61
|
+
For locally signed R2 credentials, \`actions: ["PutObject"]\` permits small single-request uploads with this configuration. Uploads larger than the default 5 MiB part size also require \`CreateMultipartUpload\`, \`UploadPart\`, \`CompleteMultipartUpload\`, and \`AbortMultipartUpload\` for failed-upload cleanup. These are R2 action names, not AWS IAM policy actions. No listing, region discovery, HEAD, or GET is required by this upload configuration. Unlike [/cloudflare/store](/docs/robots/cloudflare-store/), this Robot does not perform HEAD verification after upload.
|
|
62
|
+
|
|
63
|
+
Credentials must remain valid through queuing, processing, and upload retries; the Robot does not refresh them. A returned object URL does not grant read access. If you request signed download URLs with \`sign_urls_for\`, add \`GetObject\` permission; those URLs also expire when the temporary credentials expire. A presigned PUT URL cannot be used as \`host\`.
|
|
64
|
+
`,
|
|
39
65
|
has_small_icon: true,
|
|
40
66
|
purpose_sentence: 'exports encoding results to Amazon S3',
|
|
41
67
|
purpose_word: 'Amazon S3',
|
|
@@ -155,7 +181,7 @@ This parameter provides signed URLs in the result JSON (in the \`signed_url\` an
|
|
|
155
181
|
.string()
|
|
156
182
|
.optional()
|
|
157
183
|
.describe(`
|
|
158
|
-
The session token belonging to the temporary
|
|
184
|
+
The session token belonging to the temporary access key ID and secret access key supplied for this upload to S3 or a compatible service such as R2. The Robot does not assume a role or refresh these credentials; they must remain valid for the upload.
|
|
159
185
|
`),
|
|
160
186
|
})
|
|
161
187
|
.strict()
|
|
@@ -28,30 +28,6 @@ export const meta: RobotMetaInput = {
|
|
|
28
28
|
extended_description: `
|
|
29
29
|
> [!Warning]
|
|
30
30
|
> Transloadit aims to be deterministic, but this <dfn>Robot</dfn> uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input media. Avoid relying on exact responses in your tests and application.
|
|
31
|
-
|
|
32
|
-
## Supported languages and voices
|
|
33
|
-
|
|
34
|
-
{% for provider in text_speak_voices %}
|
|
35
|
-
|
|
36
|
-
### {{provider[0] | upcase }}
|
|
37
|
-
|
|
38
|
-
<table class="table">
|
|
39
|
-
<thead>
|
|
40
|
-
<tr>
|
|
41
|
-
<th>Language</th>
|
|
42
|
-
<th>Voices</th>
|
|
43
|
-
</tr>
|
|
44
|
-
</thead>
|
|
45
|
-
<tbody>
|
|
46
|
-
{%- for language in provider[1] %}
|
|
47
|
-
<tr>
|
|
48
|
-
<td><strong>{{language[0]}}</strong></td>
|
|
49
|
-
<td>{{ language[1] | join: ", " }}</td>
|
|
50
|
-
</tr>
|
|
51
|
-
{%- endfor %}
|
|
52
|
-
</tbody>
|
|
53
|
-
</table>
|
|
54
|
-
{% endfor %}
|
|
55
31
|
`,
|
|
56
32
|
override_lvl1: 'Artificial Intelligence',
|
|
57
33
|
purpose_sentence: 'synthesizes speech in documents',
|
|
@@ -26,19 +26,6 @@ export const meta: RobotMetaInput = {
|
|
|
26
26
|
extended_description: `
|
|
27
27
|
> [!Warning]
|
|
28
28
|
> This <dfn>Robot</dfn> uses third-party AI services. They may tweak their models over time, giving different responses for the same input media. Avoid relying on exact responses in your tests and application.
|
|
29
|
-
|
|
30
|
-
## Supported languages
|
|
31
|
-
|
|
32
|
-
{%- for provider in text_translate_languages %}
|
|
33
|
-
|
|
34
|
-
### {{provider[0] | upcase }}
|
|
35
|
-
|
|
36
|
-
<!-- prettier-ignore -->
|
|
37
|
-
{%- for language in provider[1] %}
|
|
38
|
-
{{ language }}{% unless forloop.last %},{% endunless %}
|
|
39
|
-
{%- endfor %}
|
|
40
|
-
|
|
41
|
-
{%- endfor %}
|
|
42
29
|
`,
|
|
43
30
|
override_lvl1: 'Artificial Intelligence',
|
|
44
31
|
purpose_sentence: 'translates text in documents',
|
|
@@ -45,6 +45,8 @@ Videos recorded with iPhone spatial audio can contain a stereo AAC track alongsi
|
|
|
45
45
|
|
|
46
46
|
Files containing only unsupported audio still fail. Individual audio track selections and stream-copy requests keep the tracks you select; the output format must support copied tracks.
|
|
47
47
|
|
|
48
|
+
When adding watermarks or drawing with simple FFmpeg video filters such as \`drawtext\` to HLG or PQ HDR video, the video is tone-mapped to SDR BT.709 before drawing, so overlay colors retain their intended RGB values. SDR inputs keep their existing processing. A custom \`ffmpeg.pre:filter:v\` that supplies tone mapping controls its own color conversion. Custom multi-input \`filter_complex\` graphs must handle each input’s color space explicitly.
|
|
49
|
+
|
|
48
50
|
## Adding text overlays with FFmpeg
|
|
49
51
|
|
|
50
52
|
You can add text overlays to videos using FFmpeg's \`drawtext\` filter through this <dfn>Robot</dfn>'s \`ffmpeg\` parameter. Here are two examples — one with the default font and one with a custom font family name:
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
robotParameterDocs,
|
|
12
12
|
robotUse,
|
|
13
13
|
} from './_instructions-primitives.ts'
|
|
14
|
+
import { videoGenerationParameterEnforcementDate } from './_video-generate-description.ts'
|
|
14
15
|
|
|
15
16
|
export const meta: RobotMetaInput = {
|
|
16
17
|
...robotArtificialIntelligenceMeta,
|
|
@@ -19,8 +20,6 @@ export const meta: RobotMetaInput = {
|
|
|
19
20
|
generated_video: {
|
|
20
21
|
robot: '/video/generate',
|
|
21
22
|
prompt: 'A slow cinematic drone shot of ocean cliffs at sunrise, realistic lighting.',
|
|
22
|
-
duration: 5,
|
|
23
|
-
aspect_ratio: '16:9',
|
|
24
23
|
format: 'mp4',
|
|
25
24
|
},
|
|
26
25
|
},
|
|
@@ -46,15 +45,18 @@ export const robotVideoGenerateInstructionsSchema = robotBase
|
|
|
46
45
|
.string()
|
|
47
46
|
.optional()
|
|
48
47
|
.describe(
|
|
49
|
-
`
|
|
48
|
+
`Video model; defaults to ${videoGenerateDefaultModelIdentifier}. Unsupported settings warn until ${videoGenerationParameterEnforcementDate}. MiniMax outputs are about six seconds.`,
|
|
50
49
|
),
|
|
51
50
|
prompt: z.string().describe('The prompt describing the desired video content.'),
|
|
52
51
|
format: z.enum(['mp4']).optional().describe('Format of the generated video.'),
|
|
53
52
|
seed: z.number().optional().describe(robotParameterDocs.seed.description),
|
|
54
|
-
aspect_ratio: z
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
aspect_ratio: z
|
|
54
|
+
.string()
|
|
55
|
+
.optional()
|
|
56
|
+
.describe('Aspect ratio for lightricks/ltx-video without an input image.'),
|
|
57
|
+
height: z.number().optional().describe('Pixel height for tencent/hunyuan-video.'),
|
|
58
|
+
width: z.number().optional().describe('Pixel width for tencent/hunyuan-video.'),
|
|
59
|
+
style: z.string().optional().describe('Unsupported by current models; omit it.'),
|
|
58
60
|
num_outputs: z
|
|
59
61
|
.number()
|
|
60
62
|
.int()
|
|
@@ -62,26 +64,12 @@ export const robotVideoGenerateInstructionsSchema = robotBase
|
|
|
62
64
|
.max(10)
|
|
63
65
|
.optional()
|
|
64
66
|
.describe('Number of video variants to generate.'),
|
|
65
|
-
duration: z.number().optional().describe('
|
|
66
|
-
fps: z.number().optional().describe('Frames per second
|
|
67
|
-
motion_amount: z
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
camera_motion: z
|
|
72
|
-
.string()
|
|
73
|
-
.optional()
|
|
74
|
-
.describe(
|
|
75
|
-
'Camera movement type (e.g., pan-left, pan-right, zoom-in, zoom-out, orbit, static, dolly, crane).',
|
|
76
|
-
),
|
|
77
|
-
negative_prompt: z
|
|
78
|
-
.string()
|
|
79
|
-
.optional()
|
|
80
|
-
.describe('Describes what should be avoided in the generated video.'),
|
|
81
|
-
reference_strength: z
|
|
82
|
-
.number()
|
|
83
|
-
.optional()
|
|
84
|
-
.describe('How closely the output should follow the reference input (0.0 - 1.0).'),
|
|
67
|
+
duration: z.number().optional().describe('Unsupported by current models; omit it.'),
|
|
68
|
+
fps: z.number().optional().describe('Frames per second for tencent/hunyuan-video.'),
|
|
69
|
+
motion_amount: z.number().optional().describe('Unsupported by current models; omit it.'),
|
|
70
|
+
camera_motion: z.string().optional().describe('Unsupported by current models; omit it.'),
|
|
71
|
+
negative_prompt: z.string().optional().describe('Content to avoid (lightricks/ltx-video).'),
|
|
72
|
+
reference_strength: z.number().optional().describe('Unsupported by current models; omit it.'),
|
|
85
73
|
})
|
|
86
74
|
.strict()
|
|
87
75
|
|
|
@@ -77,6 +77,8 @@ export const robotVideoSubtitleInstructionsSchema = robotBase
|
|
|
77
77
|
This <dfn>Robot</dfn> supports SRT, VTT, ASS, and the timestamped JSON output of \`/speech/transcribe\` with \`"format": "json"\`. Pass the video as \`"video"\` and the transcript as \`"subtitles"\` in the \`use\` parameter. JSON words are grouped into captions and converted to ASS for burned word highlighting, or plain SRT cues for external subtitle streams. External JSON cues preserve casing and punctuation. Provider segments do not control caption breaks. SRT and VTT timing is preserved.
|
|
78
78
|
|
|
79
79
|
Without a \`preset\` or custom \`ffmpeg\` options, the legacy \`"flash"\` preset produces FLV output. With this default, burned subtitles use MP3 audio; external subtitles copy the input audio. Set \`"preset": "empty"\` to keep the input container, such as MP4 for an MP4 input.
|
|
80
|
+
|
|
81
|
+
When burning subtitles into HLG or PQ HDR video, the video is tone-mapped to SDR BT.709 before captions are drawn. Caption colors retain their intended RGB values, and the output is tagged as SDR. External subtitles preserve the source video’s HDR colors. SDR inputs keep their existing processing. If \`ffmpeg.pre:filter:v\` supplies its own tone mapping, that filter controls color conversion.
|
|
80
82
|
`),
|
|
81
83
|
subtitles_type: zodInputPreservingTransform(z.enum(['burned', 'external', 'burn']), (val) =>
|
|
82
84
|
val === 'burn' ? 'burned' : val,
|
|
@@ -70,6 +70,8 @@ export const robotVideoThumbsInstructionsSchema = robotBase
|
|
|
70
70
|
robot: z.literal('/video/thumbs').describe(`
|
|
71
71
|
Set \`smart: true\` to select strong preview images with AI instead of taking frames only at regular intervals. The Robot scores candidate frames for clarity, brightness, composition, faces, expressions, action, and visual interest, then returns the best \`count\` frames in chronological order. Smart results include \`file.meta.smart_score\` and \`file.meta.smart_reasons\`. If AI scoring is unavailable, the Assembly continues with the candidate frames in fallback order and includes a warning. No AI credentials are required.
|
|
72
72
|
|
|
73
|
+
HLG and PQ HDR video frames are tone-mapped to SDR sRGB before image encoding, including smart-thumbnail candidates. This preserves the intended appearance in browsers that display thumbnails as sRGB images. SDR inputs keep their existing processing.
|
|
74
|
+
|
|
73
75
|
## AI pricing
|
|
74
76
|
|
|
75
77
|
Regular \`/video/thumbs\` processing charges still apply. AI frame analysis is billed separately at the underlying provider cost plus a ${smartThumbnailMarkupPercent}% Transloadit markup. Smart selection uses the dedicated \`${smartThumbnailModel}\` vision model with \`${smartThumbnailReasoningEffort}\` reasoning, independently of the \`/ai/chat\` default. The exact AI charge varies with the number of candidate frames and provider pricing.
|
|
@@ -484,12 +484,16 @@ function getJobInputPath(filepath: string): string {
|
|
|
484
484
|
return normalizedFile
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
-
function
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
487
|
+
function hasPipedStdin(): boolean {
|
|
488
|
+
if (process.stdin.isTTY) return false
|
|
489
|
+
|
|
490
|
+
try {
|
|
491
|
+
const stats = fs.fstatSync(process.stdin.fd)
|
|
492
|
+
return stats.isFIFO() || stats.isFile()
|
|
493
|
+
} catch {
|
|
494
|
+
// A closed or unavailable descriptor is not an implicit upload.
|
|
495
|
+
return false
|
|
496
|
+
}
|
|
493
497
|
}
|
|
494
498
|
|
|
495
499
|
function createOutputPlan(pathname: string | undefined, mtime: Date): OutputPlan {
|
|
@@ -747,7 +751,7 @@ async function resolveResultDownloadTargets({
|
|
|
747
751
|
return await buildDirectoryDownloadTargets({
|
|
748
752
|
allFiles,
|
|
749
753
|
baseDir: outputRoot,
|
|
750
|
-
groupByStep:
|
|
754
|
+
groupByStep: inputs.length === 0,
|
|
751
755
|
reservedPaths,
|
|
752
756
|
})
|
|
753
757
|
}
|
|
@@ -756,7 +760,7 @@ async function resolveResultDownloadTargets({
|
|
|
756
760
|
return await buildDirectoryDownloadTargets({
|
|
757
761
|
allFiles,
|
|
758
762
|
baseDir: resolveDirectoryBaseDir(),
|
|
759
|
-
groupByStep: entries.length > 1,
|
|
763
|
+
groupByStep: inPath == null || entries.length > 1,
|
|
760
764
|
reservedPaths,
|
|
761
765
|
})
|
|
762
766
|
}
|
|
@@ -1592,7 +1596,7 @@ export async function create(
|
|
|
1592
1596
|
const uploads =
|
|
1593
1597
|
inPath === stdinWithPath.path
|
|
1594
1598
|
? {
|
|
1595
|
-
in:
|
|
1599
|
+
in: process.stdin,
|
|
1596
1600
|
}
|
|
1597
1601
|
: undefined
|
|
1598
1602
|
|
|
@@ -1660,7 +1664,7 @@ export async function create(
|
|
|
1660
1664
|
scope: collection,
|
|
1661
1665
|
})
|
|
1662
1666
|
if (inPath === stdinWithPath.path) {
|
|
1663
|
-
uploads[key] =
|
|
1667
|
+
uploads[key] = process.stdin
|
|
1664
1668
|
} else {
|
|
1665
1669
|
files[key] = inPath
|
|
1666
1670
|
}
|
|
@@ -1802,6 +1806,8 @@ export class AssembliesCreateCommand extends AuthenticatedCommand {
|
|
|
1802
1806
|
details: `
|
|
1803
1807
|
Create assemblies to process media files using Transloadit.
|
|
1804
1808
|
You must specify either --steps or --template.
|
|
1809
|
+
Without uploads, directory outputs are saved as <output>/<step>/<result name>.
|
|
1810
|
+
Stdin is read with --input -, or implicitly with --steps when piped or redirected from a file.
|
|
1805
1811
|
`,
|
|
1806
1812
|
examples: [
|
|
1807
1813
|
[
|
|
@@ -1812,6 +1818,10 @@ export class AssembliesCreateCommand extends AuthenticatedCommand {
|
|
|
1812
1818
|
'Process with a template',
|
|
1813
1819
|
'transloadit assemblies create --template TEMPLATE_ID -i input.jpg -o output/',
|
|
1814
1820
|
],
|
|
1821
|
+
[
|
|
1822
|
+
'Download results from import-only or prompt-only steps',
|
|
1823
|
+
'transloadit assemblies create --steps steps.json -o output/',
|
|
1824
|
+
],
|
|
1815
1825
|
[
|
|
1816
1826
|
'Watch for changes',
|
|
1817
1827
|
'transloadit assemblies create --steps steps.json -i input/ -o output/ --watch',
|
|
@@ -1864,9 +1874,8 @@ export class AssembliesCreateCommand extends AuthenticatedCommand {
|
|
|
1864
1874
|
|
|
1865
1875
|
const inputList = this.inputs ?? []
|
|
1866
1876
|
|
|
1867
|
-
//
|
|
1868
|
-
|
|
1869
|
-
if (this.steps && inputList.length === 0 && !process.stdin.isTTY) {
|
|
1877
|
+
// Templates keep requiring explicit stdin; steps also accept pipes and redirected files.
|
|
1878
|
+
if (this.steps && inputList.length === 0 && hasPipedStdin()) {
|
|
1870
1879
|
inputList.push('-')
|
|
1871
1880
|
}
|
|
1872
1881
|
|
|
@@ -1975,7 +1984,7 @@ export class RunCommand extends AuthenticatedCommand {
|
|
|
1975
1984
|
protected async run(): Promise<number | undefined> {
|
|
1976
1985
|
const inputList = this.inputs ?? []
|
|
1977
1986
|
|
|
1978
|
-
if (inputList.length === 0 &&
|
|
1987
|
+
if (inputList.length === 0 && hasPipedStdin()) {
|
|
1979
1988
|
inputList.push('-')
|
|
1980
1989
|
}
|
|
1981
1990
|
|