@editframe/api 0.7.0-beta.8 → 0.8.0-beta.10

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 (59) hide show
  1. package/dist/CHUNK_SIZE_BYTES.d.ts +1 -0
  2. package/dist/CHUNK_SIZE_BYTES.js +7 -0
  3. package/dist/client.d.ts +6 -0
  4. package/dist/client.js +13 -7
  5. package/dist/client.test.d.ts +1 -0
  6. package/dist/index.d.ts +8 -0
  7. package/dist/index.js +11 -1
  8. package/dist/readableFromBuffers.d.ts +2 -0
  9. package/dist/resources/caption-file.d.ts +39 -0
  10. package/dist/resources/caption-file.js +28 -26
  11. package/dist/resources/caption-file.test.d.ts +1 -0
  12. package/dist/resources/image-file.d.ts +31 -0
  13. package/dist/resources/image-file.js +23 -27
  14. package/dist/resources/image-file.test.d.ts +1 -0
  15. package/dist/resources/isobmff-file.d.ts +19 -0
  16. package/dist/resources/isobmff-file.js +17 -23
  17. package/dist/resources/isobmff-file.test.d.ts +1 -0
  18. package/dist/resources/isobmff-track.d.ts +270 -0
  19. package/dist/resources/isobmff-track.js +18 -31
  20. package/dist/resources/isobmff-track.test.d.ts +1 -0
  21. package/dist/resources/renders.d.ts +34 -0
  22. package/dist/resources/renders.js +17 -21
  23. package/dist/resources/renders.test.d.ts +1 -0
  24. package/dist/resources/unprocessed-file.d.ts +45 -0
  25. package/dist/resources/unprocessed-file.js +133 -0
  26. package/dist/resources/unprocessed-file.test.d.ts +1 -0
  27. package/dist/resources/url-token.d.ts +5 -0
  28. package/dist/resources/url-token.js +20 -0
  29. package/dist/resources/url-token.test.d.ts +1 -0
  30. package/dist/streamChunker.d.ts +2 -0
  31. package/dist/streamChunker.js +17 -0
  32. package/dist/streamChunker.test.d.ts +1 -0
  33. package/dist/uploadChunks.d.ts +10 -0
  34. package/dist/uploadChunks.js +65 -0
  35. package/dist/uploadChunks.test.d.ts +1 -0
  36. package/package.json +11 -11
  37. package/src/resources/caption-file.test.ts +124 -0
  38. package/src/resources/caption-file.ts +50 -25
  39. package/src/resources/image-file.test.ts +138 -0
  40. package/src/resources/image-file.ts +29 -26
  41. package/src/resources/isobmff-file.test.ts +108 -0
  42. package/src/resources/isobmff-file.ts +20 -23
  43. package/src/resources/isobmff-track.test.ts +152 -0
  44. package/src/resources/isobmff-track.ts +23 -32
  45. package/src/resources/renders.test.ts +112 -0
  46. package/src/resources/renders.ts +20 -21
  47. package/src/resources/test-av-file.txt +1 -0
  48. package/src/resources/unprocessed-file.test.ts +312 -0
  49. package/src/resources/unprocessed-file.ts +189 -0
  50. package/src/resources/url-token.test.ts +46 -0
  51. package/src/resources/url-token.ts +27 -0
  52. package/dist/client.cjs +0 -29
  53. package/dist/index.cjs +0 -24
  54. package/dist/resources/caption-file.cjs +0 -56
  55. package/dist/resources/image-file.cjs +0 -52
  56. package/dist/resources/isobmff-file.cjs +0 -56
  57. package/dist/resources/isobmff-track.cjs +0 -71
  58. package/dist/resources/renders.cjs +0 -56
  59. package/src/util/nodeStreamToWebStream.ts +0 -20
@@ -0,0 +1,270 @@
1
+ import { Readable } from 'node:stream';
2
+ import { z } from 'zod';
3
+ import { Client } from '../client.ts';
4
+ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
5
+ file_id: z.ZodString;
6
+ track_id: z.ZodNumber;
7
+ type: z.ZodLiteral<"audio">;
8
+ probe_info: z.ZodObject<{
9
+ index: z.ZodNumber;
10
+ codec_name: z.ZodString;
11
+ codec_long_name: z.ZodString;
12
+ codec_type: z.ZodLiteral<"audio">;
13
+ codec_tag_string: z.ZodString;
14
+ codec_tag: z.ZodString;
15
+ sample_fmt: z.ZodString;
16
+ sample_rate: z.ZodString;
17
+ channels: z.ZodNumber;
18
+ channel_layout: z.ZodString;
19
+ bits_per_sample: z.ZodNumber;
20
+ initial_padding: z.ZodOptional<z.ZodNumber>;
21
+ r_frame_rate: z.ZodString;
22
+ avg_frame_rate: z.ZodString;
23
+ time_base: z.ZodString;
24
+ start_pts: z.ZodNumber;
25
+ start_time: z.ZodNumber;
26
+ duration_ts: z.ZodNumber;
27
+ duration: z.ZodNumber;
28
+ bit_rate: z.ZodString;
29
+ disposition: z.ZodRecord<z.ZodString, z.ZodUnknown>;
30
+ }, "strip", z.ZodTypeAny, {
31
+ index: number;
32
+ codec_name: string;
33
+ codec_long_name: string;
34
+ codec_type: "audio";
35
+ codec_tag_string: string;
36
+ codec_tag: string;
37
+ sample_fmt: string;
38
+ sample_rate: string;
39
+ channels: number;
40
+ channel_layout: string;
41
+ bits_per_sample: number;
42
+ r_frame_rate: string;
43
+ avg_frame_rate: string;
44
+ time_base: string;
45
+ start_pts: number;
46
+ start_time: number;
47
+ duration_ts: number;
48
+ duration: number;
49
+ bit_rate: string;
50
+ disposition: Record<string, unknown>;
51
+ initial_padding?: number | undefined;
52
+ }, {
53
+ index: number;
54
+ codec_name: string;
55
+ codec_long_name: string;
56
+ codec_type: "audio";
57
+ codec_tag_string: string;
58
+ codec_tag: string;
59
+ sample_fmt: string;
60
+ sample_rate: string;
61
+ channels: number;
62
+ channel_layout: string;
63
+ bits_per_sample: number;
64
+ r_frame_rate: string;
65
+ avg_frame_rate: string;
66
+ time_base: string;
67
+ start_pts: number;
68
+ start_time: number;
69
+ duration_ts: number;
70
+ duration: number;
71
+ bit_rate: string;
72
+ disposition: Record<string, unknown>;
73
+ initial_padding?: number | undefined;
74
+ }>;
75
+ duration_ms: z.ZodNumber;
76
+ codec_name: z.ZodString;
77
+ byte_size: z.ZodNumber;
78
+ }, "strip", z.ZodTypeAny, {
79
+ byte_size: number;
80
+ type: "audio";
81
+ codec_name: string;
82
+ file_id: string;
83
+ track_id: number;
84
+ probe_info: {
85
+ index: number;
86
+ codec_name: string;
87
+ codec_long_name: string;
88
+ codec_type: "audio";
89
+ codec_tag_string: string;
90
+ codec_tag: string;
91
+ sample_fmt: string;
92
+ sample_rate: string;
93
+ channels: number;
94
+ channel_layout: string;
95
+ bits_per_sample: number;
96
+ r_frame_rate: string;
97
+ avg_frame_rate: string;
98
+ time_base: string;
99
+ start_pts: number;
100
+ start_time: number;
101
+ duration_ts: number;
102
+ duration: number;
103
+ bit_rate: string;
104
+ disposition: Record<string, unknown>;
105
+ initial_padding?: number | undefined;
106
+ };
107
+ duration_ms: number;
108
+ }, {
109
+ byte_size: number;
110
+ type: "audio";
111
+ codec_name: string;
112
+ file_id: string;
113
+ track_id: number;
114
+ probe_info: {
115
+ index: number;
116
+ codec_name: string;
117
+ codec_long_name: string;
118
+ codec_type: "audio";
119
+ codec_tag_string: string;
120
+ codec_tag: string;
121
+ sample_fmt: string;
122
+ sample_rate: string;
123
+ channels: number;
124
+ channel_layout: string;
125
+ bits_per_sample: number;
126
+ r_frame_rate: string;
127
+ avg_frame_rate: string;
128
+ time_base: string;
129
+ start_pts: number;
130
+ start_time: number;
131
+ duration_ts: number;
132
+ duration: number;
133
+ bit_rate: string;
134
+ disposition: Record<string, unknown>;
135
+ initial_padding?: number | undefined;
136
+ };
137
+ duration_ms: number;
138
+ }>, z.ZodObject<{
139
+ file_id: z.ZodString;
140
+ track_id: z.ZodNumber;
141
+ type: z.ZodLiteral<"video">;
142
+ probe_info: z.ZodObject<{
143
+ index: z.ZodNumber;
144
+ codec_name: z.ZodString;
145
+ codec_long_name: z.ZodString;
146
+ codec_type: z.ZodLiteral<"video">;
147
+ codec_tag_string: z.ZodString;
148
+ codec_tag: z.ZodString;
149
+ width: z.ZodNumber;
150
+ height: z.ZodNumber;
151
+ coded_width: z.ZodNumber;
152
+ coded_height: z.ZodNumber;
153
+ r_frame_rate: z.ZodString;
154
+ avg_frame_rate: z.ZodString;
155
+ time_base: z.ZodString;
156
+ start_pts: z.ZodOptional<z.ZodNumber>;
157
+ start_time: z.ZodOptional<z.ZodNumber>;
158
+ duration_ts: z.ZodOptional<z.ZodNumber>;
159
+ duration: z.ZodOptional<z.ZodNumber>;
160
+ bit_rate: z.ZodOptional<z.ZodString>;
161
+ disposition: z.ZodRecord<z.ZodString, z.ZodUnknown>;
162
+ }, "strip", z.ZodTypeAny, {
163
+ height: number;
164
+ width: number;
165
+ index: number;
166
+ codec_name: string;
167
+ codec_long_name: string;
168
+ codec_type: "video";
169
+ codec_tag_string: string;
170
+ codec_tag: string;
171
+ r_frame_rate: string;
172
+ avg_frame_rate: string;
173
+ time_base: string;
174
+ disposition: Record<string, unknown>;
175
+ coded_width: number;
176
+ coded_height: number;
177
+ start_pts?: number | undefined;
178
+ start_time?: number | undefined;
179
+ duration_ts?: number | undefined;
180
+ duration?: number | undefined;
181
+ bit_rate?: string | undefined;
182
+ }, {
183
+ height: number;
184
+ width: number;
185
+ index: number;
186
+ codec_name: string;
187
+ codec_long_name: string;
188
+ codec_type: "video";
189
+ codec_tag_string: string;
190
+ codec_tag: string;
191
+ r_frame_rate: string;
192
+ avg_frame_rate: string;
193
+ time_base: string;
194
+ disposition: Record<string, unknown>;
195
+ coded_width: number;
196
+ coded_height: number;
197
+ start_pts?: number | undefined;
198
+ start_time?: number | undefined;
199
+ duration_ts?: number | undefined;
200
+ duration?: number | undefined;
201
+ bit_rate?: string | undefined;
202
+ }>;
203
+ duration_ms: z.ZodNumber;
204
+ codec_name: z.ZodString;
205
+ byte_size: z.ZodNumber;
206
+ }, "strip", z.ZodTypeAny, {
207
+ byte_size: number;
208
+ type: "video";
209
+ codec_name: string;
210
+ file_id: string;
211
+ track_id: number;
212
+ probe_info: {
213
+ height: number;
214
+ width: number;
215
+ index: number;
216
+ codec_name: string;
217
+ codec_long_name: string;
218
+ codec_type: "video";
219
+ codec_tag_string: string;
220
+ codec_tag: string;
221
+ r_frame_rate: string;
222
+ avg_frame_rate: string;
223
+ time_base: string;
224
+ disposition: Record<string, unknown>;
225
+ coded_width: number;
226
+ coded_height: number;
227
+ start_pts?: number | undefined;
228
+ start_time?: number | undefined;
229
+ duration_ts?: number | undefined;
230
+ duration?: number | undefined;
231
+ bit_rate?: string | undefined;
232
+ };
233
+ duration_ms: number;
234
+ }, {
235
+ byte_size: number;
236
+ type: "video";
237
+ codec_name: string;
238
+ file_id: string;
239
+ track_id: number;
240
+ probe_info: {
241
+ height: number;
242
+ width: number;
243
+ index: number;
244
+ codec_name: string;
245
+ codec_long_name: string;
246
+ codec_type: "video";
247
+ codec_tag_string: string;
248
+ codec_tag: string;
249
+ r_frame_rate: string;
250
+ avg_frame_rate: string;
251
+ time_base: string;
252
+ disposition: Record<string, unknown>;
253
+ coded_width: number;
254
+ coded_height: number;
255
+ start_pts?: number | undefined;
256
+ start_time?: number | undefined;
257
+ duration_ts?: number | undefined;
258
+ duration?: number | undefined;
259
+ bit_rate?: string | undefined;
260
+ };
261
+ duration_ms: number;
262
+ }>]>;
263
+ export interface CreateISOBMFFTrackResult {
264
+ next_byte: number;
265
+ byte_size: number;
266
+ track_id: number;
267
+ file_id: string;
268
+ }
269
+ export declare const createISOBMFFTrack: (client: Client, payload: z.infer<typeof CreateISOBMFFTrackPayload>) => Promise<CreateISOBMFFTrackResult>;
270
+ export declare const uploadISOBMFFTrack: (client: Client, fileId: string, trackId: number, fileStream: Readable, trackSize: number) => Promise<void>;
@@ -1,7 +1,9 @@
1
1
  import { z } from "zod";
2
2
  import debug from "debug";
3
3
  import { AudioStreamSchema, VideoStreamSchema } from "@editframe/assets";
4
+ import { uploadChunks } from "../uploadChunks.js";
4
5
  const log = debug("ef:api:isobmff-track");
6
+ const MAX_TRACK_SIZE = 1024 * 1024 * 1024;
5
7
  const CreateISOBMFFTrackPayload = z.discriminatedUnion("type", [
6
8
  z.object({
7
9
  file_id: z.string(),
@@ -10,7 +12,7 @@ const CreateISOBMFFTrackPayload = z.discriminatedUnion("type", [
10
12
  probe_info: AudioStreamSchema,
11
13
  duration_ms: z.number().int(),
12
14
  codec_name: z.string(),
13
- byte_size: z.number().int()
15
+ byte_size: z.number().int().max(MAX_TRACK_SIZE)
14
16
  }),
15
17
  z.object({
16
18
  file_id: z.string(),
@@ -19,11 +21,12 @@ const CreateISOBMFFTrackPayload = z.discriminatedUnion("type", [
19
21
  probe_info: VideoStreamSchema,
20
22
  duration_ms: z.number().int(),
21
23
  codec_name: z.string(),
22
- byte_size: z.number().int()
24
+ byte_size: z.number().int().max(MAX_TRACK_SIZE)
23
25
  })
24
26
  ]);
25
27
  const createISOBMFFTrack = async (client, payload) => {
26
28
  log("Creating isobmff track", payload);
29
+ CreateISOBMFFTrackPayload.parse(payload);
27
30
  const response = await client.authenticatedFetch(
28
31
  "/api/video2/isobmff_tracks",
29
32
  {
@@ -32,37 +35,21 @@ const createISOBMFFTrack = async (client, payload) => {
32
35
  }
33
36
  );
34
37
  log("ISOBMFF track created", response);
35
- switch (response.status) {
36
- case 200: {
37
- return await response.json();
38
- }
39
- default: {
40
- console.error("Failed to create track");
41
- console.error(await response.json());
42
- return;
43
- }
38
+ if (response.ok) {
39
+ return await response.json();
44
40
  }
45
- };
46
- const uploadISOBMFFTrack = async (client, fileId, trackId, fileStream) => {
47
- log("Uploading isobmff track", fileId, trackId);
48
- const trackIndex = await client.authenticatedFetch(
49
- `/api/video2/isobmff_tracks/${fileId}/${trackId}/upload`,
50
- {
51
- method: "POST",
52
- body: fileStream
53
- }
41
+ throw new Error(
42
+ `Failed to create isobmff track ${response.status} ${response.statusText}`
54
43
  );
55
- log("ISOBMFF track uploaded", trackIndex);
56
- switch (trackIndex.status) {
57
- case 200: {
58
- return trackIndex.json();
59
- }
60
- default: {
61
- console.error("Failed to upload track");
62
- console.error(trackIndex.status, trackIndex.statusText);
63
- return;
64
- }
65
- }
44
+ };
45
+ const uploadISOBMFFTrack = async (client, fileId, trackId, fileStream, trackSize) => {
46
+ log("Uploading fragment track", fileId);
47
+ await uploadChunks(client, {
48
+ url: `/api/video2/isobmff_tracks/${fileId}/${trackId}/upload`,
49
+ fileStream,
50
+ fileSize: trackSize
51
+ });
52
+ log("Fragment track upload complete");
66
53
  };
67
54
  export {
68
55
  CreateISOBMFFTrackPayload,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import { Readable } from 'node:stream';
2
+ import { z } from 'zod';
3
+ import { Client } from '../client.ts';
4
+ export declare const CreateRenderPayload: z.ZodObject<{
5
+ id: z.ZodString;
6
+ fps: z.ZodNumber;
7
+ width: z.ZodNumber;
8
+ height: z.ZodNumber;
9
+ work_slice_ms: z.ZodNumber;
10
+ duration_ms: z.ZodNumber;
11
+ strategy: z.ZodEnum<["v1", "v2"]>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ id: string;
14
+ height: number;
15
+ width: number;
16
+ strategy: "v1" | "v2";
17
+ duration_ms: number;
18
+ fps: number;
19
+ work_slice_ms: number;
20
+ }, {
21
+ id: string;
22
+ height: number;
23
+ width: number;
24
+ strategy: "v1" | "v2";
25
+ duration_ms: number;
26
+ fps: number;
27
+ work_slice_ms: number;
28
+ }>;
29
+ export interface CreateRenderResult {
30
+ status: "complete" | "created" | "failed" | "pending" | "rendering";
31
+ id: string;
32
+ }
33
+ export declare const createRender: (client: Client, payload: z.infer<typeof CreateRenderPayload>) => Promise<CreateRenderResult>;
34
+ export declare const uploadRender: (client: Client, fileId: string, fileStream: Readable, folderSize: number) => Promise<unknown>;
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import debug from "debug";
3
3
  const log = debug("ef:api:renders");
4
+ const FILE_SIZE_LIMIT = 1024 * 1024 * 16;
4
5
  const CreateRenderPayload = z.object({
5
6
  id: z.string().uuid(),
6
7
  fps: z.number(),
@@ -17,37 +18,32 @@ const createRender = async (client, payload) => {
17
18
  body: JSON.stringify(payload)
18
19
  });
19
20
  log("Render created", response);
20
- switch (response.status) {
21
- case 200: {
22
- return await response.json();
23
- }
24
- default: {
25
- console.error("Failed to create render");
26
- console.error(await response.json());
27
- return;
28
- }
21
+ if (response.ok) {
22
+ return await response.json();
29
23
  }
24
+ throw new Error(
25
+ `Failed to create render ${response.status} ${response.statusText}`
26
+ );
30
27
  };
31
- const uploadRender = async (client, fileId, fileStream) => {
28
+ const uploadRender = async (client, fileId, fileStream, folderSize) => {
32
29
  log("Uploading render", fileId);
33
- const fileIndex = await client.authenticatedFetch(
30
+ log("Folder size", folderSize, "bytes");
31
+ if (folderSize > FILE_SIZE_LIMIT) {
32
+ throw new Error(`File size exceeds limit of ${FILE_SIZE_LIMIT} bytes`);
33
+ }
34
+ const response = await client.authenticatedFetch(
34
35
  `/api/video2/renders/${fileId}/upload`,
35
36
  {
36
37
  method: "POST",
37
38
  body: fileStream
38
39
  }
39
40
  );
40
- log("Render uploaded", fileIndex);
41
- switch (fileIndex.status) {
42
- case 200: {
43
- return fileIndex.json();
44
- }
45
- default: {
46
- console.error("Failed to upload render");
47
- console.error(fileIndex.status, fileIndex.statusText);
48
- return;
49
- }
41
+ if (response.ok) {
42
+ return response.json();
50
43
  }
44
+ throw new Error(
45
+ `Failed to upload render ${response.status} ${response.statusText}`
46
+ );
51
47
  };
52
48
  export {
53
49
  CreateRenderPayload,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import { Readable } from 'node:stream';
2
+ import { z } from 'zod';
3
+ import { Client } from '../client.ts';
4
+ declare const FileProcessors: z.ZodEffects<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"isobmff">, z.ZodLiteral<"captions">]>, "many">, ("captions" | "isobmff")[], ("captions" | "isobmff")[]>;
5
+ export declare const CreateUnprocessedFilePayload: z.ZodObject<{
6
+ id: z.ZodString;
7
+ filename: z.ZodString;
8
+ processes: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"isobmff">, z.ZodLiteral<"captions">]>, "many">, ("captions" | "isobmff")[], ("captions" | "isobmff")[]>>;
9
+ byte_size: z.ZodNumber;
10
+ }, "strip", z.ZodTypeAny, {
11
+ id: string;
12
+ filename: string;
13
+ byte_size: number;
14
+ processes?: ("captions" | "isobmff")[] | undefined;
15
+ }, {
16
+ id: string;
17
+ filename: string;
18
+ byte_size: number;
19
+ processes?: ("captions" | "isobmff")[] | undefined;
20
+ }>;
21
+ export declare const UpdateUnprocessedFilePayload: z.ZodObject<{
22
+ processes: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"isobmff">, z.ZodLiteral<"captions">]>, "many">, ("captions" | "isobmff")[], ("captions" | "isobmff")[]>>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ processes?: ("captions" | "isobmff")[] | undefined;
25
+ }, {
26
+ processes?: ("captions" | "isobmff")[] | undefined;
27
+ }>;
28
+ export interface CreateUnprocessedFileResult {
29
+ byte_size: number;
30
+ next_byte: number;
31
+ id: string;
32
+ processes: z.infer<typeof FileProcessors>;
33
+ }
34
+ export interface UpdateUnprocessedFileResult {
35
+ byte_size?: number;
36
+ next_byte: number;
37
+ id: string;
38
+ processes: z.infer<typeof FileProcessors>;
39
+ }
40
+ export declare const createUnprocessedFile: (client: Client, payload: z.infer<typeof CreateUnprocessedFilePayload>) => Promise<CreateUnprocessedFileResult>;
41
+ export declare const updateUnprocessedFile: (client: Client, fileId: string, payload: Partial<z.infer<typeof UpdateUnprocessedFilePayload>>) => Promise<UpdateUnprocessedFileResult>;
42
+ export declare const uploadUnprocessedFile: (client: Client, fileId: string, fileStream: Readable, fileSize: number) => Promise<void>;
43
+ export declare const processAVFileBuffer: (client: Client, buffer: Buffer, filename?: string) => Promise<UpdateUnprocessedFileResult>;
44
+ export declare const processAVFile: (client: Client, filePath: string) => Promise<UpdateUnprocessedFileResult>;
45
+ export {};
@@ -0,0 +1,133 @@
1
+ import { Readable } from "node:stream";
2
+ import { basename } from "node:path";
3
+ import { createReadStream } from "node:fs";
4
+ import { stat } from "node:fs/promises";
5
+ import { z } from "zod";
6
+ import debug from "debug";
7
+ import { md5Buffer, md5FilePath } from "@editframe/assets";
8
+ import { uploadChunks } from "../uploadChunks.js";
9
+ const log = debug("ef:api:unprocessed-file");
10
+ const FileProcessors = z.array(z.union([z.literal("isobmff"), z.literal("captions")])).refine(
11
+ (value) => {
12
+ return new Set(value).size === value.length;
13
+ },
14
+ {
15
+ message: "Processors list must not include duplicates"
16
+ }
17
+ );
18
+ const MAX_FILE_SIZE = 1024 * 1024 * 1024;
19
+ const CreateUnprocessedFilePayload = z.object({
20
+ id: z.string(),
21
+ filename: z.string(),
22
+ processes: FileProcessors.optional(),
23
+ byte_size: z.number().int().max(MAX_FILE_SIZE)
24
+ });
25
+ const UpdateUnprocessedFilePayload = z.object({
26
+ processes: FileProcessors.optional()
27
+ });
28
+ const createUnprocessedFile = async (client, payload) => {
29
+ log("Creating an unprocessed file", payload);
30
+ CreateUnprocessedFilePayload.parse(payload);
31
+ const response = await client.authenticatedFetch(
32
+ "/api/v1/unprocessed_files",
33
+ {
34
+ method: "POST",
35
+ body: JSON.stringify(payload)
36
+ }
37
+ );
38
+ log(
39
+ "Unprocessed file created",
40
+ response.status,
41
+ response.statusText,
42
+ response.headers
43
+ );
44
+ if (response.ok) {
45
+ return await response.json();
46
+ }
47
+ throw new Error(
48
+ `Failed to create unprocessed file ${response.status} ${response.statusText}`
49
+ );
50
+ };
51
+ const updateUnprocessedFile = async (client, fileId, payload) => {
52
+ log("Updating unprocessed file", fileId, payload);
53
+ UpdateUnprocessedFilePayload.parse(payload);
54
+ const response = await client.authenticatedFetch(
55
+ `/api/v1/unprocessed_files/${fileId}`,
56
+ {
57
+ method: "POST",
58
+ body: JSON.stringify(payload)
59
+ }
60
+ );
61
+ log("Unprocessed file updated", response);
62
+ if (response.ok) {
63
+ return await response.json();
64
+ }
65
+ throw new Error(
66
+ `Failed to update unprocessed file ${response.status} ${response.statusText}`
67
+ );
68
+ };
69
+ const uploadUnprocessedFile = async (client, fileId, fileStream, fileSize) => {
70
+ log("Uploading unprocessed file", fileId);
71
+ await uploadChunks(client, {
72
+ url: `/api/v1/unprocessed_files/${fileId}/upload`,
73
+ fileSize,
74
+ fileStream
75
+ });
76
+ log("Unprocessed file upload complete");
77
+ };
78
+ const processAVFileBuffer = async (client, buffer, filename = "buffer") => {
79
+ log("Processing AV file buffer");
80
+ const fileId = md5Buffer(buffer);
81
+ log("File ID", fileId);
82
+ log(`File size: ${buffer.byteLength} bytes`);
83
+ await createUnprocessedFile(client, {
84
+ id: fileId,
85
+ processes: [],
86
+ filename,
87
+ byte_size: buffer.byteLength
88
+ });
89
+ const readStream = new Readable({
90
+ read() {
91
+ readStream.push(buffer);
92
+ readStream.push(null);
93
+ }
94
+ });
95
+ await uploadUnprocessedFile(client, fileId, readStream, buffer.byteLength);
96
+ const fileInformation = await updateUnprocessedFile(client, fileId, {
97
+ processes: ["isobmff"]
98
+ });
99
+ log("File processed", fileInformation);
100
+ return fileInformation;
101
+ };
102
+ const processAVFile = async (client, filePath) => {
103
+ log("Processing AV file", filePath);
104
+ const fileId = await md5FilePath(filePath);
105
+ log("File ID", fileId);
106
+ await createUnprocessedFile(client, {
107
+ id: fileId,
108
+ processes: [],
109
+ filename: basename(filePath),
110
+ byte_size: (await stat(filePath)).size
111
+ });
112
+ const readStream = createReadStream(filePath);
113
+ await uploadUnprocessedFile(
114
+ client,
115
+ fileId,
116
+ readStream,
117
+ (await stat(filePath)).size
118
+ );
119
+ const fileInformation = await updateUnprocessedFile(client, fileId, {
120
+ processes: ["isobmff"]
121
+ });
122
+ log("File processed", fileInformation);
123
+ return fileInformation;
124
+ };
125
+ export {
126
+ CreateUnprocessedFilePayload,
127
+ UpdateUnprocessedFilePayload,
128
+ createUnprocessedFile,
129
+ processAVFile,
130
+ processAVFileBuffer,
131
+ updateUnprocessedFile,
132
+ uploadUnprocessedFile
133
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { Client } from '../client.ts';
2
+ export interface URLTokenResult {
3
+ token: string;
4
+ }
5
+ export declare const createURLToken: (client: Client, url: string) => Promise<string>;
@@ -0,0 +1,20 @@
1
+ import debug from "debug";
2
+ const log = debug("ef:api:url-token");
3
+ const createURLToken = async (client, url) => {
4
+ log("Creating signed url for", url);
5
+ const response = await client.authenticatedFetch("/api/v1/url-token", {
6
+ method: "POST",
7
+ body: JSON.stringify({
8
+ url
9
+ })
10
+ });
11
+ if (!response.ok) {
12
+ throw new Error(
13
+ `Failed to create signed url: ${response.status} ${response.statusText} ${await response.text()}`
14
+ );
15
+ }
16
+ return (await response.json()).token;
17
+ };
18
+ export {
19
+ createURLToken
20
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Readable } from 'node:stream';
2
+ export declare function streamChunker(readableStream: Readable, chunkSize?: number): AsyncGenerator<Buffer, void, unknown>;
@@ -0,0 +1,17 @@
1
+ import { CHUNK_SIZE_BYTES } from "./CHUNK_SIZE_BYTES.js";
2
+ async function* streamChunker(readableStream, chunkSize = CHUNK_SIZE_BYTES) {
3
+ let buffer = Buffer.alloc(0);
4
+ for await (const chunk of readableStream) {
5
+ buffer = Buffer.concat([buffer, chunk]);
6
+ while (buffer.length >= chunkSize) {
7
+ yield buffer.slice(0, chunkSize);
8
+ buffer = buffer.slice(chunkSize);
9
+ }
10
+ }
11
+ if (buffer.length > 0) {
12
+ yield buffer;
13
+ }
14
+ }
15
+ export {
16
+ streamChunker
17
+ };