transloadit 6.4.0 → 6.6.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.
Files changed (182) hide show
  1. package/README.md +30 -5
  2. package/dist/Transloadit.d.ts.map +1 -1
  3. package/dist/Transloadit.js +61 -5
  4. package/dist/Transloadit.js.map +1 -1
  5. package/dist/alphalib/renderSimpleValue.d.ts +14 -0
  6. package/dist/alphalib/renderSimpleValue.d.ts.map +1 -0
  7. package/dist/alphalib/renderSimpleValue.js +87 -0
  8. package/dist/alphalib/renderSimpleValue.js.map +1 -0
  9. package/dist/alphalib/truncateString.d.ts +5 -0
  10. package/dist/alphalib/truncateString.d.ts.map +1 -0
  11. package/dist/alphalib/truncateString.js +19 -0
  12. package/dist/alphalib/truncateString.js.map +1 -0
  13. package/dist/alphalib/types/builtinTemplates.d.ts +15 -3
  14. package/dist/alphalib/types/builtinTemplates.d.ts.map +1 -1
  15. package/dist/alphalib/types/builtinTemplates.js +23 -3
  16. package/dist/alphalib/types/builtinTemplates.js.map +1 -1
  17. package/dist/alphalib/types/robots/_instructions-primitives.d.ts +6 -0
  18. package/dist/alphalib/types/robots/_instructions-primitives.d.ts.map +1 -1
  19. package/dist/alphalib/types/robots/_instructions-primitives.js +29 -7
  20. package/dist/alphalib/types/robots/_instructions-primitives.js.map +1 -1
  21. package/dist/alphalib/types/robots/_video-generate.d.ts +34 -0
  22. package/dist/alphalib/types/robots/_video-generate.d.ts.map +1 -0
  23. package/dist/alphalib/types/robots/_video-generate.js +157 -0
  24. package/dist/alphalib/types/robots/_video-generate.js.map +1 -0
  25. package/dist/alphalib/types/robots/ai-chat.d.ts +2 -0
  26. package/dist/alphalib/types/robots/ai-chat.d.ts.map +1 -1
  27. package/dist/alphalib/types/robots/ai-chat.js +7 -5
  28. package/dist/alphalib/types/robots/ai-chat.js.map +1 -1
  29. package/dist/alphalib/types/robots/audio-concat.d.ts.map +1 -1
  30. package/dist/alphalib/types/robots/audio-concat.js +5 -5
  31. package/dist/alphalib/types/robots/audio-concat.js.map +1 -1
  32. package/dist/alphalib/types/robots/azure-import.d.ts.map +1 -1
  33. package/dist/alphalib/types/robots/azure-import.js +3 -3
  34. package/dist/alphalib/types/robots/azure-import.js.map +1 -1
  35. package/dist/alphalib/types/robots/document-convert.d.ts.map +1 -1
  36. package/dist/alphalib/types/robots/document-convert.js +7 -5
  37. package/dist/alphalib/types/robots/document-convert.js.map +1 -1
  38. package/dist/alphalib/types/robots/document-extract.d.ts +8 -8
  39. package/dist/alphalib/types/robots/document-extract.d.ts.map +1 -1
  40. package/dist/alphalib/types/robots/document-extract.js +18 -2
  41. package/dist/alphalib/types/robots/document-extract.js.map +1 -1
  42. package/dist/alphalib/types/robots/document-split.d.ts +12 -0
  43. package/dist/alphalib/types/robots/document-split.d.ts.map +1 -1
  44. package/dist/alphalib/types/robots/document-split.js +15 -2
  45. package/dist/alphalib/types/robots/document-split.js.map +1 -1
  46. package/dist/alphalib/types/robots/file-decompress.d.ts.map +1 -1
  47. package/dist/alphalib/types/robots/file-decompress.js +2 -0
  48. package/dist/alphalib/types/robots/file-decompress.js.map +1 -1
  49. package/dist/alphalib/types/robots/file-preview.d.ts +22 -0
  50. package/dist/alphalib/types/robots/file-preview.d.ts.map +1 -1
  51. package/dist/alphalib/types/robots/file-preview.js +43 -26
  52. package/dist/alphalib/types/robots/file-preview.js.map +1 -1
  53. package/dist/alphalib/types/robots/http-request.d.ts.map +1 -1
  54. package/dist/alphalib/types/robots/http-request.js +9 -14
  55. package/dist/alphalib/types/robots/http-request.js.map +1 -1
  56. package/dist/alphalib/types/robots/image-enhance.d.ts +8 -8
  57. package/dist/alphalib/types/robots/image-enhance.js +2 -2
  58. package/dist/alphalib/types/robots/image-resize.d.ts.map +1 -1
  59. package/dist/alphalib/types/robots/image-resize.js +3 -1
  60. package/dist/alphalib/types/robots/image-resize.js.map +1 -1
  61. package/dist/alphalib/types/robots/image-upscale.js +1 -1
  62. package/dist/alphalib/types/robots/image-upscale.js.map +1 -1
  63. package/dist/alphalib/types/robots/s3-import.d.ts.map +1 -1
  64. package/dist/alphalib/types/robots/s3-import.js +14 -15
  65. package/dist/alphalib/types/robots/s3-import.js.map +1 -1
  66. package/dist/alphalib/types/robots/supabase-store.d.ts +16 -6
  67. package/dist/alphalib/types/robots/supabase-store.d.ts.map +1 -1
  68. package/dist/alphalib/types/robots/supabase-store.js +23 -1
  69. package/dist/alphalib/types/robots/supabase-store.js.map +1 -1
  70. package/dist/alphalib/types/robots/tigris-store.d.ts.map +1 -1
  71. package/dist/alphalib/types/robots/tigris-store.js +3 -4
  72. package/dist/alphalib/types/robots/tigris-store.js.map +1 -1
  73. package/dist/alphalib/types/robots/transloadit-import.d.ts +2 -2
  74. package/dist/alphalib/types/robots/transloadit-import.d.ts.map +1 -1
  75. package/dist/alphalib/types/robots/transloadit-import.js +4 -1
  76. package/dist/alphalib/types/robots/transloadit-import.js.map +1 -1
  77. package/dist/alphalib/types/robots/transloadit-store.d.ts +2 -3
  78. package/dist/alphalib/types/robots/transloadit-store.d.ts.map +1 -1
  79. package/dist/alphalib/types/robots/transloadit-store.js +19 -0
  80. package/dist/alphalib/types/robots/transloadit-store.js.map +1 -1
  81. package/dist/alphalib/types/robots/video-adaptive.d.ts.map +1 -1
  82. package/dist/alphalib/types/robots/video-adaptive.js +12 -0
  83. package/dist/alphalib/types/robots/video-adaptive.js.map +1 -1
  84. package/dist/alphalib/types/robots/video-concat.d.ts.map +1 -1
  85. package/dist/alphalib/types/robots/video-concat.js +9 -9
  86. package/dist/alphalib/types/robots/video-concat.js.map +1 -1
  87. package/dist/alphalib/types/robots/video-thumbs.d.ts +8 -8
  88. package/dist/alphalib/types/robots/video-thumbs.js +8 -8
  89. package/dist/alphalib/types/robots/video-thumbs.js.map +1 -1
  90. package/dist/alphalib/types/robots/vimeo-store.js +1 -1
  91. package/dist/alphalib/types/template.d.ts +3 -3
  92. package/dist/alphalib/types/templateValues.d.ts +83 -0
  93. package/dist/alphalib/types/templateValues.d.ts.map +1 -0
  94. package/dist/alphalib/types/templateValues.js +117 -0
  95. package/dist/alphalib/types/templateValues.js.map +1 -0
  96. package/dist/cli/OutputCtl.d.ts +1 -0
  97. package/dist/cli/OutputCtl.d.ts.map +1 -1
  98. package/dist/cli/OutputCtl.js +1 -1
  99. package/dist/cli/OutputCtl.js.map +1 -1
  100. package/dist/cli/commands/BaseCommand.d.ts +3 -2
  101. package/dist/cli/commands/BaseCommand.d.ts.map +1 -1
  102. package/dist/cli/commands/BaseCommand.js +27 -3
  103. package/dist/cli/commands/BaseCommand.js.map +1 -1
  104. package/dist/cli/commands/assemblies.d.ts +21 -1
  105. package/dist/cli/commands/assemblies.d.ts.map +1 -1
  106. package/dist/cli/commands/assemblies.js +584 -91
  107. package/dist/cli/commands/assemblies.js.map +1 -1
  108. package/dist/cli/commands/templates.d.ts +1 -1
  109. package/dist/cli/commands/templates.d.ts.map +1 -1
  110. package/dist/cli/commands/templates.js +57 -67
  111. package/dist/cli/commands/templates.js.map +1 -1
  112. package/dist/cli/errors.d.ts +8 -0
  113. package/dist/cli/errors.d.ts.map +1 -0
  114. package/dist/cli/errors.js +28 -0
  115. package/dist/cli/errors.js.map +1 -0
  116. package/dist/cli/helpers.d.ts.map +1 -1
  117. package/dist/cli/helpers.js +22 -0
  118. package/dist/cli/helpers.js.map +1 -1
  119. package/dist/cli/intentRuntime.d.ts +1 -0
  120. package/dist/cli/intentRuntime.d.ts.map +1 -1
  121. package/dist/cli/intentRuntime.js +9 -3
  122. package/dist/cli/intentRuntime.js.map +1 -1
  123. package/dist/cli/resultFiles.d.ts +1 -0
  124. package/dist/cli/resultFiles.d.ts.map +1 -1
  125. package/dist/cli/resultFiles.js.map +1 -1
  126. package/dist/cli/resultUrls.d.ts +2 -1
  127. package/dist/cli/resultUrls.d.ts.map +1 -1
  128. package/dist/cli/resultUrls.js +6 -3
  129. package/dist/cli/resultUrls.js.map +1 -1
  130. package/dist/cli/stepsInput.d.ts +1 -1
  131. package/dist/cli/stepsInput.d.ts.map +1 -1
  132. package/dist/cli/stepsInput.js +48 -6
  133. package/dist/cli/stepsInput.js.map +1 -1
  134. package/dist/cli/types.d.ts +66 -0
  135. package/dist/cli/types.d.ts.map +1 -1
  136. package/dist/cli/types.js +29 -0
  137. package/dist/cli/types.js.map +1 -1
  138. package/dist/tus.d.ts +7 -2
  139. package/dist/tus.d.ts.map +1 -1
  140. package/dist/tus.js +58 -4
  141. package/dist/tus.js.map +1 -1
  142. package/package.json +1 -1
  143. package/src/Transloadit.ts +65 -4
  144. package/src/alphalib/renderSimpleValue.ts +120 -0
  145. package/src/alphalib/truncateString.ts +23 -0
  146. package/src/alphalib/types/builtinTemplates.ts +33 -3
  147. package/src/alphalib/types/robots/_instructions-primitives.ts +34 -7
  148. package/src/alphalib/types/robots/_video-generate.ts +211 -0
  149. package/src/alphalib/types/robots/ai-chat.ts +8 -5
  150. package/src/alphalib/types/robots/audio-concat.ts +5 -4
  151. package/src/alphalib/types/robots/azure-import.ts +3 -4
  152. package/src/alphalib/types/robots/document-convert.ts +8 -5
  153. package/src/alphalib/types/robots/document-extract.ts +18 -2
  154. package/src/alphalib/types/robots/document-split.ts +17 -2
  155. package/src/alphalib/types/robots/file-decompress.ts +2 -0
  156. package/src/alphalib/types/robots/file-preview.ts +44 -25
  157. package/src/alphalib/types/robots/http-request.ts +9 -13
  158. package/src/alphalib/types/robots/image-enhance.ts +2 -2
  159. package/src/alphalib/types/robots/image-resize.ts +3 -1
  160. package/src/alphalib/types/robots/image-upscale.ts +1 -1
  161. package/src/alphalib/types/robots/s3-import.ts +14 -15
  162. package/src/alphalib/types/robots/supabase-store.ts +23 -1
  163. package/src/alphalib/types/robots/tigris-store.ts +2 -3
  164. package/src/alphalib/types/robots/transloadit-import.ts +4 -1
  165. package/src/alphalib/types/robots/transloadit-store.ts +19 -0
  166. package/src/alphalib/types/robots/video-adaptive.ts +12 -0
  167. package/src/alphalib/types/robots/video-concat.ts +9 -8
  168. package/src/alphalib/types/robots/video-thumbs.ts +8 -8
  169. package/src/alphalib/types/robots/vimeo-store.ts +1 -1
  170. package/src/alphalib/types/templateValues.ts +157 -0
  171. package/src/cli/OutputCtl.ts +2 -1
  172. package/src/cli/commands/BaseCommand.ts +29 -7
  173. package/src/cli/commands/assemblies.ts +775 -118
  174. package/src/cli/commands/templates.ts +93 -98
  175. package/src/cli/errors.ts +34 -0
  176. package/src/cli/helpers.ts +21 -0
  177. package/src/cli/intentRuntime.ts +10 -3
  178. package/src/cli/resultFiles.ts +1 -0
  179. package/src/cli/resultUrls.ts +11 -3
  180. package/src/cli/stepsInput.ts +54 -6
  181. package/src/cli/types.ts +35 -0
  182. package/src/tus.ts +72 -5
@@ -12,6 +12,7 @@ import {
12
12
  complexWidthSchema,
13
13
  createProcessingExample,
14
14
  defineRobot,
15
+ imageQualityValueSchema,
15
16
  optimize_priority,
16
17
  resize_strategy,
17
18
  robotBase,
@@ -19,6 +20,17 @@ import {
19
20
  robotUse,
20
21
  } from './_instructions-primitives.ts'
21
22
 
23
+ /** Fallback order shared by schema defaults, published examples and worker normalization. */
24
+ export const filePreviewStrategyDefaults = {
25
+ archive: ['icon'],
26
+ audio: ['artwork', 'waveform', 'icon'],
27
+ document: ['page', 'icon'],
28
+ image: ['image', 'embedded-preview', 'icon'],
29
+ unknown: ['icon'],
30
+ video: ['artwork', 'frame', 'icon'],
31
+ webpage: ['render', 'icon'],
32
+ }
33
+
22
34
  export const meta: RobotMetaInput = {
23
35
  ...robotMediaCatalogingMeta,
24
36
  example_code: createProcessingExample('previewed', '/file/preview', {
@@ -44,17 +56,20 @@ export const robotFilePreviewInstructionsSchema = robotBase
44
56
  .merge(robotUse)
45
57
  .extend({
46
58
  robot: z.literal('/file/preview').describe(`
47
- This <dfn>Robot</dfn>'s purpose is to generate a meaningful preview image for any file, in such a way that the resulting thumbnail highlights the file's content. The goal is not to losslessly present the original media in a smaller way. Instead, it is to maximize the chance of a person recognizing the media at a glance, while being visually pleasing and consistent with other previews. The generation process depends on the file type. For example, the <dfn>Robot</dfn> can extract artwork from media files, frames from videos, generate a waveform for audio files, and preview the content of documents and images. The details of all available strategies are provided in the next section.
59
+ This <dfn>Robot</dfn>'s purpose is to generate a meaningful preview image for any file, in such a way that the resulting thumbnail highlights the file's content. The goal is not to losslessly present the original media in a smaller way. Instead, it is to maximize the chance of a person recognizing the media at a glance, while being visually pleasing and consistent with other previews. The generation process depends on the file type. For example, the <dfn>Robot</dfn> can extract artwork from media files, frames from videos, generate a waveform for audio files, and preview the content of documents and images. The [\`strategy\` parameter](#strategy) lists the available strategies and controls their fallback order.
48
60
 
49
- If no file-specific thumbnail can be generated because the file type is not supported, a generic icon containing the file extension will be generated.
61
+ With the default strategy configuration, a generic icon is the fallback when no file-specific thumbnail can be generated. The default \`with-text\` icon style includes the file extension; \`icon_style\` and \`icon_text_content\` control its appearance.
50
62
 
51
- The default parameters ensure that the <dfn>Robot</dfn> always generates a preview image with the predefined dimensions and formats, to allow an easy integration into your application's UI. In addition, the generated preview images are optimized by default to reduce their file size while keeping their quality.
63
+ The default parameters produce still previews at the configured dimensions and format for integration into your application's UI. The optional \`clip\` strategy produces animated previews using \`clip_format\`. Preview images are optimized by default to reduce their file size while maintaining visual quality.
52
64
  `),
53
65
  format: z
54
66
  .enum(['avif', 'gif', 'jpeg', 'jpg', 'png', 'webp'])
55
67
  .default('png')
56
68
  .describe(`
57
- The output format for the generated thumbnail image. AVIF and WebP are also supported. If a short video clip is generated using the \`clip\` strategy, its format is defined by \`clip_format\`.
69
+ The output format for the generated thumbnail image. If a short video clip is generated using the \`clip\` strategy, its format is defined by \`clip_format\`.
70
+ `),
71
+ quality: imageQualityValueSchema.optional().describe(`
72
+ Compression quality for still previews, from 1 to 100. When specified, the final image resize encodes directly into the requested format at this quality and strips embedded metadata. Omit it to retain the default encoding behavior. Animated clips use their own encoding settings. If \`optimize\` is enabled, optimization runs after this encoding.
58
73
  `),
59
74
  width: complexWidthSchema.default(300).describe(`
60
75
  Width of the thumbnail, in pixels.
@@ -80,32 +95,36 @@ Note that this parameter only affects strategies that produce a still image. Ani
80
95
  `),
81
96
  strategy: z
82
97
  .object({
83
- archive: z.array(z.string()).default(['icon']),
84
- audio: z.array(z.string()).default(['artwork', 'waveform', 'icon']),
85
- document: z.array(z.string()).default(['page', 'icon']),
86
- image: z.array(z.string()).default(['image', 'icon']),
87
- unknown: z.array(z.string()).default(['icon']),
88
- video: z.array(z.string()).default(['artwork', 'frame', 'icon']),
89
- webpage: z.array(z.string()).default(['render', 'icon']),
98
+ archive: z.array(z.string()).default(filePreviewStrategyDefaults.archive),
99
+ audio: z.array(z.string()).default(filePreviewStrategyDefaults.audio),
100
+ document: z.array(z.string()).default(filePreviewStrategyDefaults.document),
101
+ image: z.array(z.string()).default(filePreviewStrategyDefaults.image),
102
+ unknown: z.array(z.string()).default(filePreviewStrategyDefaults.unknown),
103
+ video: z.array(z.string()).default(filePreviewStrategyDefaults.video),
104
+ webpage: z.array(z.string()).default(filePreviewStrategyDefaults.webpage),
90
105
  })
91
106
  .optional()
92
107
  .describe(`
93
- Definition of the thumbnail generation process per file category. The parameter must be an object whose keys can be one of the file categories: \`audio\`, \`video\`, \`image\`, \`document\`, \`archive\`, \`webpage\`, and \`unknown\`. The corresponding value is an array of strategies for the specific file category. See the above section for a list of all available strategies.
108
+ Definition of the thumbnail generation process per file category. Each key selects a file category and its value is an ordered array of strategies:
109
+
110
+ | File category | Available strategies |
111
+ |---------------|----------------------|
112
+ | \`audio\` | \`artwork\`, \`waveform\`, \`icon\` |
113
+ | \`video\` | \`artwork\`, \`frame\`, \`clip\`, \`icon\` |
114
+ | \`image\` | \`image\`, \`embedded-preview\`, \`icon\` |
115
+ | \`document\` | \`page\`, \`icon\` |
116
+ | \`archive\` | \`icon\` |
117
+ | \`webpage\` | \`render\`, \`icon\` |
118
+ | \`unknown\` | \`icon\` |
119
+
120
+ \`artwork\` extracts embedded artwork, \`waveform\` draws an audio waveform, \`frame\` extracts a video frame, and \`clip\` creates an animated video preview. For images, \`image\` resizes the original and \`embedded-preview\` extracts an embedded preview when available. \`page\` renders the first document page, \`render\` captures a webpage, and \`icon\` generates a file-type icon.
94
121
 
95
122
  For each file, the <dfn>Robot</dfn> will attempt to use the first strategy to generate the thumbnail. If this process fails (e.g., because no artwork is available in a video file), the next strategy is attempted. This is repeated until either a thumbnail is generated or the list is exhausted. Selecting the \`icon\` strategy as the last entry provides a fallback mechanism to ensure that an appropriate strategy is always available.
96
123
 
97
124
  The parameter defaults to the following definition:
98
125
 
99
126
  \`\`\`json
100
- {
101
- "audio": ["artwork", "waveform", "icon"],
102
- "video": ["artwork", "frame", "icon"],
103
- "document": ["page", "icon"],
104
- "image": ["image", "icon"],
105
- "webpage": ["render", "icon"],
106
- "archive": ["icon"],
107
- "unknown": ["icon"]
108
- }
127
+ ${JSON.stringify(filePreviewStrategyDefaults, null, 2)}
109
128
  \`\`\`
110
129
  `),
111
130
  artwork_outer_color: color_with_alpha.optional().describe(`
@@ -163,13 +182,13 @@ The font family of the text used in the icon. Only used if the \`icon\` strategy
163
182
  .enum(['extension', 'none'])
164
183
  .default('extension')
165
184
  .describe(`
166
- The content of the text box in generated icons. Only used if the \`icon_style\` parameter is set to \`with-text\`. The default value, \`extension\`, adds the file extension (e.g. MP4, JPEG) to the icon. The value \`none\` can be used to render an empty text box, which is useful if no text should not be included in the raster image, but some place should be reserved in the image for later overlaying custom text over the image using HTML etc.
185
+ The content of the text box in generated icons. Only used if the \`icon_style\` parameter is set to \`with-text\`. The default value, \`extension\`, adds the file extension (e.g. MP4, JPEG) to the icon. The value \`none\` leaves the text box empty while reserving space for a custom text overlay, for example using HTML in your application.
167
186
  `),
168
187
  optimize: z
169
188
  .boolean()
170
189
  .default(true)
171
190
  .describe(`
172
- Specifies whether the generated preview image should be optimized to reduce the image's file size while keeping their quaility. If enabled, the images will be optimized using [🤖/image/optimize](/docs/robots/image-optimize/).
191
+ Specifies whether the generated preview image should be optimized to reduce its file size while maintaining visual quality. If enabled, the image will be optimized using [🤖/image/optimize](/docs/robots/image-optimize/).
173
192
  `),
174
193
  optimize_priority: optimize_priority.default('conversion-speed').describe(`
175
194
  Specifies whether conversion speed or compression ratio is prioritized when optimizing images. Only used if \`optimize\` is enabled. Please see the [🤖/image/optimize documentation](/docs/robots/image-optimize/#priority) for more details.
@@ -186,14 +205,14 @@ Specifies whether images should be interlaced, which makes the result image load
186
205
  .describe(`
187
206
  The animated image format for the generated video clip. Only used if the \`clip\` strategy for video files is applied.
188
207
 
189
- Please consult the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) for detailed information about the image formats and their characteristics. GIF enjoys the broadest support in software, but only supports a limit color palette. APNG supports a variety of color depths, but its lossless compression produces large images for videos. AVIF is a modern image format that offers great compression, but proper support for animations is still lacking in some browsers. WebP on the other hand, enjoys broad support while offering a great balance between small file sizes and good visual quality, making it the default clip format.
208
+ Please consult the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) for detailed information about the image formats and their characteristics. GIF enjoys broad support in software, but only supports a limited color palette. APNG supports a variety of color depths, but its lossless compression produces large images for videos. AVIF offers efficient compression, but animation support varies between browsers. WebP offers a balance between small file sizes and visual quality and is the default clip format.
190
209
  `),
191
210
  clip_offset: z
192
211
  .number()
193
212
  .min(0)
194
213
  .default(1)
195
214
  .describe(`
196
- The start position in seconds of where the clip is cut. Only used if the \`clip\` strategy for video files is applied. Be aware that for larger video only the first few MBs of the file may be imported to improve speed. Larger offsets may seek to a position outside of the imported part and thus fail to generate a clip.
215
+ The start position in seconds of where the clip is cut. Only used if the \`clip\` strategy for video files is applied. For large videos, only portions of the file may be imported to improve speed. An offset outside the available media data can prevent clip generation.
197
216
  `),
198
217
  clip_duration: z
199
218
  .number()
@@ -7,6 +7,7 @@ import type {
7
7
  import { z } from 'zod'
8
8
 
9
9
  import {
10
+ createProcessingExample,
10
11
  defineRobot,
11
12
  httpHeaderSafetyDescription,
12
13
  httpUrlSchema,
@@ -17,17 +18,11 @@ import {
17
18
 
18
19
  export const meta: RobotMetaInput = {
19
20
  ...robotCodeEvaluationMeta,
20
- example_code: {
21
- steps: {
22
- request: {
23
- robot: '/http/request',
24
- method: 'POST',
25
- payload: 'metadata',
26
- url: 'https://example.com/transloadit/files',
27
- use: [],
28
- },
29
- },
30
- },
21
+ example_code: createProcessingExample('request', '/http/request', {
22
+ method: 'POST',
23
+ payload: 'metadata',
24
+ url: 'https://example.com/transloadit/files',
25
+ }),
31
26
  example_code_description: 'Ask your application which input files should continue:',
32
27
  extended_description: `
33
28
  Use this <dfn>Robot</dfn> when your application should inspect the current Assembly state or input
@@ -56,7 +51,7 @@ in the Assembly Template and use this Step as their input.
56
51
  `,
57
52
  override_lvl1: 'Workflow Automation',
58
53
  purpose_sentence:
59
- 'calls your application during an Assembly and emits returned input file references',
54
+ 'calls your application during an Assembly and emits selected input files or downloads returned result-file URLs',
60
55
  purpose_verb: 'run',
61
56
  purpose_word: 'HTTP endpoint',
62
57
  purpose_words: 'Call HTTP endpoints',
@@ -100,7 +95,8 @@ Controls what is sent to your endpoint.
100
95
  - \`file\`: Send multipart form data with a \`payload\` JSON field and the first input file as a
101
96
  \`file\` part.
102
97
  - \`files\`: Send multipart form data with a \`payload\` JSON field and all input files as repeated
103
- \`files[]\` parts. Useful with \`bundle_steps\`.
98
+ \`files[]\` parts. To send several input files in one request, set \`bundle_steps: true\` in the
99
+ advanced [\`use\` options](/docs/topics/use-parameter/#step-bundling).
104
100
 
105
101
  The \`payload\` JSON object contains \`assembly\`, \`fields\`, \`previous_step\`, \`file\`, and
106
102
  \`files\`. \`file\` is the first input file’s metadata or \`null\`; \`files\` is an array of input
@@ -77,11 +77,11 @@ export const robotImageEnhanceInstructionsSchema = robotBase
77
77
  .merge(robotImagemagick)
78
78
  .extend({
79
79
  robot: z.literal('/image/enhance').describe(`
80
- With this <dfn>Robot</dfn> you can automatically enhance images with one click — adjusting levels, contrast, saturation, sharpness, and white balance to produce an optimally balanced image. It also supports a library of named photo filter presets (e.g. \`warm\`, \`cool\`, \`vintage\`, \`vivid\`) that apply curated color grading and tone adjustments.
80
+ With this <dfn>Robot</dfn> you can automatically enhance images by adjusting levels, gamma, and sharpness. The selected enhancement mode determines which adjustments are applied. It also supports named photo filter presets (e.g. \`warm\`, \`cool\`, \`vintage\`, \`vivid\`) for color grading and tone adjustments.
81
81
 
82
82
  It works well together with [🤖/image/resize](/docs/robots/image-resize/) — you can enhance first, then resize, or vice versa.
83
83
 
84
- This <dfn>Robot</dfn> accepts all image types supported by ImageMagick and passes unsupported types through unchanged. \`engine: "classic"\` uses \`enhance\`, \`preset\`, \`sharpen\`, and \`denoise\`, while \`engine: "ai"\` uses \`ai_preset\` and \`quality\`; classic controls are ignored in AI mode. Output keeps the original file extension when possible. In AI mode, if the target extension is not writable by the selected \`imagemagick_stack\`, the file is passed through unchanged.
84
+ This <dfn>Robot</dfn> accepts all image types supported by ImageMagick and passes unsupported image types through unchanged. \`engine: "classic"\` uses \`enhance\`, \`preset\`, \`sharpen\`, and \`denoise\`, while \`engine: "ai"\` uses \`ai_preset\`; classic controls are ignored in AI mode. Both engines use \`quality\` for the output image. Output keeps the original file extension when possible. In AI mode, if the target extension is not writable by the selected \`imagemagick_stack\`, the file is passed through unchanged.
85
85
  `),
86
86
  enhance: enhanceModeSchema.describe(`
87
87
  The auto-enhancement mode. \`"auto"\` applies balanced auto-levels, gamma correction, and subtle sharpening. \`"auto_gentle"\` is more conservative (good for already-decent photos). \`"auto_aggressive"\` applies stronger normalization and contrast. \`"none"\` skips auto-enhance (useful when only applying a preset filter).
@@ -443,7 +443,7 @@ Changes the hue by rotating the color of the image. The value \`100\` would prod
443
443
  .max(2)
444
444
  .default(1)
445
445
  .describe(`
446
- Adjusts the contrast of the image. A value of \`1\` produces no change. Values below \`1\` decrease contrast (with \`0\` being minimum contrast), and values above \`1\` increase contrast (with \`2\` being maximum contrast). This works like the \`brightness\` parameter.
446
+ Adjusts the contrast of the image. A value of \`1\` produces no change. Values below \`1\` decrease contrast, and values above \`1\` increase it. Start with small adjustments, such as \`0.9\` or \`1.1\`. Larger increases can clip shadows and highlights.
447
447
  `),
448
448
  watermark_url: httpUrlSchema.optional().describe(`
449
449
  A URL indicating a PNG image to be overlaid above this image. Please note that you can also [supply the watermark via another Assembly Step](/docs/topics/use-parameter/#supplying-the-watermark-via-an-assembly-step). With watermarking you can add an image onto another image. This is usually used for logos.
@@ -480,6 +480,8 @@ Values can be both positive and negative and yield different results depending o
480
480
  The size of the watermark, as a percentage.
481
481
 
482
482
  For example, a value of \`"50%"\` means that size of the watermark will be 50% of the size of image on which it is placed. The exact sizing depends on \`watermark_resize_strategy\`, too.
483
+
484
+ With \`watermark_repeat_x\` or \`watermark_repeat_y\`, this sizes each tile relative to the output image, before repeating it. It does not set the percentage of the image covered by the whole pattern. Smaller values produce more, smaller tiles. If omitted, the watermark keeps its original pixel dimensions.
483
485
  `),
484
486
  watermark_resize_strategy: z
485
487
  .enum(['area', 'fit', 'min_fit', 'stretch'])
@@ -70,7 +70,7 @@ export const robotImageUpscaleInstructionsSchema = robotBase
70
70
  face_enhance: upscaleFaceEnhanceSchema
71
71
  .optional()
72
72
  .describe(
73
- `Enable face enhancement for better face restoration. Defaults to ${upscaleFaceEnhanceSchema.parse(undefined)}.`,
73
+ `Enable face enhancement with Real-ESRGAN or face upsampling with CodeFormer. GFPGAN always performs face restoration and ignores this parameter. Defaults to ${upscaleFaceEnhanceSchema.parse(undefined)}.`,
74
74
  ),
75
75
  })
76
76
  .strict()
@@ -43,40 +43,39 @@ export const robotS3ImportInstructionsSchema = robotBase
43
43
  robot: z.literal('/s3/import').describe(`
44
44
  If you are new to Amazon S3, see our tutorial on [using your own S3 bucket](/docs/faq/how-to-set-up-an-amazon-s3-bucket/).
45
45
 
46
- The URL to the result file in your S3 bucket will be returned in the <dfn>Assembly Status JSON</dfn>.
47
-
48
- > [!Warning]
49
- > **Use DNS-compliant bucket names**. Your bucket name [must be DNS-compliant](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) and must not contain uppercase letters. Any non-alphanumeric characters in the file names will be replaced with an underscore, and spaces will be replaced with dashes. If your existing S3 bucket contains uppercase letters or is otherwise not DNS-compliant, rewrite the result URLs using the <dfn>Robot</dfn>’s \`url_prefix\` parameter.
50
-
51
46
  <span id="minimum-s3-iam-permissions" aria-hidden="true"></span>
52
47
 
53
48
  ## Limit access
54
49
 
55
- You will also need to add permissions to your bucket so that Transloadit can access it properly. Here is an example IAM policy that you can use. Following the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), it contains the **minimum required permissions** to import a file from your S3 bucket using Transloadit. You may require more permissions (especially viewing permissions) depending on your application.
50
+ Use dedicated AWS credentials in your <dfn>Template Credentials</dfn> and grant that IAM identity access only to the bucket and objects it needs. The following example is an **identity-based IAM policy** for reading current objects from one general purpose S3 bucket. Attach it to the IAM user or role whose credentials you supply to Transloadit, not to the bucket. An identity-based policy has no \`Principal\` element: it applies to the identity it is attached to.
56
51
 
57
- Please change \`{BUCKET_NAME}\` in the values for \`Sid\` and \`Resource\` accordingly. Also, this policy will grant the minimum required permissions to all your users. We advise you to create a separate Amazon IAM user, and use its User ARN (can be found in the "Summary" tab of a user [here](https://console.aws.amazon.com/iam/home#users)) for the \`Principal\` value. More information about this can be found [here](https://docs.aws.amazon.com/AmazonS3/latest/dev/AccessPolicyLanguage_UseCases_s3_a.html).
52
+ Replace \`{BUCKET_NAME}\` in both \`Resource\` values with your bucket name. The bucket-level statement permits listing objects and discovering the bucket's region; the object-level statement permits downloading them. See [AWS's S3 operation permissions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-with-s3-policy-actions.html) and [identity-based versus resource-based policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_identity-vs-resource.html).
58
53
 
59
54
  \`\`\`json
60
55
  {
61
56
  "Version": "2012-10-17",
62
57
  "Statement": [
63
58
  {
64
- "Sid": "AllowTransloaditToImportFilesIn{BUCKET_NAME}Bucket",
59
+ "Sid": "ReadBucketMetadata",
65
60
  "Effect": "Allow",
66
61
  "Action": ["s3:GetBucketLocation", "s3:ListBucket"],
67
- "Resource": ["arn:aws:s3:::{BUCKET_NAME}", "arn:aws:s3:::{BUCKET_NAME}/*"]
62
+ "Resource": "arn:aws:s3:::{BUCKET_NAME}"
63
+ },
64
+ {
65
+ "Sid": "ReadBucketObjects",
66
+ "Effect": "Allow",
67
+ "Action": "s3:GetObject",
68
+ "Resource": "arn:aws:s3:::{BUCKET_NAME}/*"
68
69
  }
69
70
  ]
70
71
  }
71
72
  \`\`\`
72
73
 
73
- The \`Sid\` value is just an identifier for you to recognize the rule later. You can name it anything you like.
74
-
75
- The policy needs to be separated into two parts, because the \`ListBucket\` action requires permissions on the bucket while the other actions require permissions on the objects in the bucket. When targeting the objects there's a trailing slash and an asterisk in the \`Resource\` parameter, whereas when the policy targets the bucket, the slash and the asterisk are omitted.
74
+ The \`Sid\` values are labels for recognizing statements. Supplying the correct \`bucket_region\` avoids automatic region discovery and its \`s3:GetBucketLocation\` permission. The example still grants bucket-wide listing and reads: narrow its object resources and listing conditions when your workflow needs only a specific prefix.
76
75
 
77
- In order to build proper result URLs we need to know the region in which your S3 bucket resides. For this we require the \`GetBucketLocation\` permission. Figuring out your bucket's region this way will also slow down your Assemblies. To make this much faster and to also not require the \`GetBucketLocation\` permission, we have added the \`bucket_region\` parameter to the /s3/store and /s3/import Robots. We recommend using them at all times.
76
+ This example is not a guarantee of access. Explicit denies, organization policies, permission boundaries, cross-account bucket policies, and object ownership can affect the result. For cross-account access, review [AWS's policy evaluation guidance](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic-cross-account.html).
78
77
 
79
- Please keep in mind that if you use bucket encryption you may also need to add \`"sts:*"\` and \`"kms:*"\` to the bucket policy. Please read [here](https://docs.aws.amazon.com/kms/latest/developerguide/kms-api-permissions-reference.html) and [here](https://aws.amazon.com/blogs/security/how-to-restrict-amazon-s3-bucket-access-to-a-specific-iam-role/) in case you run into trouble with our example bucket policy.
78
+ For SSE-KMS-encrypted objects, also configure the IAM identity and KMS key policy with the key permissions required by [AWS's GetObject documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html), scoped to the intended key. KMS actions belong in IAM identity policies and KMS key policies, **not S3 bucket policies**. Do not grant broad \`sts:*\` or \`kms:*\` permissions as a workaround.
80
79
  `),
81
80
  path: path.describe(`
82
81
  The path in your bucket to the specific file or directory. If the path points to a file, only this file will be imported. For example: \`images/avatar.jpg\`.
@@ -85,7 +84,7 @@ If it points to a directory, indicated by a trailing slash (\`/\`), then all fil
85
84
 
86
85
  Directories are **not** imported recursively. If you want to import files from subdirectories and sub-subdirectories, enable the \`recursive\` parameter.
87
86
 
88
- If you want to import all files from the root directory, please use \`/\` as the value here. In this case, make sure all your objects belong to a path. If you have objects in the root of your bucket that aren't prefixed with \`/\`, you'll receive an error: \`A client error (NoSuchKey) occurred when calling the GetObject operation: The specified key does not exist.\`
87
+ Use \`/\` to import files from the bucket root. Object keys do not need a leading slash. Enable \`recursive\` to include files under nested prefixes.
89
88
 
90
89
  You can also use an array of path strings here to import multiple paths in the same <dfn>Robot</dfn>'s <dfn>Step</dfn>.
91
90
  `),
@@ -33,8 +33,30 @@ export const robotSupabaseStoreInstructionsSchema = robotBase
33
33
  .merge(robotUse)
34
34
  .merge(supabaseBase)
35
35
  .extend({
36
- robot: z.literal('/supabase/store'),
36
+ robot: z.literal('/supabase/store').describe(`
37
+ ## Upload as a Supabase user
38
+
39
+ For [Supabase session-token authentication](https://supabase.com/docs/guides/storage/s3/authentication#session-token), supply your project ref as \`key\`, your project’s anon key as \`secret\`, and the user’s access-token JWT as \`session_token\`. Supabase applies the authenticated user’s Row Level Security (RLS) policies to these uploads. Choose a \`path\` those policies permit, and use the endpoint and region from your project’s S3 connection settings.
40
+
41
+ Send the user’s JWT with each Assembly from your trusted backend. It must remain valid through queuing, processing, and upload retries; the Robot does not refresh it. Without \`session_token\`, continue using your generated Supabase S3 access key and secret. Those generated S3 access keys bypass RLS.
42
+
43
+ Supply \`session_token\` inline in the Step. Assembly Status returns \`fields\` unchanged, so passing the JWT through Assembly \`fields\` exposes it in the status response.
44
+
45
+ The result URL uses Supabase’s public-object URL format. It does not grant read access to a private bucket; use Supabase’s download authorization for private objects.
46
+ `),
47
+ credentials: supabaseBase.shape.credentials.describe(`
48
+ The name of your Supabase <dfn>Template Credentials</dfn>, containing the bucket, host, key, secret, and bucket region. You can instead supply those parameters directly in the Step.
49
+
50
+ For generated S3 access-key authentication, use the endpoint and S3 access keys from your Supabase project’s S3 connection settings. For user-scoped session-token authentication, use the project ref as \`key\` and the project’s anon key as \`secret\`, then supply the user’s access-token JWT as \`session_token\` with each Assembly.
51
+ `),
37
52
  path: storeFilePath,
53
+ session_token: z
54
+ .string()
55
+ .min(1)
56
+ .optional()
57
+ .describe(`
58
+ The user’s Supabase access-token JWT for [session-token authentication](https://supabase.com/docs/guides/storage/s3/authentication#session-token). Use the Supabase project ref as \`key\` and the project’s anon key as \`secret\` so Supabase applies that user’s RLS policies. The JWT must remain valid through queuing, processing, and upload retries; the Robot does not refresh it. Supply this value directly in the Step; Assembly Status exposes values in \`fields\`.
59
+ `),
38
60
  headers: z
39
61
  .record(z.string())
40
62
  .default({ 'Content-Type': '${file.mime}' })
@@ -33,9 +33,7 @@ export const robotTigrisStoreInstructionsSchema = robotBase
33
33
  .merge(robotUse)
34
34
  .merge(tigrisBase)
35
35
  .extend({
36
- robot: z.literal('/tigris/store').describe(`
37
- The URL to the result file will be returned in the <dfn>Assembly Status JSON</dfn>.
38
- `),
36
+ robot: z.literal('/tigris/store'),
39
37
  path: storeFilePath,
40
38
  acl: storageAcl,
41
39
  headers: z
@@ -62,6 +60,7 @@ If this parameter is not used, no URL signing is done.
62
60
  .describe('The region of your Tigris bucket. This is optional as it can often be derived.'),
63
61
  })
64
62
  .strict()
63
+ .describe('The URL to the result file will be returned in the <dfn>Assembly Status JSON</dfn>.')
65
64
 
66
65
  export const robotDefinition: RobotDefinition<typeof robotTigrisStoreInstructionsSchema.shape> =
67
66
  defineRobot(meta, robotTigrisStoreInstructionsSchema)
@@ -40,12 +40,15 @@ Whether to import files from subfolders and sub-subfolders when \`path\` is a fo
40
40
  only the folder's own files are imported.
41
41
  `
42
42
 
43
- /** Storage imports require proof of possession independently of Workspace signature settings. */
43
+ /** Private Storage imports require proof of possession independently of Workspace settings. */
44
44
  export const transloaditImportAuthenticationGuidanceLines = [
45
45
  'Assemblies that use `/transloadit/import`, directly or through a Template, require',
46
46
  'either a bearer token or signed `params` with a future `params.auth.expires` timestamp.',
47
47
  'This applies even when Workspace Signature Authentication is disabled. An Auth Key alone',
48
48
  'is not sufficient; bearer-authenticated requests do not need a separate signature or expiry.',
49
+ 'Public Storage Builtin Templates are the exception: they can serve unsigned requests only',
50
+ 'when every Storage import remains within a declared public prefix. This does not authorize',
51
+ 'unsigned Assemblies with arbitrary Steps or access to private Storage paths.',
49
52
  ] as const
50
53
 
51
54
  export const meta: RobotMetaInput = {
@@ -64,6 +64,25 @@ existing asset. Always save the returned asset identity, version and final path.
64
64
  Sets the destination path inside your Transloadit Storage workspace, relative to its root: a
65
65
  filename, or folders and a filename such as \`website/hero.jpg\`. Folders that do not exist yet
66
66
  are created.
67
+
68
+ Repeated interior slashes are collapsed after variable interpolation. For example,
69
+ \`a/\${file.meta.relative_path}/\${file.name}\` stores a root-level HLS playlist as
70
+ \`a/playlist.m3u8\` when \`relative_path\` is empty. Leading or trailing slashes and
71
+ \`.\` or \`..\` segments remain invalid.
72
+ `),
73
+ result: z
74
+ .boolean()
75
+ .default(true)
76
+ .describe(`
77
+ Leave \`result\` unset or set it to \`true\` on this Storage Step. Setting \`result: false\`
78
+ here can omit stored entries, including asset and version IDs, from the Assembly Status JSON
79
+ even though the files are stored.
80
+
81
+ Stored results appear under their producing Step in the Assembly Status JSON, or under
82
+ \`:original\` for uploaded files, rather than under this Storage Step. Setting \`result: false\`
83
+ on a producing processing Step also omits its stored entries, including asset and version IDs,
84
+ from the Assembly Status JSON even though the files are stored. Leave \`result\` unset or set it
85
+ to \`true\` on that Step to receive those identities.
67
86
  `),
68
87
  })
69
88
  .strict()
@@ -71,6 +71,18 @@ This <dfn>Robot</dfn> accepts all types of video files and audio files. Do not f
71
71
 
72
72
  This <dfn>Robot</dfn> is normally used in combination with [🤖/video/encode](/docs/robots/video-encode/). We have implemented video and audio encoding presets specifically for MPEG-Dash and HTTP Live Streaming support. These presets are prefixed with \`"dash/"\` and \`"hls/"\`. [View a HTTP Live Streaming demo here](/demos/video-encoding/implement-http-live-streaming/).
73
73
 
74
+ ### HLS subtitles
75
+
76
+ With \`"technique": "hls"\`, the first embedded subtitle track of each input is converted to segmented WebVTT and linked from the master playlist. Additional subtitle tracks in the same input are dropped. The Assembly adds a warning when any input contains more than one subtitle track.
77
+
78
+ To add a track before packaging, use [🤖/video/subtitle](/docs/robots/video-subtitle/) with \`"subtitles_type": "external"\`, \`"preset": "empty"\`, and a \`language\` such as \`"eng"\`. Then encode with an \`"hls/"\` preset and pass the encoded video to this Robot. By default, the playlist’s subtitle \`NAME\` uses the language’s English name, for example “English”; its \`LANGUAGE\` preserves the input’s language tag. Unrecognized language codes are used as the name. If the language is missing or \`"und"\`, the name is “Unknown”. Custom \`ffmpeg.var_stream_map\` mappings control their own subtitle labels.
79
+
80
+ To set an explicit subtitle name in a custom \`ffmpeg.var_stream_map\`, select \`"ffmpeg_stack": "v8.0.0"\` and use its \`sname\` option.
81
+
82
+ WebVTT cues and MPEG-TS video use the same timestamp timeline. AAC priming or video frame reordering can move both after zero, so a small nonzero first cue timestamp does not by itself mean subtitles are delayed. Preserve the generated timestamps to keep subtitles synchronized with the video. When any input to an HLS Step has subtitles, \`ffmpeg.muxdelay\` defaults to \`0\` for the whole Step, including renditions without subtitles. Overriding it or other timestamp options can change that alignment.
83
+
84
+ Store the WebVTT segments and subtitle playlists along with all other results, preserving \`\${file.meta.relative_path}\` as described below.
85
+
74
86
  ### CMAF aspect ratios
75
87
 
76
88
  CMAF aligns near-equal rendition aspect ratios, within 1% of the largest rendition, without re-encoding or changing pixel dimensions. This lets presets such as \`"hls/480p"\` and \`"hls/720p"\` support bitrate switching together.
@@ -8,6 +8,7 @@ import { z } from 'zod'
8
8
 
9
9
  import {
10
10
  defineRobot,
11
+ getConcatFadeDescription,
11
12
  inputSortBySchema,
12
13
  robotBase,
13
14
  robotFFmpegVideo,
@@ -67,23 +68,23 @@ It’s possible to concatenate a virtually infinite number of video files using
67
68
  `),
68
69
  video_fade_seconds: z
69
70
  .number()
70
- .default(1)
71
+ .default(0)
71
72
  .describe(`
72
- When used this adds a video fade in and out effect between each section of your concatenated video. The float value is used so if you want a video delay effect of 500 milliseconds between each video section you would select \`0.5\`, however, integer values can also be represented.
73
+ ${getConcatFadeDescription('video')}
73
74
 
74
- This parameter does not add a video fade effect at the beginning or end of your video. If you want to do so, create an additional [🤖/video/encode](/docs/robots/video-encode/) Step and use our \`ffmpeg\` parameter as shown in this [demo](/demos/video-encoding/concatenate-fade-effect/).
75
+ This parameter only applies when \`transition\` is \`"none"\`. It is ignored when \`transition\` is \`"crossfade"\` or \`"fade_to_black"\`; use \`transition_duration\` for those effects. Audio fades are controlled separately by \`audio_fade_seconds\`.
75
76
 
76
- Please note this parameter is independent of adding audio fades between sections.
77
+ To add a fade-in at the beginning of the output, use an additional [video encoding Step](/docs/robots/video-encode/) with the \`ffmpeg\` parameter, as shown in the [video fade demo](/demos/video-encoding/concatenate-fade-effect/).
77
78
  `),
78
79
  audio_fade_seconds: z
79
80
  .number()
80
- .default(1)
81
+ .default(0)
81
82
  .describe(`
82
- When used this adds an audio fade in and out effect between each section of your concatenated video. The float value is used so if you want an audio delay effect of 500 milliseconds between each video section you would select \`0.5\`, however, integer values can also be represented.
83
+ ${getConcatFadeDescription('audio')}
83
84
 
84
- This parameter does not add an audio fade effect at the beginning or end of your video. If you want to do so, create an additional [🤖/video/encode](/docs/robots/video-encode/) Step and use our \`ffmpeg\` parameter as shown in this [demo](/demos/audio-encoding/ffmpeg-fade-in-and-out/).
85
+ This parameter only applies when \`transition\` is \`"none"\`. It is ignored when \`transition\` is \`"crossfade"\` or \`"fade_to_black"\`; use \`transition_duration\` for those effects. Video fades are controlled separately by \`video_fade_seconds\`.
85
86
 
86
- Please note this parameter is independent of adding video fades between sections.
87
+ To add an audio fade-in at the beginning of the output, use an additional [video encoding Step](/docs/robots/video-encode/) with the \`ffmpeg\` parameter. The [audio fade demo](/demos/audio-encoding/ffmpeg-fade-in-and-out/) shows the corresponding FFmpeg audio filter.
87
88
  `),
88
89
  chapter_markers: z
89
90
  .boolean()
@@ -91,18 +91,18 @@ For an upload-to-poster Template, SDK usage, and publication checks, see the [HT
91
91
  count: thumbnailCountSchema.describe(`
92
92
  The number of thumbnails to be extracted. As some videos have incorrect durations, the actual number of thumbnails generated may be less in rare cases. The maximum number of thumbnails we currently allow is 999.
93
93
 
94
- The thumbnails are taken at regular intervals, determined by dividing the video duration by the count. For example, a count of 3 will produce thumbnails at 25%, 50% and 75% through the video.
94
+ When \`smart\` is \`false\`, the thumbnails are taken at regular intervals. For example, a count of 3 produces thumbnails at 25%, 50% and 75% through the video. With \`smart: true\`, this is the number of thumbnails selected from the candidate pool instead.
95
95
 
96
96
  To extract thumbnails for specific timestamps, use the \`offsets\` parameter.
97
97
  `),
98
98
  offsets: z
99
- .union([z.array(z.number().finite()), z.array(percentageSchema)])
99
+ .union([z.array(z.number().finite()).min(1), z.array(percentageSchema).min(1)])
100
100
  .nullable()
101
- .default([])
101
+ .optional()
102
102
  .describe(`
103
103
  An array of offsets representing seconds of the file duration, such as \`[ 2, 45, 120 ]\`. Millisecond durations of a file can also be used by using decimal place values. For example, an offset from 1250 milliseconds would be represented with \`1.25\`. Offsets can also be percentage values such as \`[ "2%", "50%", "75%" ]\`.
104
104
 
105
- This option cannot be used with the \`count\` parameter, and takes precedence if both are specified. Out-of-range offsets are silently ignored.
105
+ When \`smart\` is \`false\`, \`offsets\` takes precedence over \`count\`. Request timestamps within the video duration. If FFmpeg produces no frame at a requested offset, the Robot may retry without timestamp seeking and return an early frame instead. It does not simply ignore the offset, and extraction can still fail if no frame is available.
106
106
 
107
107
  When \`smart\` is \`true\`, smart selection ignores \`offsets\` and uses \`count\` to select from its own candidate timestamps. If no smart candidates can be extracted, the Robot falls back to standard extraction, where \`offsets\` takes precedence. Use \`smart: false\` to consistently extract the specified timestamps.
108
108
 
@@ -128,17 +128,17 @@ The width of the thumbnail, in pixels. Omit or use \`null\` for the original wid
128
128
  .number()
129
129
  .int()
130
130
  .min(1)
131
- .max(1080)
131
+ .max(1920)
132
132
  .nullable()
133
133
  .optional()
134
134
  .describe(`
135
- The height of the thumbnail, in pixels. Omit or use \`null\` for the original height of the video.
135
+ The thumbnail height in pixels, capped like \`width\`. Omit or use \`null\` for the original height.
136
136
  `),
137
137
  resize_strategy: resize_strategy.describe(`
138
138
  One of the [available resize strategies](/docs/topics/resize-strategies/).
139
139
  `),
140
- background: color_with_alpha.default('#00000000').describe(`
141
- The background color of the resulting thumbnails in the \`"rrggbbaa"\` format (red, green, blue, alpha) when used with the \`"pad"\` resize strategy. The default color is black.
140
+ background: color_with_alpha.default('#000000ff').describe(`
141
+ The background color of the resulting thumbnails in the \`"rrggbbaa"\` format (red, green, blue, alpha) when used with the \`"pad"\` resize strategy. The default color is opaque black.
142
142
  `),
143
143
  rotate: z
144
144
  .union([z.literal(0), z.literal(90), z.literal(180), z.literal(270), z.literal(360)])
@@ -39,7 +39,7 @@ export const robotVimeoStoreInstructionsSchema = robotBase
39
39
  robot: z.literal('/vimeo/store').describe(`
40
40
  ## Getting started
41
41
 
42
- Since Vimeo works with OAuth, you will need to generate [Template Credentials](https://transloadit.com/c/template-credentials/) to use this <dfn>Robot</dfn>.
42
+ Create [Template Credentials](https://transloadit.com/c/template-credentials/) using Vimeo OAuth for uploads, or paste your own personal access token. A personal token needs \`upload\`, \`edit\`, and \`interact\` for this <dfn>Robot</dfn>, plus \`public\`, \`private\`, and \`video_files\` when saving it through the Console for imports. Your Vimeo app must have upload access. Follow the [Vimeo token instructions](/docs/topics/template-credentials/#vimeo) to create a token or reconnect.
43
43
 
44
44
  To change the \`title\` or \`description\` per video, we recommend to [inject variables into your Template](/docs/topics/templates/).
45
45