@uploadista/flow-videos-nodes 0.0.19 → 0.0.20-beta.2
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/.turbo/turbo-build.log +15 -14
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +82 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +82 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/resize-node.ts +76 -9
- package/src/transcode-node.ts +120 -39
- package/src/trim-node.ts +76 -9
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @uploadista/flow-videos-nodes@0.0.
|
|
3
|
+
> @uploadista/flow-videos-nodes@0.0.20-beta.1 build /Users/denislaboureyras/Documents/uploadista/dev/uploadista-workspace/uploadista-sdk/packages/flow/videos/nodes
|
|
4
4
|
> tsdown
|
|
5
5
|
|
|
6
|
-
[34mℹ[39m tsdown [2mv0.
|
|
7
|
-
[34mℹ[39m
|
|
6
|
+
[34mℹ[39m tsdown [2mv0.17.1[22m powered by rolldown [2mv1.0.0-beta.53[22m
|
|
7
|
+
[34mℹ[39m config file: [4m/Users/denislaboureyras/Documents/uploadista/dev/uploadista-workspace/uploadista-sdk/packages/flow/videos/nodes/tsdown.config.ts[24m
|
|
8
8
|
[34mℹ[39m entry: [34msrc/index.ts[39m
|
|
9
9
|
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
10
10
|
[34mℹ[39m Build start
|
|
11
11
|
[34mℹ[39m Cleaning 7 files
|
|
12
|
-
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[1mindex.cjs[22m [
|
|
13
|
-
[34mℹ[39m [33m[CJS][39m 1 files, total:
|
|
14
|
-
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[1mindex.mjs[22m [2m
|
|
15
|
-
[34mℹ[39m [34m[ESM][39m [2mdist/[22mindex.mjs.map [
|
|
16
|
-
[34mℹ[39m [34m[ESM][39m [2mdist/[22mindex.d.mts.map [2m 2.
|
|
17
|
-
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [
|
|
18
|
-
[34mℹ[39m [34m[ESM][39m 4 files, total:
|
|
19
|
-
[
|
|
20
|
-
[34mℹ[39m [33m[CJS][39m [2mdist/[
|
|
21
|
-
[34mℹ[39m [33m[CJS][39m
|
|
22
|
-
[
|
|
12
|
+
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[1mindex.cjs[22m [2m5.29 kB[22m [2m│ gzip: 1.60 kB[22m
|
|
13
|
+
[34mℹ[39m [33m[CJS][39m 1 files, total: 5.29 kB
|
|
14
|
+
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[1mindex.mjs[22m [2m 4.91 kB[22m [2m│ gzip: 1.62 kB[22m
|
|
15
|
+
[34mℹ[39m [34m[ESM][39m [2mdist/[22mindex.mjs.map [2m26.96 kB[22m [2m│ gzip: 5.12 kB[22m
|
|
16
|
+
[34mℹ[39m [34m[ESM][39m [2mdist/[22mindex.d.mts.map [2m 2.51 kB[22m [2m│ gzip: 0.78 kB[22m
|
|
17
|
+
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m13.70 kB[22m [2m│ gzip: 2.07 kB[22m
|
|
18
|
+
[34mℹ[39m [34m[ESM][39m 4 files, total: 48.08 kB
|
|
19
|
+
[32m✔[39m Build complete in [32m5178ms[39m
|
|
20
|
+
[34mℹ[39m [33m[CJS][39m [2mdist/[22mindex.d.cts.map [2m 2.51 kB[22m [2m│ gzip: 0.78 kB[22m
|
|
21
|
+
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[32m[1mindex.d.cts[22m[39m [2m13.70 kB[22m [2m│ gzip: 2.07 kB[22m
|
|
22
|
+
[34mℹ[39m [33m[CJS][39m 2 files, total: 16.21 kB
|
|
23
|
+
[32m✔[39m Build complete in [32m5180ms[39m
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e=require(`@uploadista/core/flow`),t=require(`effect`),n=require(`@uploadista/core/errors`);function r(n,r){return t.Effect.gen(function*(){let i=yield*e.VideoPlugin;return yield*(0,e.createTransformNode)({id:n,name:`Describe Video`,description:`Extracts video metadata (duration, resolution, codec, etc.)`,nodeTypeId:`describe-video`,outputTypeId:e.STORAGE_OUTPUT_TYPE_ID,keepOutput:r?.keepOutput,transform:(e,n)=>t.Effect.gen(function*(){let t=yield*i.describe(e);return{bytes:e,metadata:{...n.metadata,videoInfo:t}}})})})}function
|
|
1
|
+
let e=require(`@uploadista/core/flow`),t=require(`effect`),n=require(`@uploadista/core/errors`);function r(n,r){return t.Effect.gen(function*(){let i=yield*e.VideoPlugin;return yield*(0,e.createTransformNode)({id:n,name:`Describe Video`,description:`Extracts video metadata (duration, resolution, codec, etc.)`,nodeTypeId:`describe-video`,outputTypeId:e.STORAGE_OUTPUT_TYPE_ID,keepOutput:r?.keepOutput,transform:(e,n)=>t.Effect.gen(function*(){let t=yield*i.describe(e);return{bytes:e,metadata:{...n.metadata,videoInfo:t}}})})})}const i={fileSizeThreshold:1e7,chunkSize:1048576};function a(n,r,a){return t.Effect.gen(function*(){let o=yield*e.VideoPlugin,s=o.supportsStreaming??!1,c=a?.mode??`auto`,l=c===`buffered`?`buffered`:s?c:`buffered`,u={...i,...a?.streamingConfig},d=a?.naming?{...a.naming,autoSuffix:a.naming.autoSuffix??(e=>`${e.width??r.width}x${e.height??r.height}`)}:void 0;return yield*(0,e.createTransformNode)({id:n,name:`Resize Video`,description:`Changes video resolution`,nodeTypeId:`resize-video`,outputTypeId:e.STORAGE_OUTPUT_TYPE_ID,keepOutput:a?.keepOutput,naming:d,nodeType:`resize-video`,namingVars:{width:r.width,height:r.height},mode:l,streamingConfig:u,transform:e=>t.Effect.map(o.resize(e,r),e=>({bytes:e})),streamingTransform:o.resizeStream?e=>t.Effect.gen(function*(){let t=o.resizeStream;if(!t)throw Error(`resizeStream not available`);return{stream:yield*t(e,r)}}):void 0})})}function o(n,r,i){return t.Effect.gen(function*(){let a=yield*e.VideoPlugin,o=r.format||`jpeg`;return yield*(0,e.createTransformNode)({id:n,name:`Generate Thumbnail`,description:`Extracts a frame from video as an image`,nodeTypeId:`thumbnail-video`,outputTypeId:e.STORAGE_OUTPUT_TYPE_ID,keepOutput:i?.keepOutput,nodeType:`thumbnail`,namingVars:{format:o},transform:(s,c)=>t.Effect.map(a.extractFrame(s,r),t=>{let r=o===`png`?`image/png`:`image/jpeg`,a=o===`png`?`png`:`jpg`,s=c.metadata?.fileName,l;if(s&&typeof s==`string`)if(i?.naming){let t={...i.naming,autoSuffix:i.naming.autoSuffix??(()=>`thumb`)};l=`${(0,e.getBaseName)((0,e.applyFileNaming)(c,(0,e.buildNamingContext)(c,{flowId:c.flow?.flowId??``,jobId:c.flow?.jobId??``,nodeId:n,nodeType:`thumbnail`},{format:o}),t))}.${a}`}else l=s.replace(/\.[^.]+$/,`.${a}`);return{bytes:t,type:r,fileName:l}})})})}const s={fileSizeThreshold:1e7,chunkSize:1048576},c={mp4:`video/mp4`,webm:`video/webm`,mov:`video/quicktime`,avi:`video/x-msvideo`},l={mp4:`mp4`,webm:`webm`,mov:`mov`,avi:`avi`};function u(n,r,i){return t.Effect.gen(function*(){let a=yield*e.VideoPlugin,o=a.supportsStreaming??!1,u=i?.mode??`auto`,d=u===`buffered`?`buffered`:o?u:`buffered`,f={...s,...i?.streamingConfig},p=t=>{let a=c[r.format],o=l[r.format],s=t.metadata?.fileName,u;if(s&&typeof s==`string`)if(i?.naming){let a={...i.naming,autoSuffix:i.naming.autoSuffix??(e=>e.format??r.format)};u=`${(0,e.getBaseName)((0,e.applyFileNaming)(t,(0,e.buildNamingContext)(t,{flowId:t.flow?.flowId??``,jobId:t.flow?.jobId??``,nodeId:n,nodeType:`transcode`},{format:r.format}),a))}.${o}`}else u=s.replace(/\.[^.]+$/,`.${o}`);return{newType:a,newFileName:u}};return yield*(0,e.createTransformNode)({id:n,name:`Transcode`,description:`Converts video to specified format and codec`,nodeTypeId:`transcode-video`,outputTypeId:e.STORAGE_OUTPUT_TYPE_ID,keepOutput:i?.keepOutput,nodeType:`transcode`,namingVars:{format:r.format},mode:d,streamingConfig:f,transform:(e,n)=>t.Effect.map(a.transcode(e,r),e=>{let{newType:t,newFileName:r}=p(n);return{bytes:e,type:t,fileName:r}}),streamingTransform:a.transcodeStream?(e,n)=>t.Effect.gen(function*(){let t=a.transcodeStream;if(!t)throw Error(`transcodeStream not available`);let i=yield*t(e,r),{newType:o,newFileName:s}=p(n);return{stream:i,type:o,fileName:s}}):void 0})})}const d={fileSizeThreshold:1e7,chunkSize:1048576};function f(r,i,a){return t.Effect.gen(function*(){let o=yield*e.VideoPlugin;if(i.endTime!==void 0&&i.endTime<=i.startTime)return yield*n.UploadistaError.fromCode(`VALIDATION_ERROR`,{body:`endTime must be greater than startTime`,details:{params:i}}).toEffect();if(i.duration!==void 0&&i.endTime!==void 0&&i.duration!==i.endTime-i.startTime)return yield*n.UploadistaError.fromCode(`VALIDATION_ERROR`,{body:`Cannot specify both endTime and duration with conflicting values`,details:{params:i}}).toEffect();if(i.duration!==void 0&&i.duration<=0)return yield*n.UploadistaError.fromCode(`VALIDATION_ERROR`,{body:`duration must be greater than 0`,details:{params:i}}).toEffect();let s=o.supportsStreaming??!1,c=a?.mode??`auto`,l=c===`buffered`?`buffered`:s?c:`buffered`,u={...d,...a?.streamingConfig},f=a?.naming?{...a.naming,autoSuffix:a.naming.autoSuffix??(()=>`trimmed`)}:void 0;return yield*(0,e.createTransformNode)({id:r,name:`Trim Video`,description:`Extracts a segment from the video`,nodeTypeId:`trim-video`,outputTypeId:e.STORAGE_OUTPUT_TYPE_ID,keepOutput:a?.keepOutput,naming:f,nodeType:`trim`,mode:l,streamingConfig:u,transform:e=>t.Effect.map(o.trim(e,i),e=>({bytes:e})),streamingTransform:o.trimStream?e=>t.Effect.gen(function*(){let t=o.trimStream;if(!t)throw Error(`trimStream not available`);return{stream:yield*t(e,i)}}):void 0})})}exports.createDescribeVideoNode=r,exports.createTranscodeVideoNode=u,exports.createTrimVideoNode=f,exports.createVideoResizeNode=a,exports.createVideoThumbnailNode=o;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _uploadista_core_flow3 from "@uploadista/core/flow";
|
|
2
|
-
import { DescribeVideoMetadata, ExtractFrameVideoParams, ExtractFrameVideoParams as ExtractFrameVideoParams$1, FileNamingConfig, ResizeVideoParams, ResizeVideoParams as ResizeVideoParams$1, TranscodeVideoParams, TranscodeVideoParams as TranscodeVideoParams$1, TrimVideoParams, TrimVideoParams as TrimVideoParams$1, VideoPlugin } from "@uploadista/core/flow";
|
|
2
|
+
import { DescribeVideoMetadata, ExtractFrameVideoParams, ExtractFrameVideoParams as ExtractFrameVideoParams$1, FileNamingConfig, ResizeVideoParams, ResizeVideoParams as ResizeVideoParams$1, StreamingConfig, TranscodeVideoParams, TranscodeVideoParams as TranscodeVideoParams$1, TransformMode, TrimVideoParams, TrimVideoParams as TrimVideoParams$1, VideoPlugin } from "@uploadista/core/flow";
|
|
3
3
|
import * as _uploadista_core_types5 from "@uploadista/core/types";
|
|
4
4
|
import * as zod_v4_core1 from "zod/v4/core";
|
|
5
5
|
import * as zod1 from "zod";
|
|
@@ -62,15 +62,21 @@ declare function createDescribeVideoNode(id: string, options?: {
|
|
|
62
62
|
*
|
|
63
63
|
* Changes video resolution while optionally maintaining aspect ratio.
|
|
64
64
|
*
|
|
65
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
66
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
67
|
+
* reducing peak memory usage.
|
|
68
|
+
*
|
|
65
69
|
* @param id - Unique node identifier
|
|
66
70
|
* @param params - Resize parameters
|
|
67
71
|
* @param options - Optional configuration
|
|
68
72
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
69
73
|
* @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)
|
|
74
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
75
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
70
76
|
*
|
|
71
77
|
* @example
|
|
72
78
|
* ```typescript
|
|
73
|
-
* //
|
|
79
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
74
80
|
* const node = yield* createVideoResizeNode("resize-1", {
|
|
75
81
|
* width: 1280,
|
|
76
82
|
* height: 720,
|
|
@@ -79,11 +85,31 @@ declare function createDescribeVideoNode(id: string, options?: {
|
|
|
79
85
|
* }, {
|
|
80
86
|
* naming: { mode: "auto" }
|
|
81
87
|
* });
|
|
88
|
+
*
|
|
89
|
+
* // Force buffered mode for small files
|
|
90
|
+
* const nodeBuffered = yield* createVideoResizeNode("resize-2", {
|
|
91
|
+
* width: 1920,
|
|
92
|
+
* height: 1080
|
|
93
|
+
* }, {
|
|
94
|
+
* mode: "buffered",
|
|
95
|
+
* naming: { mode: "auto" }
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* // Force streaming mode for memory efficiency
|
|
99
|
+
* const nodeStreaming = yield* createVideoResizeNode("resize-3", {
|
|
100
|
+
* width: 1280,
|
|
101
|
+
* height: 720
|
|
102
|
+
* }, {
|
|
103
|
+
* mode: "streaming",
|
|
104
|
+
* naming: { mode: "auto" }
|
|
105
|
+
* });
|
|
82
106
|
* ```
|
|
83
107
|
*/
|
|
84
108
|
declare function createVideoResizeNode(id: string, params: ResizeVideoParams$1, options?: {
|
|
85
109
|
keepOutput?: boolean;
|
|
86
110
|
naming?: FileNamingConfig;
|
|
111
|
+
mode?: TransformMode;
|
|
112
|
+
streamingConfig?: StreamingConfig;
|
|
87
113
|
}): Effect.Effect<_uploadista_core_flow3.FlowNodeData & {
|
|
88
114
|
inputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
89
115
|
outputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
@@ -175,15 +201,21 @@ declare function createVideoThumbnailNode(id: string, params: ExtractFrameVideoP
|
|
|
175
201
|
*
|
|
176
202
|
* Converts video to specified format and codec, optionally adjusting bitrates.
|
|
177
203
|
*
|
|
204
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
205
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
206
|
+
* reducing peak memory usage.
|
|
207
|
+
*
|
|
178
208
|
* @param id - Unique node identifier
|
|
179
209
|
* @param params - Transcode parameters
|
|
180
210
|
* @param options - Optional configuration
|
|
181
211
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
182
212
|
* @param options.naming - File naming configuration (auto suffix: `${format}`)
|
|
213
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
214
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
183
215
|
*
|
|
184
216
|
* @example
|
|
185
217
|
* ```typescript
|
|
186
|
-
* //
|
|
218
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
187
219
|
* const node = yield* createTranscodeVideoNode("transcode-1", {
|
|
188
220
|
* format: "webm",
|
|
189
221
|
* codec: "vp9",
|
|
@@ -191,11 +223,31 @@ declare function createVideoThumbnailNode(id: string, params: ExtractFrameVideoP
|
|
|
191
223
|
* }, {
|
|
192
224
|
* naming: { mode: "auto" }
|
|
193
225
|
* });
|
|
226
|
+
*
|
|
227
|
+
* // Force buffered mode for small files
|
|
228
|
+
* const nodeBuffered = yield* createTranscodeVideoNode("transcode-2", {
|
|
229
|
+
* format: "mp4",
|
|
230
|
+
* codec: "h264"
|
|
231
|
+
* }, {
|
|
232
|
+
* mode: "buffered",
|
|
233
|
+
* naming: { mode: "auto" }
|
|
234
|
+
* });
|
|
235
|
+
*
|
|
236
|
+
* // Force streaming mode for memory efficiency
|
|
237
|
+
* const nodeStreaming = yield* createTranscodeVideoNode("transcode-3", {
|
|
238
|
+
* format: "mp4",
|
|
239
|
+
* codec: "h264"
|
|
240
|
+
* }, {
|
|
241
|
+
* mode: "streaming",
|
|
242
|
+
* naming: { mode: "auto" }
|
|
243
|
+
* });
|
|
194
244
|
* ```
|
|
195
245
|
*/
|
|
196
246
|
declare function createTranscodeVideoNode(id: string, params: TranscodeVideoParams$1, options?: {
|
|
197
247
|
keepOutput?: boolean;
|
|
198
248
|
naming?: FileNamingConfig;
|
|
249
|
+
mode?: TransformMode;
|
|
250
|
+
streamingConfig?: StreamingConfig;
|
|
199
251
|
}): Effect.Effect<_uploadista_core_flow3.FlowNodeData & {
|
|
200
252
|
inputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
201
253
|
outputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
@@ -231,26 +283,52 @@ declare function createTranscodeVideoNode(id: string, params: TranscodeVideoPara
|
|
|
231
283
|
*
|
|
232
284
|
* Extracts a segment from the video by time range.
|
|
233
285
|
*
|
|
286
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
287
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
288
|
+
* reducing peak memory usage.
|
|
289
|
+
*
|
|
234
290
|
* @param id - Unique node identifier
|
|
235
291
|
* @param params - Trim parameters
|
|
236
292
|
* @param options - Optional configuration
|
|
237
293
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
238
294
|
* @param options.naming - File naming configuration (auto suffix: `trimmed`)
|
|
295
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
296
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
239
297
|
*
|
|
240
298
|
* @example
|
|
241
299
|
* ```typescript
|
|
242
|
-
* //
|
|
300
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
243
301
|
* const node = yield* createTrimVideoNode("trim-1", {
|
|
244
302
|
* startTime: 10,
|
|
245
303
|
* endTime: 30
|
|
246
304
|
* }, {
|
|
247
305
|
* naming: { mode: "auto" }
|
|
248
306
|
* });
|
|
307
|
+
*
|
|
308
|
+
* // Force buffered mode for small files
|
|
309
|
+
* const nodeBuffered = yield* createTrimVideoNode("trim-2", {
|
|
310
|
+
* startTime: 0,
|
|
311
|
+
* duration: 60
|
|
312
|
+
* }, {
|
|
313
|
+
* mode: "buffered",
|
|
314
|
+
* naming: { mode: "auto" }
|
|
315
|
+
* });
|
|
316
|
+
*
|
|
317
|
+
* // Force streaming mode for memory efficiency
|
|
318
|
+
* const nodeStreaming = yield* createTrimVideoNode("trim-3", {
|
|
319
|
+
* startTime: 5,
|
|
320
|
+
* endTime: 25
|
|
321
|
+
* }, {
|
|
322
|
+
* mode: "streaming",
|
|
323
|
+
* naming: { mode: "auto" }
|
|
324
|
+
* });
|
|
249
325
|
* ```
|
|
250
326
|
*/
|
|
251
327
|
declare function createTrimVideoNode(id: string, params: TrimVideoParams$1, options?: {
|
|
252
328
|
keepOutput?: boolean;
|
|
253
329
|
naming?: FileNamingConfig;
|
|
330
|
+
mode?: TransformMode;
|
|
331
|
+
streamingConfig?: StreamingConfig;
|
|
254
332
|
}): Effect.Effect<_uploadista_core_flow3.FlowNodeData & {
|
|
255
333
|
inputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
256
334
|
outputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/describe-video-node.ts","../src/resize-node.ts","../src/thumbnail-node.ts","../src/transcode-node.ts","../src/trim-node.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA;;;;;;iBAAgB,uBAAA;;IAEoB,MAAA,CAAA,OAFG,sBAAA,CAEH,YAAA;4BAAA,uBAAA,CAAA,UAAA;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/describe-video-node.ts","../src/resize-node.ts","../src/thumbnail-node.ts","../src/transcode-node.ts","../src/trim-node.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _uploadista_core_flow3 from "@uploadista/core/flow";
|
|
2
|
-
import { DescribeVideoMetadata, ExtractFrameVideoParams, ExtractFrameVideoParams as ExtractFrameVideoParams$1, FileNamingConfig, ResizeVideoParams, ResizeVideoParams as ResizeVideoParams$1, TranscodeVideoParams, TranscodeVideoParams as TranscodeVideoParams$1, TrimVideoParams, TrimVideoParams as TrimVideoParams$1, VideoPlugin } from "@uploadista/core/flow";
|
|
2
|
+
import { DescribeVideoMetadata, ExtractFrameVideoParams, ExtractFrameVideoParams as ExtractFrameVideoParams$1, FileNamingConfig, ResizeVideoParams, ResizeVideoParams as ResizeVideoParams$1, StreamingConfig, TranscodeVideoParams, TranscodeVideoParams as TranscodeVideoParams$1, TransformMode, TrimVideoParams, TrimVideoParams as TrimVideoParams$1, VideoPlugin } from "@uploadista/core/flow";
|
|
3
3
|
import { Effect } from "effect";
|
|
4
4
|
import * as _uploadista_core_errors0 from "@uploadista/core/errors";
|
|
5
5
|
import { UploadistaError } from "@uploadista/core/errors";
|
|
@@ -62,15 +62,21 @@ declare function createDescribeVideoNode(id: string, options?: {
|
|
|
62
62
|
*
|
|
63
63
|
* Changes video resolution while optionally maintaining aspect ratio.
|
|
64
64
|
*
|
|
65
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
66
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
67
|
+
* reducing peak memory usage.
|
|
68
|
+
*
|
|
65
69
|
* @param id - Unique node identifier
|
|
66
70
|
* @param params - Resize parameters
|
|
67
71
|
* @param options - Optional configuration
|
|
68
72
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
69
73
|
* @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)
|
|
74
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
75
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
70
76
|
*
|
|
71
77
|
* @example
|
|
72
78
|
* ```typescript
|
|
73
|
-
* //
|
|
79
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
74
80
|
* const node = yield* createVideoResizeNode("resize-1", {
|
|
75
81
|
* width: 1280,
|
|
76
82
|
* height: 720,
|
|
@@ -79,11 +85,31 @@ declare function createDescribeVideoNode(id: string, options?: {
|
|
|
79
85
|
* }, {
|
|
80
86
|
* naming: { mode: "auto" }
|
|
81
87
|
* });
|
|
88
|
+
*
|
|
89
|
+
* // Force buffered mode for small files
|
|
90
|
+
* const nodeBuffered = yield* createVideoResizeNode("resize-2", {
|
|
91
|
+
* width: 1920,
|
|
92
|
+
* height: 1080
|
|
93
|
+
* }, {
|
|
94
|
+
* mode: "buffered",
|
|
95
|
+
* naming: { mode: "auto" }
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* // Force streaming mode for memory efficiency
|
|
99
|
+
* const nodeStreaming = yield* createVideoResizeNode("resize-3", {
|
|
100
|
+
* width: 1280,
|
|
101
|
+
* height: 720
|
|
102
|
+
* }, {
|
|
103
|
+
* mode: "streaming",
|
|
104
|
+
* naming: { mode: "auto" }
|
|
105
|
+
* });
|
|
82
106
|
* ```
|
|
83
107
|
*/
|
|
84
108
|
declare function createVideoResizeNode(id: string, params: ResizeVideoParams$1, options?: {
|
|
85
109
|
keepOutput?: boolean;
|
|
86
110
|
naming?: FileNamingConfig;
|
|
111
|
+
mode?: TransformMode;
|
|
112
|
+
streamingConfig?: StreamingConfig;
|
|
87
113
|
}): Effect.Effect<_uploadista_core_flow3.FlowNodeData & {
|
|
88
114
|
inputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
89
115
|
outputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
@@ -175,15 +201,21 @@ declare function createVideoThumbnailNode(id: string, params: ExtractFrameVideoP
|
|
|
175
201
|
*
|
|
176
202
|
* Converts video to specified format and codec, optionally adjusting bitrates.
|
|
177
203
|
*
|
|
204
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
205
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
206
|
+
* reducing peak memory usage.
|
|
207
|
+
*
|
|
178
208
|
* @param id - Unique node identifier
|
|
179
209
|
* @param params - Transcode parameters
|
|
180
210
|
* @param options - Optional configuration
|
|
181
211
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
182
212
|
* @param options.naming - File naming configuration (auto suffix: `${format}`)
|
|
213
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
214
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
183
215
|
*
|
|
184
216
|
* @example
|
|
185
217
|
* ```typescript
|
|
186
|
-
* //
|
|
218
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
187
219
|
* const node = yield* createTranscodeVideoNode("transcode-1", {
|
|
188
220
|
* format: "webm",
|
|
189
221
|
* codec: "vp9",
|
|
@@ -191,11 +223,31 @@ declare function createVideoThumbnailNode(id: string, params: ExtractFrameVideoP
|
|
|
191
223
|
* }, {
|
|
192
224
|
* naming: { mode: "auto" }
|
|
193
225
|
* });
|
|
226
|
+
*
|
|
227
|
+
* // Force buffered mode for small files
|
|
228
|
+
* const nodeBuffered = yield* createTranscodeVideoNode("transcode-2", {
|
|
229
|
+
* format: "mp4",
|
|
230
|
+
* codec: "h264"
|
|
231
|
+
* }, {
|
|
232
|
+
* mode: "buffered",
|
|
233
|
+
* naming: { mode: "auto" }
|
|
234
|
+
* });
|
|
235
|
+
*
|
|
236
|
+
* // Force streaming mode for memory efficiency
|
|
237
|
+
* const nodeStreaming = yield* createTranscodeVideoNode("transcode-3", {
|
|
238
|
+
* format: "mp4",
|
|
239
|
+
* codec: "h264"
|
|
240
|
+
* }, {
|
|
241
|
+
* mode: "streaming",
|
|
242
|
+
* naming: { mode: "auto" }
|
|
243
|
+
* });
|
|
194
244
|
* ```
|
|
195
245
|
*/
|
|
196
246
|
declare function createTranscodeVideoNode(id: string, params: TranscodeVideoParams$1, options?: {
|
|
197
247
|
keepOutput?: boolean;
|
|
198
248
|
naming?: FileNamingConfig;
|
|
249
|
+
mode?: TransformMode;
|
|
250
|
+
streamingConfig?: StreamingConfig;
|
|
199
251
|
}): Effect.Effect<_uploadista_core_flow3.FlowNodeData & {
|
|
200
252
|
inputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
201
253
|
outputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
@@ -231,26 +283,52 @@ declare function createTranscodeVideoNode(id: string, params: TranscodeVideoPara
|
|
|
231
283
|
*
|
|
232
284
|
* Extracts a segment from the video by time range.
|
|
233
285
|
*
|
|
286
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
287
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
288
|
+
* reducing peak memory usage.
|
|
289
|
+
*
|
|
234
290
|
* @param id - Unique node identifier
|
|
235
291
|
* @param params - Trim parameters
|
|
236
292
|
* @param options - Optional configuration
|
|
237
293
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
238
294
|
* @param options.naming - File naming configuration (auto suffix: `trimmed`)
|
|
295
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
296
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
239
297
|
*
|
|
240
298
|
* @example
|
|
241
299
|
* ```typescript
|
|
242
|
-
* //
|
|
300
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
243
301
|
* const node = yield* createTrimVideoNode("trim-1", {
|
|
244
302
|
* startTime: 10,
|
|
245
303
|
* endTime: 30
|
|
246
304
|
* }, {
|
|
247
305
|
* naming: { mode: "auto" }
|
|
248
306
|
* });
|
|
307
|
+
*
|
|
308
|
+
* // Force buffered mode for small files
|
|
309
|
+
* const nodeBuffered = yield* createTrimVideoNode("trim-2", {
|
|
310
|
+
* startTime: 0,
|
|
311
|
+
* duration: 60
|
|
312
|
+
* }, {
|
|
313
|
+
* mode: "buffered",
|
|
314
|
+
* naming: { mode: "auto" }
|
|
315
|
+
* });
|
|
316
|
+
*
|
|
317
|
+
* // Force streaming mode for memory efficiency
|
|
318
|
+
* const nodeStreaming = yield* createTrimVideoNode("trim-3", {
|
|
319
|
+
* startTime: 5,
|
|
320
|
+
* endTime: 25
|
|
321
|
+
* }, {
|
|
322
|
+
* mode: "streaming",
|
|
323
|
+
* naming: { mode: "auto" }
|
|
324
|
+
* });
|
|
249
325
|
* ```
|
|
250
326
|
*/
|
|
251
327
|
declare function createTrimVideoNode(id: string, params: TrimVideoParams$1, options?: {
|
|
252
328
|
keepOutput?: boolean;
|
|
253
329
|
naming?: FileNamingConfig;
|
|
330
|
+
mode?: TransformMode;
|
|
331
|
+
streamingConfig?: StreamingConfig;
|
|
254
332
|
}): Effect.Effect<_uploadista_core_flow3.FlowNodeData & {
|
|
255
333
|
inputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
|
256
334
|
outputSchema: zod1.ZodType<_uploadista_core_types5.UploadFile, unknown, zod_v4_core1.$ZodTypeInternals<_uploadista_core_types5.UploadFile, unknown>>;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/describe-video-node.ts","../src/resize-node.ts","../src/thumbnail-node.ts","../src/transcode-node.ts","../src/trim-node.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA;;;;;;iBAAgB,uBAAA;;IAEoB,MAAA,CAAA,OAFG,sBAAA,CAEH,YAAA;4BAAA,uBAAA,CAAA,UAAA;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/describe-video-node.ts","../src/resize-node.ts","../src/thumbnail-node.ts","../src/transcode-node.ts","../src/trim-node.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{STORAGE_OUTPUT_TYPE_ID as e,VideoPlugin as t,applyFileNaming as n,buildNamingContext as r,createTransformNode as i,getBaseName as a}from"@uploadista/core/flow";import{Effect as o}from"effect";import{UploadistaError as s}from"@uploadista/core/errors";function c(n,r){return o.gen(function*(){let a=yield*t;return yield*i({id:n,name:`Describe Video`,description:`Extracts video metadata (duration, resolution, codec, etc.)`,nodeTypeId:`describe-video`,outputTypeId:e,keepOutput:r?.keepOutput,transform:(e,t)=>o.gen(function*(){let n=yield*a.describe(e);return{bytes:e,metadata:{...t.metadata,videoInfo:n}}})})})}function
|
|
1
|
+
import{STORAGE_OUTPUT_TYPE_ID as e,VideoPlugin as t,applyFileNaming as n,buildNamingContext as r,createTransformNode as i,getBaseName as a}from"@uploadista/core/flow";import{Effect as o}from"effect";import{UploadistaError as s}from"@uploadista/core/errors";function c(n,r){return o.gen(function*(){let a=yield*t;return yield*i({id:n,name:`Describe Video`,description:`Extracts video metadata (duration, resolution, codec, etc.)`,nodeTypeId:`describe-video`,outputTypeId:e,keepOutput:r?.keepOutput,transform:(e,t)=>o.gen(function*(){let n=yield*a.describe(e);return{bytes:e,metadata:{...t.metadata,videoInfo:n}}})})})}const l={fileSizeThreshold:1e7,chunkSize:1048576};function u(n,r,a){return o.gen(function*(){let s=yield*t,c=s.supportsStreaming??!1,u=a?.mode??`auto`,d=u===`buffered`?`buffered`:c?u:`buffered`,f={...l,...a?.streamingConfig},p=a?.naming?{...a.naming,autoSuffix:a.naming.autoSuffix??(e=>`${e.width??r.width}x${e.height??r.height}`)}:void 0;return yield*i({id:n,name:`Resize Video`,description:`Changes video resolution`,nodeTypeId:`resize-video`,outputTypeId:e,keepOutput:a?.keepOutput,naming:p,nodeType:`resize-video`,namingVars:{width:r.width,height:r.height},mode:d,streamingConfig:f,transform:e=>o.map(s.resize(e,r),e=>({bytes:e})),streamingTransform:s.resizeStream?e=>o.gen(function*(){let t=s.resizeStream;if(!t)throw Error(`resizeStream not available`);return{stream:yield*t(e,r)}}):void 0})})}function d(s,c,l){return o.gen(function*(){let u=yield*t,d=c.format||`jpeg`;return yield*i({id:s,name:`Generate Thumbnail`,description:`Extracts a frame from video as an image`,nodeTypeId:`thumbnail-video`,outputTypeId:e,keepOutput:l?.keepOutput,nodeType:`thumbnail`,namingVars:{format:d},transform:(e,t)=>o.map(u.extractFrame(e,c),e=>{let i=d===`png`?`image/png`:`image/jpeg`,o=d===`png`?`png`:`jpg`,c=t.metadata?.fileName,u;if(c&&typeof c==`string`)if(l?.naming){let e={...l.naming,autoSuffix:l.naming.autoSuffix??(()=>`thumb`)};u=`${a(n(t,r(t,{flowId:t.flow?.flowId??``,jobId:t.flow?.jobId??``,nodeId:s,nodeType:`thumbnail`},{format:d}),e))}.${o}`}else u=c.replace(/\.[^.]+$/,`.${o}`);return{bytes:e,type:i,fileName:u}})})})}const f={fileSizeThreshold:1e7,chunkSize:1048576},p={mp4:`video/mp4`,webm:`video/webm`,mov:`video/quicktime`,avi:`video/x-msvideo`},m={mp4:`mp4`,webm:`webm`,mov:`mov`,avi:`avi`};function h(s,c,l){return o.gen(function*(){let u=yield*t,d=u.supportsStreaming??!1,h=l?.mode??`auto`,g=h===`buffered`?`buffered`:d?h:`buffered`,_={...f,...l?.streamingConfig},v=e=>{let t=p[c.format],i=m[c.format],o=e.metadata?.fileName,u;if(o&&typeof o==`string`)if(l?.naming){let t={...l.naming,autoSuffix:l.naming.autoSuffix??(e=>e.format??c.format)};u=`${a(n(e,r(e,{flowId:e.flow?.flowId??``,jobId:e.flow?.jobId??``,nodeId:s,nodeType:`transcode`},{format:c.format}),t))}.${i}`}else u=o.replace(/\.[^.]+$/,`.${i}`);return{newType:t,newFileName:u}};return yield*i({id:s,name:`Transcode`,description:`Converts video to specified format and codec`,nodeTypeId:`transcode-video`,outputTypeId:e,keepOutput:l?.keepOutput,nodeType:`transcode`,namingVars:{format:c.format},mode:g,streamingConfig:_,transform:(e,t)=>o.map(u.transcode(e,c),e=>{let{newType:n,newFileName:r}=v(t);return{bytes:e,type:n,fileName:r}}),streamingTransform:u.transcodeStream?(e,t)=>o.gen(function*(){let n=u.transcodeStream;if(!n)throw Error(`transcodeStream not available`);let r=yield*n(e,c),{newType:i,newFileName:a}=v(t);return{stream:r,type:i,fileName:a}}):void 0})})}const g={fileSizeThreshold:1e7,chunkSize:1048576};function _(n,r,a){return o.gen(function*(){let c=yield*t;if(r.endTime!==void 0&&r.endTime<=r.startTime)return yield*s.fromCode(`VALIDATION_ERROR`,{body:`endTime must be greater than startTime`,details:{params:r}}).toEffect();if(r.duration!==void 0&&r.endTime!==void 0&&r.duration!==r.endTime-r.startTime)return yield*s.fromCode(`VALIDATION_ERROR`,{body:`Cannot specify both endTime and duration with conflicting values`,details:{params:r}}).toEffect();if(r.duration!==void 0&&r.duration<=0)return yield*s.fromCode(`VALIDATION_ERROR`,{body:`duration must be greater than 0`,details:{params:r}}).toEffect();let l=c.supportsStreaming??!1,u=a?.mode??`auto`,d=u===`buffered`?`buffered`:l?u:`buffered`,f={...g,...a?.streamingConfig},p=a?.naming?{...a.naming,autoSuffix:a.naming.autoSuffix??(()=>`trimmed`)}:void 0;return yield*i({id:n,name:`Trim Video`,description:`Extracts a segment from the video`,nodeTypeId:`trim-video`,outputTypeId:e,keepOutput:a?.keepOutput,naming:p,nodeType:`trim`,mode:d,streamingConfig:f,transform:e=>o.map(c.trim(e,r),e=>({bytes:e})),streamingTransform:c.trimStream?e=>o.gen(function*(){let t=c.trimStream;if(!t)throw Error(`trimStream not available`);return{stream:yield*t(e,r)}}):void 0})})}export{c as createDescribeVideoNode,h as createTranscodeVideoNode,_ as createTrimVideoNode,u as createVideoResizeNode,d as createVideoThumbnailNode};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["namingConfig: FileNamingConfig | undefined","newFileName: string | undefined","namingConfig: FileNamingConfig","formatToMimeType: Record<TranscodeVideoParams[\"format\"], string>","formatToExtension: Record<TranscodeVideoParams[\"format\"], string>","newFileName: string | undefined","namingConfig: FileNamingConfig","namingConfig: FileNamingConfig | undefined"],"sources":["../src/describe-video-node.ts","../src/resize-node.ts","../src/thumbnail-node.ts","../src/transcode-node.ts","../src/trim-node.ts"],"sourcesContent":["import {\n createTransformNode,\n STORAGE_OUTPUT_TYPE_ID,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Creates a Describe Video metadata extraction node\n *\n * Extracts comprehensive metadata from the video file including duration,\n * resolution, codec, bitrate, and audio information. The metadata is stored\n * in the file context for downstream nodes while passing through the original bytes.\n *\n * @param id - Unique node identifier\n * @returns Effect that resolves to the configured node\n *\n * @example\n * ```typescript\n * const node = yield* createDescribeVideoNode(\"describe-1\");\n * ```\n */\nexport function createDescribeVideoNode(\n id: string,\n options?: { keepOutput?: boolean },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n return yield* createTransformNode({\n id,\n name: \"Describe Video\",\n description:\n \"Extracts video metadata (duration, resolution, codec, etc.)\",\n nodeTypeId: \"describe-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n transform: (inputBytes, file) =>\n Effect.gen(function* () {\n // Extract metadata\n const metadata = yield* videoService.describe(inputBytes);\n\n // Store metadata in file context for downstream nodes\n return {\n bytes: inputBytes, // Pass through original bytes unchanged\n metadata: {\n ...file.metadata,\n videoInfo: metadata,\n },\n };\n }),\n });\n });\n}\n","import {\n createTransformNode,\n type FileNamingConfig,\n type ResizeVideoParams,\n STORAGE_OUTPUT_TYPE_ID,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Creates a Resize video processing node\n *\n * Changes video resolution while optionally maintaining aspect ratio.\n *\n * @param id - Unique node identifier\n * @param params - Resize parameters\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"video.mp4\" -> \"video-1280x720.mp4\"\n * const node = yield* createVideoResizeNode(\"resize-1\", {\n * width: 1280,\n * height: 720,\n * aspectRatio: \"keep\",\n * scaling: \"bicubic\"\n * }, {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createVideoResizeNode(\n id: string,\n params: ResizeVideoParams,\n options?: { keepOutput?: boolean; naming?: FileNamingConfig },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n // Build naming config with auto suffix for video resize\n const namingConfig: FileNamingConfig | undefined = options?.naming\n ? {\n ...options.naming,\n autoSuffix:\n options.naming.autoSuffix ??\n ((ctx) => `${ctx.width ?? params.width}x${ctx.height ?? params.height}`),\n }\n : undefined;\n\n return yield* createTransformNode({\n id,\n name: \"Resize Video\",\n description: \"Changes video resolution\",\n nodeTypeId: \"resize-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n naming: namingConfig,\n nodeType: \"resize-video\",\n namingVars: { width: params.width, height: params.height },\n transform: (inputBytes, _file) =>\n Effect.map(videoService.resize(inputBytes, params), (resizedBytes) => {\n // Pass through video bytes (no metadata changes needed)\n return {\n bytes: resizedBytes,\n };\n }),\n });\n });\n}\n","import {\n applyFileNaming,\n buildNamingContext,\n createTransformNode,\n type ExtractFrameVideoParams,\n type FileNamingConfig,\n getBaseName,\n STORAGE_OUTPUT_TYPE_ID,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Creates a Thumbnail generation node\n *\n * Extracts a single frame from the video as an image (JPEG or PNG).\n *\n * @param id - Unique node identifier\n * @param params - Frame extraction parameters\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `thumb`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"video.mp4\" -> \"video-thumb.jpg\"\n * const node = yield* createVideoThumbnailNode(\"thumbnail-1\", {\n * timestamp: 15,\n * format: \"jpeg\",\n * quality: 85\n * }, {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createVideoThumbnailNode(\n id: string,\n params: ExtractFrameVideoParams,\n options?: { keepOutput?: boolean; naming?: FileNamingConfig },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n const format = params.format || \"jpeg\";\n\n return yield* createTransformNode({\n id,\n name: \"Generate Thumbnail\",\n description: \"Extracts a frame from video as an image\",\n nodeTypeId: \"thumbnail-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n // Note: naming is handled in transform since format changes extension\n nodeType: \"thumbnail\",\n namingVars: { format },\n transform: (inputBytes, file) =>\n Effect.map(\n videoService.extractFrame(inputBytes, params),\n (imageBytes) => {\n // Map output to image MIME type and extension\n const mimeType = format === \"png\" ? \"image/png\" : \"image/jpeg\";\n const extension = format === \"png\" ? \"png\" : \"jpg\";\n\n // Get original fileName\n const fileName = file.metadata?.fileName;\n let newFileName: string | undefined;\n\n if (fileName && typeof fileName === \"string\") {\n // Apply naming if configured\n if (options?.naming) {\n const namingConfig: FileNamingConfig = {\n ...options.naming,\n autoSuffix: options.naming.autoSuffix ?? (() => \"thumb\"),\n };\n const namingContext = buildNamingContext(\n file,\n {\n flowId: file.flow?.flowId ?? \"\",\n jobId: file.flow?.jobId ?? \"\",\n nodeId: id,\n nodeType: \"thumbnail\",\n },\n { format },\n );\n // Apply naming to get base name with suffix\n const namedFile = applyFileNaming(file, namingContext, namingConfig);\n // Replace extension with image extension\n newFileName = `${getBaseName(namedFile)}.${extension}`;\n } else {\n // No naming config, just update extension\n newFileName = fileName.replace(/\\.[^.]+$/, `.${extension}`);\n }\n }\n\n return {\n bytes: imageBytes,\n type: mimeType,\n fileName: newFileName,\n };\n },\n ),\n });\n });\n}\n","import {\n applyFileNaming,\n buildNamingContext,\n createTransformNode,\n type FileNamingConfig,\n getBaseName,\n STORAGE_OUTPUT_TYPE_ID,\n type TranscodeVideoParams,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n// Map video format to MIME type\nconst formatToMimeType: Record<TranscodeVideoParams[\"format\"], string> = {\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mov: \"video/quicktime\",\n avi: \"video/x-msvideo\",\n};\n\n// Map video format to file extension\nconst formatToExtension: Record<TranscodeVideoParams[\"format\"], string> = {\n mp4: \"mp4\",\n webm: \"webm\",\n mov: \"mov\",\n avi: \"avi\",\n};\n\n/**\n * Creates a Transcode video processing node\n *\n * Converts video to specified format and codec, optionally adjusting bitrates.\n *\n * @param id - Unique node identifier\n * @param params - Transcode parameters\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `${format}`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"video.mov\" -> \"video-webm.webm\"\n * const node = yield* createTranscodeVideoNode(\"transcode-1\", {\n * format: \"webm\",\n * codec: \"vp9\",\n * videoBitrate: \"1000k\"\n * }, {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createTranscodeVideoNode(\n id: string,\n params: TranscodeVideoParams,\n options?: { keepOutput?: boolean; naming?: FileNamingConfig },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n return yield* createTransformNode({\n id,\n name: \"Transcode\",\n description: \"Converts video to specified format and codec\",\n nodeTypeId: \"transcode-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n // Note: naming is handled in transform since format changes extension\n nodeType: \"transcode\",\n namingVars: { format: params.format },\n transform: (inputBytes, file) =>\n Effect.map(\n videoService.transcode(inputBytes, params),\n (transcodedBytes) => {\n // Update metadata if format changes\n const newType = formatToMimeType[params.format];\n const newExtension = formatToExtension[params.format];\n\n // Get original fileName\n const fileName = file.metadata?.fileName;\n let newFileName: string | undefined;\n\n if (fileName && typeof fileName === \"string\") {\n // Apply naming if configured\n if (options?.naming) {\n const namingConfig: FileNamingConfig = {\n ...options.naming,\n autoSuffix:\n options.naming.autoSuffix ?? ((ctx) => ctx.format ?? params.format),\n };\n const namingContext = buildNamingContext(\n file,\n {\n flowId: file.flow?.flowId ?? \"\",\n jobId: file.flow?.jobId ?? \"\",\n nodeId: id,\n nodeType: \"transcode\",\n },\n { format: params.format },\n );\n // Apply naming to get base name with suffix\n const namedFile = applyFileNaming(file, namingContext, namingConfig);\n // Replace extension with new format extension\n newFileName = `${getBaseName(namedFile)}.${newExtension}`;\n } else {\n // No naming config, just update extension\n newFileName = fileName.replace(/\\.[^.]+$/, `.${newExtension}`);\n }\n }\n\n return {\n bytes: transcodedBytes,\n type: newType,\n fileName: newFileName,\n };\n },\n ),\n });\n });\n}\n","import { UploadistaError } from \"@uploadista/core/errors\";\nimport {\n createTransformNode,\n type FileNamingConfig,\n STORAGE_OUTPUT_TYPE_ID,\n type TrimVideoParams,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Creates a Trim video processing node\n *\n * Extracts a segment from the video by time range.\n *\n * @param id - Unique node identifier\n * @param params - Trim parameters\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `trimmed`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"video.mp4\" -> \"video-trimmed.mp4\"\n * const node = yield* createTrimVideoNode(\"trim-1\", {\n * startTime: 10,\n * endTime: 30\n * }, {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createTrimVideoNode(\n id: string,\n params: TrimVideoParams,\n options?: { keepOutput?: boolean; naming?: FileNamingConfig },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n // Validate params\n if (params.endTime !== undefined && params.endTime <= params.startTime) {\n return yield* UploadistaError.fromCode(\"VALIDATION_ERROR\", {\n body: \"endTime must be greater than startTime\",\n details: { params },\n }).toEffect();\n }\n\n if (\n params.duration !== undefined &&\n params.endTime !== undefined &&\n params.duration !== params.endTime - params.startTime\n ) {\n return yield* UploadistaError.fromCode(\"VALIDATION_ERROR\", {\n body: \"Cannot specify both endTime and duration with conflicting values\",\n details: { params },\n }).toEffect();\n }\n\n if (params.duration !== undefined && params.duration <= 0) {\n return yield* UploadistaError.fromCode(\"VALIDATION_ERROR\", {\n body: \"duration must be greater than 0\",\n details: { params },\n }).toEffect();\n }\n\n // Build naming config with auto suffix for trim\n const namingConfig: FileNamingConfig | undefined = options?.naming\n ? {\n ...options.naming,\n autoSuffix: options.naming.autoSuffix ?? (() => \"trimmed\"),\n }\n : undefined;\n\n return yield* createTransformNode({\n id,\n name: \"Trim Video\",\n description: \"Extracts a segment from the video\",\n nodeTypeId: \"trim-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n naming: namingConfig,\n nodeType: \"trim\",\n transform: (inputBytes, _file) =>\n Effect.map(videoService.trim(inputBytes, params), (trimmedBytes) => {\n // Pass through video bytes\n return {\n bytes: trimmedBytes,\n };\n }),\n });\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig","effectiveMode: TransformMode","streamingConfig: StreamingConfig","DEFAULT_VIDEO_STREAMING_CONFIG","namingConfig: FileNamingConfig | undefined","newFileName: string | undefined","namingConfig: FileNamingConfig","DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig","formatToMimeType: Record<TranscodeVideoParams[\"format\"], string>","formatToExtension: Record<TranscodeVideoParams[\"format\"], string>","effectiveMode: TransformMode","streamingConfig: StreamingConfig","DEFAULT_VIDEO_STREAMING_CONFIG","newFileName: string | undefined","namingConfig: FileNamingConfig","DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig","effectiveMode: TransformMode","streamingConfig: StreamingConfig","namingConfig: FileNamingConfig | undefined"],"sources":["../src/describe-video-node.ts","../src/resize-node.ts","../src/thumbnail-node.ts","../src/transcode-node.ts","../src/trim-node.ts"],"sourcesContent":["import {\n createTransformNode,\n STORAGE_OUTPUT_TYPE_ID,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Creates a Describe Video metadata extraction node\n *\n * Extracts comprehensive metadata from the video file including duration,\n * resolution, codec, bitrate, and audio information. The metadata is stored\n * in the file context for downstream nodes while passing through the original bytes.\n *\n * @param id - Unique node identifier\n * @returns Effect that resolves to the configured node\n *\n * @example\n * ```typescript\n * const node = yield* createDescribeVideoNode(\"describe-1\");\n * ```\n */\nexport function createDescribeVideoNode(\n id: string,\n options?: { keepOutput?: boolean },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n return yield* createTransformNode({\n id,\n name: \"Describe Video\",\n description:\n \"Extracts video metadata (duration, resolution, codec, etc.)\",\n nodeTypeId: \"describe-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n transform: (inputBytes, file) =>\n Effect.gen(function* () {\n // Extract metadata\n const metadata = yield* videoService.describe(inputBytes);\n\n // Store metadata in file context for downstream nodes\n return {\n bytes: inputBytes, // Pass through original bytes unchanged\n metadata: {\n ...file.metadata,\n videoInfo: metadata,\n },\n };\n }),\n });\n });\n}\n","import {\n createTransformNode,\n type FileNamingConfig,\n type ResizeVideoParams,\n STORAGE_OUTPUT_TYPE_ID,\n type StreamingConfig,\n type TransformMode,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n// Default streaming config for video processing\nconst DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig = {\n fileSizeThreshold: 10_000_000, // 10MB threshold for video\n chunkSize: 1_048_576, // 1MB chunks\n};\n\n/**\n * Creates a Resize video processing node\n *\n * Changes video resolution while optionally maintaining aspect ratio.\n *\n * Supports both buffered and streaming modes for memory-efficient processing\n * of large videos. In streaming mode, the output is streamed directly to storage,\n * reducing peak memory usage.\n *\n * @param id - Unique node identifier\n * @param params - Resize parameters\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)\n * @param options.mode - Transform mode: \"buffered\", \"streaming\", or \"auto\" (default)\n * @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)\n *\n * @example\n * ```typescript\n * // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered\n * const node = yield* createVideoResizeNode(\"resize-1\", {\n * width: 1280,\n * height: 720,\n * aspectRatio: \"keep\",\n * scaling: \"bicubic\"\n * }, {\n * naming: { mode: \"auto\" }\n * });\n *\n * // Force buffered mode for small files\n * const nodeBuffered = yield* createVideoResizeNode(\"resize-2\", {\n * width: 1920,\n * height: 1080\n * }, {\n * mode: \"buffered\",\n * naming: { mode: \"auto\" }\n * });\n *\n * // Force streaming mode for memory efficiency\n * const nodeStreaming = yield* createVideoResizeNode(\"resize-3\", {\n * width: 1280,\n * height: 720\n * }, {\n * mode: \"streaming\",\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createVideoResizeNode(\n id: string,\n params: ResizeVideoParams,\n options?: {\n keepOutput?: boolean;\n naming?: FileNamingConfig;\n mode?: TransformMode;\n streamingConfig?: StreamingConfig;\n },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n // Determine if streaming is available and requested\n const supportsStreaming = videoService.supportsStreaming ?? false;\n const requestedMode = options?.mode ?? \"auto\";\n\n // If streaming requested but not supported, fall back to buffered\n const effectiveMode: TransformMode =\n requestedMode === \"buffered\"\n ? \"buffered\"\n : supportsStreaming\n ? requestedMode\n : \"buffered\";\n\n // Use video-specific streaming config as default\n const streamingConfig: StreamingConfig = {\n ...DEFAULT_VIDEO_STREAMING_CONFIG,\n ...options?.streamingConfig,\n };\n\n // Build naming config with auto suffix for video resize\n const namingConfig: FileNamingConfig | undefined = options?.naming\n ? {\n ...options.naming,\n autoSuffix:\n options.naming.autoSuffix ??\n ((ctx) => `${ctx.width ?? params.width}x${ctx.height ?? params.height}`),\n }\n : undefined;\n\n return yield* createTransformNode({\n id,\n name: \"Resize Video\",\n description: \"Changes video resolution\",\n nodeTypeId: \"resize-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n naming: namingConfig,\n nodeType: \"resize-video\",\n namingVars: { width: params.width, height: params.height },\n mode: effectiveMode,\n streamingConfig,\n // Buffered transform\n transform: (inputBytes) =>\n Effect.map(videoService.resize(inputBytes, params), (resizedBytes) => ({\n bytes: resizedBytes,\n })),\n // Streaming transform\n streamingTransform: videoService.resizeStream\n ? (inputStream) =>\n Effect.gen(function* () {\n const resizeStreamFn = videoService.resizeStream;\n if (!resizeStreamFn) {\n throw new Error(\"resizeStream not available\");\n }\n const outputStream = yield* resizeStreamFn(inputStream, params);\n return { stream: outputStream };\n })\n : undefined,\n });\n });\n}\n","import {\n applyFileNaming,\n buildNamingContext,\n createTransformNode,\n type ExtractFrameVideoParams,\n type FileNamingConfig,\n getBaseName,\n STORAGE_OUTPUT_TYPE_ID,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Creates a Thumbnail generation node\n *\n * Extracts a single frame from the video as an image (JPEG or PNG).\n *\n * @param id - Unique node identifier\n * @param params - Frame extraction parameters\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `thumb`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"video.mp4\" -> \"video-thumb.jpg\"\n * const node = yield* createVideoThumbnailNode(\"thumbnail-1\", {\n * timestamp: 15,\n * format: \"jpeg\",\n * quality: 85\n * }, {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createVideoThumbnailNode(\n id: string,\n params: ExtractFrameVideoParams,\n options?: { keepOutput?: boolean; naming?: FileNamingConfig },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n const format = params.format || \"jpeg\";\n\n return yield* createTransformNode({\n id,\n name: \"Generate Thumbnail\",\n description: \"Extracts a frame from video as an image\",\n nodeTypeId: \"thumbnail-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n // Note: naming is handled in transform since format changes extension\n nodeType: \"thumbnail\",\n namingVars: { format },\n transform: (inputBytes, file) =>\n Effect.map(\n videoService.extractFrame(inputBytes, params),\n (imageBytes) => {\n // Map output to image MIME type and extension\n const mimeType = format === \"png\" ? \"image/png\" : \"image/jpeg\";\n const extension = format === \"png\" ? \"png\" : \"jpg\";\n\n // Get original fileName\n const fileName = file.metadata?.fileName;\n let newFileName: string | undefined;\n\n if (fileName && typeof fileName === \"string\") {\n // Apply naming if configured\n if (options?.naming) {\n const namingConfig: FileNamingConfig = {\n ...options.naming,\n autoSuffix: options.naming.autoSuffix ?? (() => \"thumb\"),\n };\n const namingContext = buildNamingContext(\n file,\n {\n flowId: file.flow?.flowId ?? \"\",\n jobId: file.flow?.jobId ?? \"\",\n nodeId: id,\n nodeType: \"thumbnail\",\n },\n { format },\n );\n // Apply naming to get base name with suffix\n const namedFile = applyFileNaming(file, namingContext, namingConfig);\n // Replace extension with image extension\n newFileName = `${getBaseName(namedFile)}.${extension}`;\n } else {\n // No naming config, just update extension\n newFileName = fileName.replace(/\\.[^.]+$/, `.${extension}`);\n }\n }\n\n return {\n bytes: imageBytes,\n type: mimeType,\n fileName: newFileName,\n };\n },\n ),\n });\n });\n}\n","import {\n applyFileNaming,\n buildNamingContext,\n createTransformNode,\n type FileNamingConfig,\n getBaseName,\n STORAGE_OUTPUT_TYPE_ID,\n type StreamingConfig,\n type TranscodeVideoParams,\n type TransformMode,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n// Default streaming config for video processing\nconst DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig = {\n fileSizeThreshold: 10_000_000, // 10MB threshold for video\n chunkSize: 1_048_576, // 1MB chunks\n};\n\n// Map video format to MIME type\nconst formatToMimeType: Record<TranscodeVideoParams[\"format\"], string> = {\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mov: \"video/quicktime\",\n avi: \"video/x-msvideo\",\n};\n\n// Map video format to file extension\nconst formatToExtension: Record<TranscodeVideoParams[\"format\"], string> = {\n mp4: \"mp4\",\n webm: \"webm\",\n mov: \"mov\",\n avi: \"avi\",\n};\n\n/**\n * Creates a Transcode video processing node\n *\n * Converts video to specified format and codec, optionally adjusting bitrates.\n *\n * Supports both buffered and streaming modes for memory-efficient processing\n * of large videos. In streaming mode, the output is streamed directly to storage,\n * reducing peak memory usage.\n *\n * @param id - Unique node identifier\n * @param params - Transcode parameters\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `${format}`)\n * @param options.mode - Transform mode: \"buffered\", \"streaming\", or \"auto\" (default)\n * @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)\n *\n * @example\n * ```typescript\n * // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered\n * const node = yield* createTranscodeVideoNode(\"transcode-1\", {\n * format: \"webm\",\n * codec: \"vp9\",\n * videoBitrate: \"1000k\"\n * }, {\n * naming: { mode: \"auto\" }\n * });\n *\n * // Force buffered mode for small files\n * const nodeBuffered = yield* createTranscodeVideoNode(\"transcode-2\", {\n * format: \"mp4\",\n * codec: \"h264\"\n * }, {\n * mode: \"buffered\",\n * naming: { mode: \"auto\" }\n * });\n *\n * // Force streaming mode for memory efficiency\n * const nodeStreaming = yield* createTranscodeVideoNode(\"transcode-3\", {\n * format: \"mp4\",\n * codec: \"h264\"\n * }, {\n * mode: \"streaming\",\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createTranscodeVideoNode(\n id: string,\n params: TranscodeVideoParams,\n options?: {\n keepOutput?: boolean;\n naming?: FileNamingConfig;\n mode?: TransformMode;\n streamingConfig?: StreamingConfig;\n },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n // Determine if streaming is available and requested\n const supportsStreaming = videoService.supportsStreaming ?? false;\n const requestedMode = options?.mode ?? \"auto\";\n\n // If streaming requested but not supported, fall back to buffered\n const effectiveMode: TransformMode =\n requestedMode === \"buffered\"\n ? \"buffered\"\n : supportsStreaming\n ? requestedMode\n : \"buffered\";\n\n // Use video-specific streaming config as default\n const streamingConfig: StreamingConfig = {\n ...DEFAULT_VIDEO_STREAMING_CONFIG,\n ...options?.streamingConfig,\n };\n\n // Helper to build output metadata\n const buildOutputMetadata = (file: {\n metadata?: Record<string, unknown>;\n flow?: { flowId?: string; jobId?: string };\n }) => {\n const newType = formatToMimeType[params.format];\n const newExtension = formatToExtension[params.format];\n\n const fileName = file.metadata?.fileName;\n let newFileName: string | undefined;\n\n if (fileName && typeof fileName === \"string\") {\n if (options?.naming) {\n const namingConfig: FileNamingConfig = {\n ...options.naming,\n autoSuffix:\n options.naming.autoSuffix ?? ((ctx) => ctx.format ?? params.format),\n };\n const namingContext = buildNamingContext(\n file as Parameters<typeof buildNamingContext>[0],\n {\n flowId: file.flow?.flowId ?? \"\",\n jobId: file.flow?.jobId ?? \"\",\n nodeId: id,\n nodeType: \"transcode\",\n },\n { format: params.format },\n );\n const namedFile = applyFileNaming(\n file as Parameters<typeof applyFileNaming>[0],\n namingContext,\n namingConfig,\n );\n newFileName = `${getBaseName(namedFile)}.${newExtension}`;\n } else {\n newFileName = fileName.replace(/\\.[^.]+$/, `.${newExtension}`);\n }\n }\n\n return { newType, newFileName };\n };\n\n return yield* createTransformNode({\n id,\n name: \"Transcode\",\n description: \"Converts video to specified format and codec\",\n nodeTypeId: \"transcode-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n nodeType: \"transcode\",\n namingVars: { format: params.format },\n mode: effectiveMode,\n streamingConfig,\n // Buffered transform\n transform: (inputBytes, file) =>\n Effect.map(\n videoService.transcode(inputBytes, params),\n (transcodedBytes) => {\n const { newType, newFileName } = buildOutputMetadata(file);\n return {\n bytes: transcodedBytes,\n type: newType,\n fileName: newFileName,\n };\n },\n ),\n // Streaming transform\n streamingTransform: videoService.transcodeStream\n ? (inputStream, file) =>\n Effect.gen(function* () {\n const transcodeStreamFn = videoService.transcodeStream;\n if (!transcodeStreamFn) {\n throw new Error(\"transcodeStream not available\");\n }\n const outputStream = yield* transcodeStreamFn(inputStream, params);\n const { newType, newFileName } = buildOutputMetadata(file);\n return {\n stream: outputStream,\n type: newType,\n fileName: newFileName,\n };\n })\n : undefined,\n });\n });\n}\n","import { UploadistaError } from \"@uploadista/core/errors\";\nimport {\n createTransformNode,\n type FileNamingConfig,\n STORAGE_OUTPUT_TYPE_ID,\n type StreamingConfig,\n type TransformMode,\n type TrimVideoParams,\n VideoPlugin,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n// Default streaming config for video processing\nconst DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig = {\n fileSizeThreshold: 10_000_000, // 10MB threshold for video\n chunkSize: 1_048_576, // 1MB chunks\n};\n\n/**\n * Creates a Trim video processing node\n *\n * Extracts a segment from the video by time range.\n *\n * Supports both buffered and streaming modes for memory-efficient processing\n * of large videos. In streaming mode, the output is streamed directly to storage,\n * reducing peak memory usage.\n *\n * @param id - Unique node identifier\n * @param params - Trim parameters\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `trimmed`)\n * @param options.mode - Transform mode: \"buffered\", \"streaming\", or \"auto\" (default)\n * @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)\n *\n * @example\n * ```typescript\n * // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered\n * const node = yield* createTrimVideoNode(\"trim-1\", {\n * startTime: 10,\n * endTime: 30\n * }, {\n * naming: { mode: \"auto\" }\n * });\n *\n * // Force buffered mode for small files\n * const nodeBuffered = yield* createTrimVideoNode(\"trim-2\", {\n * startTime: 0,\n * duration: 60\n * }, {\n * mode: \"buffered\",\n * naming: { mode: \"auto\" }\n * });\n *\n * // Force streaming mode for memory efficiency\n * const nodeStreaming = yield* createTrimVideoNode(\"trim-3\", {\n * startTime: 5,\n * endTime: 25\n * }, {\n * mode: \"streaming\",\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createTrimVideoNode(\n id: string,\n params: TrimVideoParams,\n options?: {\n keepOutput?: boolean;\n naming?: FileNamingConfig;\n mode?: TransformMode;\n streamingConfig?: StreamingConfig;\n },\n) {\n return Effect.gen(function* () {\n const videoService = yield* VideoPlugin;\n\n // Validate params\n if (params.endTime !== undefined && params.endTime <= params.startTime) {\n return yield* UploadistaError.fromCode(\"VALIDATION_ERROR\", {\n body: \"endTime must be greater than startTime\",\n details: { params },\n }).toEffect();\n }\n\n if (\n params.duration !== undefined &&\n params.endTime !== undefined &&\n params.duration !== params.endTime - params.startTime\n ) {\n return yield* UploadistaError.fromCode(\"VALIDATION_ERROR\", {\n body: \"Cannot specify both endTime and duration with conflicting values\",\n details: { params },\n }).toEffect();\n }\n\n if (params.duration !== undefined && params.duration <= 0) {\n return yield* UploadistaError.fromCode(\"VALIDATION_ERROR\", {\n body: \"duration must be greater than 0\",\n details: { params },\n }).toEffect();\n }\n\n // Determine if streaming is available and requested\n const supportsStreaming = videoService.supportsStreaming ?? false;\n const requestedMode = options?.mode ?? \"auto\";\n\n // If streaming requested but not supported, fall back to buffered\n const effectiveMode: TransformMode =\n requestedMode === \"buffered\"\n ? \"buffered\"\n : supportsStreaming\n ? requestedMode\n : \"buffered\";\n\n // Use video-specific streaming config as default\n const streamingConfig: StreamingConfig = {\n ...DEFAULT_VIDEO_STREAMING_CONFIG,\n ...options?.streamingConfig,\n };\n\n // Build naming config with auto suffix for trim\n const namingConfig: FileNamingConfig | undefined = options?.naming\n ? {\n ...options.naming,\n autoSuffix: options.naming.autoSuffix ?? (() => \"trimmed\"),\n }\n : undefined;\n\n return yield* createTransformNode({\n id,\n name: \"Trim Video\",\n description: \"Extracts a segment from the video\",\n nodeTypeId: \"trim-video\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n naming: namingConfig,\n nodeType: \"trim\",\n mode: effectiveMode,\n streamingConfig,\n // Buffered transform\n transform: (inputBytes) =>\n Effect.map(videoService.trim(inputBytes, params), (trimmedBytes) => ({\n bytes: trimmedBytes,\n })),\n // Streaming transform\n streamingTransform: videoService.trimStream\n ? (inputStream) =>\n Effect.gen(function* () {\n const trimStreamFn = videoService.trimStream;\n if (!trimStreamFn) {\n throw new Error(\"trimStream not available\");\n }\n const outputStream = yield* trimStreamFn(inputStream, params);\n return { stream: outputStream };\n })\n : undefined,\n });\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uploadista/flow-videos-nodes",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.20-beta.2",
|
|
5
5
|
"description": "Video processing nodes for Uploadista Flow",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Uploadista",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@uploadista/core": "0.0.
|
|
17
|
+
"@uploadista/core": "0.0.20-beta.2"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"effect": "^3.0.0",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@effect/vitest": "0.27.0",
|
|
25
25
|
"@types/node": "24.10.1",
|
|
26
|
-
"effect": "3.19.
|
|
27
|
-
"tsdown": "0.
|
|
26
|
+
"effect": "3.19.9",
|
|
27
|
+
"tsdown": "0.17.1",
|
|
28
28
|
"vitest": "4.0.15",
|
|
29
29
|
"zod": "4.1.13",
|
|
30
|
-
"@uploadista/typescript-config": "0.0.
|
|
30
|
+
"@uploadista/typescript-config": "0.0.20-beta.2"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsdown",
|
package/src/resize-node.ts
CHANGED
|
@@ -3,24 +3,38 @@ import {
|
|
|
3
3
|
type FileNamingConfig,
|
|
4
4
|
type ResizeVideoParams,
|
|
5
5
|
STORAGE_OUTPUT_TYPE_ID,
|
|
6
|
+
type StreamingConfig,
|
|
7
|
+
type TransformMode,
|
|
6
8
|
VideoPlugin,
|
|
7
9
|
} from "@uploadista/core/flow";
|
|
8
10
|
import { Effect } from "effect";
|
|
9
11
|
|
|
12
|
+
// Default streaming config for video processing
|
|
13
|
+
const DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig = {
|
|
14
|
+
fileSizeThreshold: 10_000_000, // 10MB threshold for video
|
|
15
|
+
chunkSize: 1_048_576, // 1MB chunks
|
|
16
|
+
};
|
|
17
|
+
|
|
10
18
|
/**
|
|
11
19
|
* Creates a Resize video processing node
|
|
12
20
|
*
|
|
13
21
|
* Changes video resolution while optionally maintaining aspect ratio.
|
|
14
22
|
*
|
|
23
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
24
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
25
|
+
* reducing peak memory usage.
|
|
26
|
+
*
|
|
15
27
|
* @param id - Unique node identifier
|
|
16
28
|
* @param params - Resize parameters
|
|
17
29
|
* @param options - Optional configuration
|
|
18
30
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
19
31
|
* @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)
|
|
32
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
33
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
20
34
|
*
|
|
21
35
|
* @example
|
|
22
36
|
* ```typescript
|
|
23
|
-
* //
|
|
37
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
24
38
|
* const node = yield* createVideoResizeNode("resize-1", {
|
|
25
39
|
* width: 1280,
|
|
26
40
|
* height: 720,
|
|
@@ -29,16 +43,57 @@ import { Effect } from "effect";
|
|
|
29
43
|
* }, {
|
|
30
44
|
* naming: { mode: "auto" }
|
|
31
45
|
* });
|
|
46
|
+
*
|
|
47
|
+
* // Force buffered mode for small files
|
|
48
|
+
* const nodeBuffered = yield* createVideoResizeNode("resize-2", {
|
|
49
|
+
* width: 1920,
|
|
50
|
+
* height: 1080
|
|
51
|
+
* }, {
|
|
52
|
+
* mode: "buffered",
|
|
53
|
+
* naming: { mode: "auto" }
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* // Force streaming mode for memory efficiency
|
|
57
|
+
* const nodeStreaming = yield* createVideoResizeNode("resize-3", {
|
|
58
|
+
* width: 1280,
|
|
59
|
+
* height: 720
|
|
60
|
+
* }, {
|
|
61
|
+
* mode: "streaming",
|
|
62
|
+
* naming: { mode: "auto" }
|
|
63
|
+
* });
|
|
32
64
|
* ```
|
|
33
65
|
*/
|
|
34
66
|
export function createVideoResizeNode(
|
|
35
67
|
id: string,
|
|
36
68
|
params: ResizeVideoParams,
|
|
37
|
-
options?: {
|
|
69
|
+
options?: {
|
|
70
|
+
keepOutput?: boolean;
|
|
71
|
+
naming?: FileNamingConfig;
|
|
72
|
+
mode?: TransformMode;
|
|
73
|
+
streamingConfig?: StreamingConfig;
|
|
74
|
+
},
|
|
38
75
|
) {
|
|
39
76
|
return Effect.gen(function* () {
|
|
40
77
|
const videoService = yield* VideoPlugin;
|
|
41
78
|
|
|
79
|
+
// Determine if streaming is available and requested
|
|
80
|
+
const supportsStreaming = videoService.supportsStreaming ?? false;
|
|
81
|
+
const requestedMode = options?.mode ?? "auto";
|
|
82
|
+
|
|
83
|
+
// If streaming requested but not supported, fall back to buffered
|
|
84
|
+
const effectiveMode: TransformMode =
|
|
85
|
+
requestedMode === "buffered"
|
|
86
|
+
? "buffered"
|
|
87
|
+
: supportsStreaming
|
|
88
|
+
? requestedMode
|
|
89
|
+
: "buffered";
|
|
90
|
+
|
|
91
|
+
// Use video-specific streaming config as default
|
|
92
|
+
const streamingConfig: StreamingConfig = {
|
|
93
|
+
...DEFAULT_VIDEO_STREAMING_CONFIG,
|
|
94
|
+
...options?.streamingConfig,
|
|
95
|
+
};
|
|
96
|
+
|
|
42
97
|
// Build naming config with auto suffix for video resize
|
|
43
98
|
const namingConfig: FileNamingConfig | undefined = options?.naming
|
|
44
99
|
? {
|
|
@@ -59,13 +114,25 @@ export function createVideoResizeNode(
|
|
|
59
114
|
naming: namingConfig,
|
|
60
115
|
nodeType: "resize-video",
|
|
61
116
|
namingVars: { width: params.width, height: params.height },
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}),
|
|
117
|
+
mode: effectiveMode,
|
|
118
|
+
streamingConfig,
|
|
119
|
+
// Buffered transform
|
|
120
|
+
transform: (inputBytes) =>
|
|
121
|
+
Effect.map(videoService.resize(inputBytes, params), (resizedBytes) => ({
|
|
122
|
+
bytes: resizedBytes,
|
|
123
|
+
})),
|
|
124
|
+
// Streaming transform
|
|
125
|
+
streamingTransform: videoService.resizeStream
|
|
126
|
+
? (inputStream) =>
|
|
127
|
+
Effect.gen(function* () {
|
|
128
|
+
const resizeStreamFn = videoService.resizeStream;
|
|
129
|
+
if (!resizeStreamFn) {
|
|
130
|
+
throw new Error("resizeStream not available");
|
|
131
|
+
}
|
|
132
|
+
const outputStream = yield* resizeStreamFn(inputStream, params);
|
|
133
|
+
return { stream: outputStream };
|
|
134
|
+
})
|
|
135
|
+
: undefined,
|
|
69
136
|
});
|
|
70
137
|
});
|
|
71
138
|
}
|
package/src/transcode-node.ts
CHANGED
|
@@ -5,11 +5,19 @@ import {
|
|
|
5
5
|
type FileNamingConfig,
|
|
6
6
|
getBaseName,
|
|
7
7
|
STORAGE_OUTPUT_TYPE_ID,
|
|
8
|
+
type StreamingConfig,
|
|
8
9
|
type TranscodeVideoParams,
|
|
10
|
+
type TransformMode,
|
|
9
11
|
VideoPlugin,
|
|
10
12
|
} from "@uploadista/core/flow";
|
|
11
13
|
import { Effect } from "effect";
|
|
12
14
|
|
|
15
|
+
// Default streaming config for video processing
|
|
16
|
+
const DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig = {
|
|
17
|
+
fileSizeThreshold: 10_000_000, // 10MB threshold for video
|
|
18
|
+
chunkSize: 1_048_576, // 1MB chunks
|
|
19
|
+
};
|
|
20
|
+
|
|
13
21
|
// Map video format to MIME type
|
|
14
22
|
const formatToMimeType: Record<TranscodeVideoParams["format"], string> = {
|
|
15
23
|
mp4: "video/mp4",
|
|
@@ -31,15 +39,21 @@ const formatToExtension: Record<TranscodeVideoParams["format"], string> = {
|
|
|
31
39
|
*
|
|
32
40
|
* Converts video to specified format and codec, optionally adjusting bitrates.
|
|
33
41
|
*
|
|
42
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
43
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
44
|
+
* reducing peak memory usage.
|
|
45
|
+
*
|
|
34
46
|
* @param id - Unique node identifier
|
|
35
47
|
* @param params - Transcode parameters
|
|
36
48
|
* @param options - Optional configuration
|
|
37
49
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
38
50
|
* @param options.naming - File naming configuration (auto suffix: `${format}`)
|
|
51
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
52
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
39
53
|
*
|
|
40
54
|
* @example
|
|
41
55
|
* ```typescript
|
|
42
|
-
* //
|
|
56
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
43
57
|
* const node = yield* createTranscodeVideoNode("transcode-1", {
|
|
44
58
|
* format: "webm",
|
|
45
59
|
* codec: "vp9",
|
|
@@ -47,16 +61,99 @@ const formatToExtension: Record<TranscodeVideoParams["format"], string> = {
|
|
|
47
61
|
* }, {
|
|
48
62
|
* naming: { mode: "auto" }
|
|
49
63
|
* });
|
|
64
|
+
*
|
|
65
|
+
* // Force buffered mode for small files
|
|
66
|
+
* const nodeBuffered = yield* createTranscodeVideoNode("transcode-2", {
|
|
67
|
+
* format: "mp4",
|
|
68
|
+
* codec: "h264"
|
|
69
|
+
* }, {
|
|
70
|
+
* mode: "buffered",
|
|
71
|
+
* naming: { mode: "auto" }
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* // Force streaming mode for memory efficiency
|
|
75
|
+
* const nodeStreaming = yield* createTranscodeVideoNode("transcode-3", {
|
|
76
|
+
* format: "mp4",
|
|
77
|
+
* codec: "h264"
|
|
78
|
+
* }, {
|
|
79
|
+
* mode: "streaming",
|
|
80
|
+
* naming: { mode: "auto" }
|
|
81
|
+
* });
|
|
50
82
|
* ```
|
|
51
83
|
*/
|
|
52
84
|
export function createTranscodeVideoNode(
|
|
53
85
|
id: string,
|
|
54
86
|
params: TranscodeVideoParams,
|
|
55
|
-
options?: {
|
|
87
|
+
options?: {
|
|
88
|
+
keepOutput?: boolean;
|
|
89
|
+
naming?: FileNamingConfig;
|
|
90
|
+
mode?: TransformMode;
|
|
91
|
+
streamingConfig?: StreamingConfig;
|
|
92
|
+
},
|
|
56
93
|
) {
|
|
57
94
|
return Effect.gen(function* () {
|
|
58
95
|
const videoService = yield* VideoPlugin;
|
|
59
96
|
|
|
97
|
+
// Determine if streaming is available and requested
|
|
98
|
+
const supportsStreaming = videoService.supportsStreaming ?? false;
|
|
99
|
+
const requestedMode = options?.mode ?? "auto";
|
|
100
|
+
|
|
101
|
+
// If streaming requested but not supported, fall back to buffered
|
|
102
|
+
const effectiveMode: TransformMode =
|
|
103
|
+
requestedMode === "buffered"
|
|
104
|
+
? "buffered"
|
|
105
|
+
: supportsStreaming
|
|
106
|
+
? requestedMode
|
|
107
|
+
: "buffered";
|
|
108
|
+
|
|
109
|
+
// Use video-specific streaming config as default
|
|
110
|
+
const streamingConfig: StreamingConfig = {
|
|
111
|
+
...DEFAULT_VIDEO_STREAMING_CONFIG,
|
|
112
|
+
...options?.streamingConfig,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Helper to build output metadata
|
|
116
|
+
const buildOutputMetadata = (file: {
|
|
117
|
+
metadata?: Record<string, unknown>;
|
|
118
|
+
flow?: { flowId?: string; jobId?: string };
|
|
119
|
+
}) => {
|
|
120
|
+
const newType = formatToMimeType[params.format];
|
|
121
|
+
const newExtension = formatToExtension[params.format];
|
|
122
|
+
|
|
123
|
+
const fileName = file.metadata?.fileName;
|
|
124
|
+
let newFileName: string | undefined;
|
|
125
|
+
|
|
126
|
+
if (fileName && typeof fileName === "string") {
|
|
127
|
+
if (options?.naming) {
|
|
128
|
+
const namingConfig: FileNamingConfig = {
|
|
129
|
+
...options.naming,
|
|
130
|
+
autoSuffix:
|
|
131
|
+
options.naming.autoSuffix ?? ((ctx) => ctx.format ?? params.format),
|
|
132
|
+
};
|
|
133
|
+
const namingContext = buildNamingContext(
|
|
134
|
+
file as Parameters<typeof buildNamingContext>[0],
|
|
135
|
+
{
|
|
136
|
+
flowId: file.flow?.flowId ?? "",
|
|
137
|
+
jobId: file.flow?.jobId ?? "",
|
|
138
|
+
nodeId: id,
|
|
139
|
+
nodeType: "transcode",
|
|
140
|
+
},
|
|
141
|
+
{ format: params.format },
|
|
142
|
+
);
|
|
143
|
+
const namedFile = applyFileNaming(
|
|
144
|
+
file as Parameters<typeof applyFileNaming>[0],
|
|
145
|
+
namingContext,
|
|
146
|
+
namingConfig,
|
|
147
|
+
);
|
|
148
|
+
newFileName = `${getBaseName(namedFile)}.${newExtension}`;
|
|
149
|
+
} else {
|
|
150
|
+
newFileName = fileName.replace(/\.[^.]+$/, `.${newExtension}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { newType, newFileName };
|
|
155
|
+
};
|
|
156
|
+
|
|
60
157
|
return yield* createTransformNode({
|
|
61
158
|
id,
|
|
62
159
|
name: "Transcode",
|
|
@@ -64,49 +161,16 @@ export function createTranscodeVideoNode(
|
|
|
64
161
|
nodeTypeId: "transcode-video",
|
|
65
162
|
outputTypeId: STORAGE_OUTPUT_TYPE_ID,
|
|
66
163
|
keepOutput: options?.keepOutput,
|
|
67
|
-
// Note: naming is handled in transform since format changes extension
|
|
68
164
|
nodeType: "transcode",
|
|
69
165
|
namingVars: { format: params.format },
|
|
166
|
+
mode: effectiveMode,
|
|
167
|
+
streamingConfig,
|
|
168
|
+
// Buffered transform
|
|
70
169
|
transform: (inputBytes, file) =>
|
|
71
170
|
Effect.map(
|
|
72
171
|
videoService.transcode(inputBytes, params),
|
|
73
172
|
(transcodedBytes) => {
|
|
74
|
-
|
|
75
|
-
const newType = formatToMimeType[params.format];
|
|
76
|
-
const newExtension = formatToExtension[params.format];
|
|
77
|
-
|
|
78
|
-
// Get original fileName
|
|
79
|
-
const fileName = file.metadata?.fileName;
|
|
80
|
-
let newFileName: string | undefined;
|
|
81
|
-
|
|
82
|
-
if (fileName && typeof fileName === "string") {
|
|
83
|
-
// Apply naming if configured
|
|
84
|
-
if (options?.naming) {
|
|
85
|
-
const namingConfig: FileNamingConfig = {
|
|
86
|
-
...options.naming,
|
|
87
|
-
autoSuffix:
|
|
88
|
-
options.naming.autoSuffix ?? ((ctx) => ctx.format ?? params.format),
|
|
89
|
-
};
|
|
90
|
-
const namingContext = buildNamingContext(
|
|
91
|
-
file,
|
|
92
|
-
{
|
|
93
|
-
flowId: file.flow?.flowId ?? "",
|
|
94
|
-
jobId: file.flow?.jobId ?? "",
|
|
95
|
-
nodeId: id,
|
|
96
|
-
nodeType: "transcode",
|
|
97
|
-
},
|
|
98
|
-
{ format: params.format },
|
|
99
|
-
);
|
|
100
|
-
// Apply naming to get base name with suffix
|
|
101
|
-
const namedFile = applyFileNaming(file, namingContext, namingConfig);
|
|
102
|
-
// Replace extension with new format extension
|
|
103
|
-
newFileName = `${getBaseName(namedFile)}.${newExtension}`;
|
|
104
|
-
} else {
|
|
105
|
-
// No naming config, just update extension
|
|
106
|
-
newFileName = fileName.replace(/\.[^.]+$/, `.${newExtension}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
173
|
+
const { newType, newFileName } = buildOutputMetadata(file);
|
|
110
174
|
return {
|
|
111
175
|
bytes: transcodedBytes,
|
|
112
176
|
type: newType,
|
|
@@ -114,6 +178,23 @@ export function createTranscodeVideoNode(
|
|
|
114
178
|
};
|
|
115
179
|
},
|
|
116
180
|
),
|
|
181
|
+
// Streaming transform
|
|
182
|
+
streamingTransform: videoService.transcodeStream
|
|
183
|
+
? (inputStream, file) =>
|
|
184
|
+
Effect.gen(function* () {
|
|
185
|
+
const transcodeStreamFn = videoService.transcodeStream;
|
|
186
|
+
if (!transcodeStreamFn) {
|
|
187
|
+
throw new Error("transcodeStream not available");
|
|
188
|
+
}
|
|
189
|
+
const outputStream = yield* transcodeStreamFn(inputStream, params);
|
|
190
|
+
const { newType, newFileName } = buildOutputMetadata(file);
|
|
191
|
+
return {
|
|
192
|
+
stream: outputStream,
|
|
193
|
+
type: newType,
|
|
194
|
+
fileName: newFileName,
|
|
195
|
+
};
|
|
196
|
+
})
|
|
197
|
+
: undefined,
|
|
117
198
|
});
|
|
118
199
|
});
|
|
119
200
|
}
|
package/src/trim-node.ts
CHANGED
|
@@ -3,37 +3,74 @@ import {
|
|
|
3
3
|
createTransformNode,
|
|
4
4
|
type FileNamingConfig,
|
|
5
5
|
STORAGE_OUTPUT_TYPE_ID,
|
|
6
|
+
type StreamingConfig,
|
|
7
|
+
type TransformMode,
|
|
6
8
|
type TrimVideoParams,
|
|
7
9
|
VideoPlugin,
|
|
8
10
|
} from "@uploadista/core/flow";
|
|
9
11
|
import { Effect } from "effect";
|
|
10
12
|
|
|
13
|
+
// Default streaming config for video processing
|
|
14
|
+
const DEFAULT_VIDEO_STREAMING_CONFIG: StreamingConfig = {
|
|
15
|
+
fileSizeThreshold: 10_000_000, // 10MB threshold for video
|
|
16
|
+
chunkSize: 1_048_576, // 1MB chunks
|
|
17
|
+
};
|
|
18
|
+
|
|
11
19
|
/**
|
|
12
20
|
* Creates a Trim video processing node
|
|
13
21
|
*
|
|
14
22
|
* Extracts a segment from the video by time range.
|
|
15
23
|
*
|
|
24
|
+
* Supports both buffered and streaming modes for memory-efficient processing
|
|
25
|
+
* of large videos. In streaming mode, the output is streamed directly to storage,
|
|
26
|
+
* reducing peak memory usage.
|
|
27
|
+
*
|
|
16
28
|
* @param id - Unique node identifier
|
|
17
29
|
* @param params - Trim parameters
|
|
18
30
|
* @param options - Optional configuration
|
|
19
31
|
* @param options.keepOutput - Whether to keep output in flow results
|
|
20
32
|
* @param options.naming - File naming configuration (auto suffix: `trimmed`)
|
|
33
|
+
* @param options.mode - Transform mode: "buffered", "streaming", or "auto" (default)
|
|
34
|
+
* @param options.streamingConfig - Streaming configuration (file size threshold, chunk size)
|
|
21
35
|
*
|
|
22
36
|
* @example
|
|
23
37
|
* ```typescript
|
|
24
|
-
* //
|
|
38
|
+
* // Auto mode (default) - uses streaming for files > 10MB, otherwise buffered
|
|
25
39
|
* const node = yield* createTrimVideoNode("trim-1", {
|
|
26
40
|
* startTime: 10,
|
|
27
41
|
* endTime: 30
|
|
28
42
|
* }, {
|
|
29
43
|
* naming: { mode: "auto" }
|
|
30
44
|
* });
|
|
45
|
+
*
|
|
46
|
+
* // Force buffered mode for small files
|
|
47
|
+
* const nodeBuffered = yield* createTrimVideoNode("trim-2", {
|
|
48
|
+
* startTime: 0,
|
|
49
|
+
* duration: 60
|
|
50
|
+
* }, {
|
|
51
|
+
* mode: "buffered",
|
|
52
|
+
* naming: { mode: "auto" }
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // Force streaming mode for memory efficiency
|
|
56
|
+
* const nodeStreaming = yield* createTrimVideoNode("trim-3", {
|
|
57
|
+
* startTime: 5,
|
|
58
|
+
* endTime: 25
|
|
59
|
+
* }, {
|
|
60
|
+
* mode: "streaming",
|
|
61
|
+
* naming: { mode: "auto" }
|
|
62
|
+
* });
|
|
31
63
|
* ```
|
|
32
64
|
*/
|
|
33
65
|
export function createTrimVideoNode(
|
|
34
66
|
id: string,
|
|
35
67
|
params: TrimVideoParams,
|
|
36
|
-
options?: {
|
|
68
|
+
options?: {
|
|
69
|
+
keepOutput?: boolean;
|
|
70
|
+
naming?: FileNamingConfig;
|
|
71
|
+
mode?: TransformMode;
|
|
72
|
+
streamingConfig?: StreamingConfig;
|
|
73
|
+
},
|
|
37
74
|
) {
|
|
38
75
|
return Effect.gen(function* () {
|
|
39
76
|
const videoService = yield* VideoPlugin;
|
|
@@ -64,6 +101,24 @@ export function createTrimVideoNode(
|
|
|
64
101
|
}).toEffect();
|
|
65
102
|
}
|
|
66
103
|
|
|
104
|
+
// Determine if streaming is available and requested
|
|
105
|
+
const supportsStreaming = videoService.supportsStreaming ?? false;
|
|
106
|
+
const requestedMode = options?.mode ?? "auto";
|
|
107
|
+
|
|
108
|
+
// If streaming requested but not supported, fall back to buffered
|
|
109
|
+
const effectiveMode: TransformMode =
|
|
110
|
+
requestedMode === "buffered"
|
|
111
|
+
? "buffered"
|
|
112
|
+
: supportsStreaming
|
|
113
|
+
? requestedMode
|
|
114
|
+
: "buffered";
|
|
115
|
+
|
|
116
|
+
// Use video-specific streaming config as default
|
|
117
|
+
const streamingConfig: StreamingConfig = {
|
|
118
|
+
...DEFAULT_VIDEO_STREAMING_CONFIG,
|
|
119
|
+
...options?.streamingConfig,
|
|
120
|
+
};
|
|
121
|
+
|
|
67
122
|
// Build naming config with auto suffix for trim
|
|
68
123
|
const namingConfig: FileNamingConfig | undefined = options?.naming
|
|
69
124
|
? {
|
|
@@ -81,13 +136,25 @@ export function createTrimVideoNode(
|
|
|
81
136
|
keepOutput: options?.keepOutput,
|
|
82
137
|
naming: namingConfig,
|
|
83
138
|
nodeType: "trim",
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}),
|
|
139
|
+
mode: effectiveMode,
|
|
140
|
+
streamingConfig,
|
|
141
|
+
// Buffered transform
|
|
142
|
+
transform: (inputBytes) =>
|
|
143
|
+
Effect.map(videoService.trim(inputBytes, params), (trimmedBytes) => ({
|
|
144
|
+
bytes: trimmedBytes,
|
|
145
|
+
})),
|
|
146
|
+
// Streaming transform
|
|
147
|
+
streamingTransform: videoService.trimStream
|
|
148
|
+
? (inputStream) =>
|
|
149
|
+
Effect.gen(function* () {
|
|
150
|
+
const trimStreamFn = videoService.trimStream;
|
|
151
|
+
if (!trimStreamFn) {
|
|
152
|
+
throw new Error("trimStream not available");
|
|
153
|
+
}
|
|
154
|
+
const outputStream = yield* trimStreamFn(inputStream, params);
|
|
155
|
+
return { stream: outputStream };
|
|
156
|
+
})
|
|
157
|
+
: undefined,
|
|
91
158
|
});
|
|
92
159
|
});
|
|
93
160
|
}
|