@editframe/api 0.12.0-beta.3 → 0.12.0-beta.8

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 (29) hide show
  1. package/dist/{index.js → api/src/index.js} +7 -3
  2. package/dist/{resources → api/src/resources}/image-file.js +5 -4
  3. package/dist/{resources → api/src/resources}/isobmff-file.js +20 -0
  4. package/dist/{resources → api/src/resources}/unprocessed-file.js +41 -8
  5. package/dist/cli/src/utils/createReadableStreamFromReadable.js +82 -0
  6. package/dist/index.d.ts +2 -2
  7. package/dist/resources/image-file.d.ts +9 -3
  8. package/dist/resources/isobmff-file.d.ts +13 -0
  9. package/dist/resources/isobmff-track.d.ts +8 -8
  10. package/dist/resources/renders.d.ts +4 -4
  11. package/dist/resources/unprocessed-file.d.ts +5 -2
  12. package/package.json +2 -2
  13. package/src/resources/image-file.test.ts +3 -6
  14. package/src/resources/image-file.ts +43 -5
  15. package/src/resources/isobmff-file.ts +32 -0
  16. package/src/resources/unprocessed-file.test.ts +5 -7
  17. package/src/resources/unprocessed-file.ts +58 -12
  18. /package/dist/{CHUNK_SIZE_BYTES.js → api/src/CHUNK_SIZE_BYTES.js} +0 -0
  19. /package/dist/{ProgressIterator.js → api/src/ProgressIterator.js} +0 -0
  20. /package/dist/{StreamEventSource.js → api/src/StreamEventSource.js} +0 -0
  21. /package/dist/{client.js → api/src/client.js} +0 -0
  22. /package/dist/{resources → api/src/resources}/caption-file.js +0 -0
  23. /package/dist/{resources → api/src/resources}/isobmff-track.js +0 -0
  24. /package/dist/{resources → api/src/resources}/process-isobmff.js +0 -0
  25. /package/dist/{resources → api/src/resources}/renders.js +0 -0
  26. /package/dist/{resources → api/src/resources}/transcriptions.js +0 -0
  27. /package/dist/{resources → api/src/resources}/url-token.js +0 -0
  28. /package/dist/{streamChunker.js → api/src/streamChunker.js} +0 -0
  29. /package/dist/{uploadChunks.js → api/src/uploadChunks.js} +0 -0
@@ -1,11 +1,11 @@
1
1
  import { CreateCaptionFilePayload, createCaptionFile, lookupCaptionFileByMd5, uploadCaptionFile } from "./resources/caption-file.js";
2
2
  import { CreateImageFilePayload, createImageFile, lookupImageFileByMd5, uploadImageFile } from "./resources/image-file.js";
3
- import { CreateISOBMFFFilePayload, createISOBMFFFile, getISOBMFFFileTranscription, lookupISOBMFFFileByMd5, uploadFragmentIndex } from "./resources/isobmff-file.js";
3
+ import { CreateISOBMFFFilePayload, TranscribeISOBMFFFilePayload, createISOBMFFFile, getISOBMFFFileTranscription, lookupISOBMFFFileByMd5, transcribeISOBMFFFile, uploadFragmentIndex } from "./resources/isobmff-file.js";
4
4
  import { CreateISOBMFFTrackPayload, createISOBMFFTrack, uploadISOBMFFTrack } from "./resources/isobmff-track.js";
5
5
  import { CreateRenderPayload, createRender, lookupRenderByMd5, uploadRender } from "./resources/renders.js";
6
6
  import { CreateTranscriptionPayload, createTranscription, getTranscriptionInfo, getTranscriptionProgress } from "./resources/transcriptions.js";
7
7
  import { createURLToken } from "./resources/url-token.js";
8
- import { CreateUnprocessedFilePayload, createUnprocessedFile, lookupUnprocessedFileByMd5, processIsobmffFile, uploadUnprocessedFile } from "./resources/unprocessed-file.js";
8
+ import { CreateUnprocessedFilePayload, createUnprocessedFile, createUnprocessedFileFromPath, lookupUnprocessedFileByMd5, processIsobmffFile, uploadUnprocessedFile, uploadUnprocessedReadableStream } from "./resources/unprocessed-file.js";
9
9
  import { getIsobmffProcessInfo, getIsobmffProcessProgress } from "./resources/process-isobmff.js";
10
10
  import { Client } from "./client.js";
11
11
  export {
@@ -17,6 +17,7 @@ export {
17
17
  CreateRenderPayload,
18
18
  CreateTranscriptionPayload,
19
19
  CreateUnprocessedFilePayload,
20
+ TranscribeISOBMFFFilePayload,
20
21
  createCaptionFile,
21
22
  createISOBMFFFile,
22
23
  createISOBMFFTrack,
@@ -25,6 +26,7 @@ export {
25
26
  createTranscription,
26
27
  createURLToken,
27
28
  createUnprocessedFile,
29
+ createUnprocessedFileFromPath,
28
30
  getISOBMFFFileTranscription,
29
31
  getIsobmffProcessInfo,
30
32
  getIsobmffProcessProgress,
@@ -36,10 +38,12 @@ export {
36
38
  lookupRenderByMd5,
37
39
  lookupUnprocessedFileByMd5,
38
40
  processIsobmffFile,
41
+ transcribeISOBMFFFile,
39
42
  uploadCaptionFile,
40
43
  uploadFragmentIndex,
41
44
  uploadISOBMFFTrack,
42
45
  uploadImageFile,
43
46
  uploadRender,
44
- uploadUnprocessedFile
47
+ uploadUnprocessedFile,
48
+ uploadUnprocessedReadableStream
45
49
  };
@@ -1,4 +1,5 @@
1
1
  import debug from "debug";
2
+ import "mime";
2
3
  import { z } from "zod";
3
4
  import { uploadChunks } from "../uploadChunks.js";
4
5
  const log = debug("ef:api:image-file");
@@ -26,11 +27,11 @@ const createImageFile = async (client, payload) => {
26
27
  `Failed to create file ${response.status} ${response.statusText}`
27
28
  );
28
29
  };
29
- const uploadImageFile = (client, fileId, fileStream, fileSize) => {
30
- log("Uploading image file", fileId);
30
+ const uploadImageFile = (client, uploadDetails, fileStream) => {
31
+ log("Uploading image file", uploadDetails.id);
31
32
  return uploadChunks(client, {
32
- url: `/api/v1/image_files/${fileId}/upload`,
33
- fileSize,
33
+ url: `/api/v1/image_files/${uploadDetails.id}/upload`,
34
+ fileSize: uploadDetails.byte_size,
34
35
  fileStream,
35
36
  maxSize: MAX_IMAGE_SIZE
36
37
  });
@@ -73,10 +73,30 @@ const getISOBMFFFileTranscription = async (client, id) => {
73
73
  `Failed to get isobmff file transcription ${id} ${response.status} ${response.statusText}`
74
74
  );
75
75
  };
76
+ const TranscribeISOBMFFFilePayload = z.object({
77
+ trackId: z.string().optional()
78
+ });
79
+ const transcribeISOBMFFFile = async (client, id, payload = {}) => {
80
+ const response = await client.authenticatedFetch(
81
+ `/api/v1/isobmff_files/${id}/transcribe`,
82
+ {
83
+ method: "POST",
84
+ body: JSON.stringify(payload)
85
+ }
86
+ );
87
+ if (response.ok) {
88
+ return await response.json();
89
+ }
90
+ throw new Error(
91
+ `Failed to transcribe isobmff file ${id} ${response.status} ${response.statusText}`
92
+ );
93
+ };
76
94
  export {
77
95
  CreateISOBMFFFilePayload,
96
+ TranscribeISOBMFFFilePayload,
78
97
  createISOBMFFFile,
79
98
  getISOBMFFFileTranscription,
80
99
  lookupISOBMFFFileByMd5,
100
+ transcribeISOBMFFFile,
81
101
  uploadFragmentIndex
82
102
  };
@@ -9,6 +9,27 @@ const CreateUnprocessedFilePayload = z.object({
9
9
  byte_size: z.number().int().max(MAX_FILE_SIZE)
10
10
  });
11
11
  z.object({});
12
+ const createUnprocessedFileFromPath = async (client, path) => {
13
+ const [{ stat }, { basename }, { md5FilePath }] = await Promise.all([
14
+ import("node:fs/promises"),
15
+ import("node:path"),
16
+ import("@editframe/assets")
17
+ ]).catch((error) => {
18
+ console.error("Error importing modules", error);
19
+ console.error(
20
+ "This is likely because you are bundling for the browser. createUnprocessedFileFromPath can only be run in environments that support importing `node:path`"
21
+ );
22
+ throw error;
23
+ });
24
+ const fileInfo = await stat(path);
25
+ const byte_size = fileInfo.size;
26
+ const md5 = await md5FilePath(path);
27
+ return createUnprocessedFile(client, {
28
+ md5,
29
+ filename: basename(path),
30
+ byte_size
31
+ });
32
+ };
12
33
  const createUnprocessedFile = async (client, payload) => {
13
34
  log("Creating an unprocessed file", payload);
14
35
  CreateUnprocessedFilePayload.parse(payload);
@@ -32,11 +53,21 @@ const createUnprocessedFile = async (client, payload) => {
32
53
  `Failed to create unprocessed file ${response.status} ${response.statusText}`
33
54
  );
34
55
  };
35
- const uploadUnprocessedFile = (client, fileId, fileStream, fileSize) => {
36
- log("Uploading unprocessed file", fileId);
56
+ const uploadUnprocessedFile = async (client, uploadDetails, path) => {
57
+ const { createReadStream } = await import("node:fs");
58
+ const readStream = createReadStream(path);
59
+ const { createReadableStreamFromReadable } = await import("../../../cli/src/utils/createReadableStreamFromReadable.js");
60
+ return uploadUnprocessedReadableStream(
61
+ client,
62
+ uploadDetails,
63
+ createReadableStreamFromReadable(readStream)
64
+ );
65
+ };
66
+ const uploadUnprocessedReadableStream = (client, uploadDetails, fileStream) => {
67
+ log("Uploading unprocessed file", uploadDetails.id);
37
68
  return uploadChunks(client, {
38
- url: `/api/v1/unprocessed_files/${fileId}/upload`,
39
- fileSize,
69
+ url: `/api/v1/unprocessed_files/${uploadDetails.id}/upload`,
70
+ fileSize: uploadDetails.byte_size,
40
71
  fileStream,
41
72
  maxSize: MAX_FILE_SIZE
42
73
  });
@@ -58,9 +89,9 @@ const lookupUnprocessedFileByMd5 = async (client, md5) => {
58
89
  `Failed to lookup unprocessed file by md5 ${md5} ${response.status} ${response.statusText}`
59
90
  );
60
91
  };
61
- const processIsobmffFile = async (client, unprocessedFileId) => {
92
+ const processIsobmffFile = async (client, id) => {
62
93
  const response = await client.authenticatedFetch(
63
- `/api/v1/unprocessed_files/${unprocessedFileId}/isobmff`,
94
+ `/api/v1/unprocessed_files/${id}/isobmff`,
64
95
  {
65
96
  method: "POST"
66
97
  }
@@ -69,13 +100,15 @@ const processIsobmffFile = async (client, unprocessedFileId) => {
69
100
  return await response.json();
70
101
  }
71
102
  throw new Error(
72
- `Failed to process isobmff file ${response.status} ${response.statusText}`
103
+ `Failed to process isobmff file ${id} ${response.status} ${response.statusText}`
73
104
  );
74
105
  };
75
106
  export {
76
107
  CreateUnprocessedFilePayload,
77
108
  createUnprocessedFile,
109
+ createUnprocessedFileFromPath,
78
110
  lookupUnprocessedFileByMd5,
79
111
  processIsobmffFile,
80
- uploadUnprocessedFile
112
+ uploadUnprocessedFile,
113
+ uploadUnprocessedReadableStream
81
114
  };
@@ -0,0 +1,82 @@
1
+ import { Stream } from "node:stream";
2
+ const createReadableStreamFromReadable = (source) => {
3
+ const pump = new StreamPump(source);
4
+ const stream = new ReadableStream(pump, pump);
5
+ return stream;
6
+ };
7
+ class StreamPump {
8
+ constructor(stream) {
9
+ this.highWaterMark = stream.readableHighWaterMark || new Stream.Readable().readableHighWaterMark;
10
+ this.accumalatedSize = 0;
11
+ this.stream = stream;
12
+ this.enqueue = this.enqueue.bind(this);
13
+ this.error = this.error.bind(this);
14
+ this.close = this.close.bind(this);
15
+ }
16
+ size(chunk) {
17
+ return chunk?.byteLength || 0;
18
+ }
19
+ start(controller) {
20
+ this.controller = controller;
21
+ this.stream.on("data", this.enqueue);
22
+ this.stream.once("error", this.error);
23
+ this.stream.once("end", this.close);
24
+ this.stream.once("close", this.close);
25
+ }
26
+ pull() {
27
+ this.resume();
28
+ }
29
+ cancel(reason) {
30
+ if (this.stream.destroy) {
31
+ this.stream.destroy(reason);
32
+ }
33
+ this.stream.off("data", this.enqueue);
34
+ this.stream.off("error", this.error);
35
+ this.stream.off("end", this.close);
36
+ this.stream.off("close", this.close);
37
+ }
38
+ enqueue(chunk) {
39
+ if (this.controller) {
40
+ try {
41
+ const bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk);
42
+ const available = (this.controller.desiredSize || 0) - bytes.byteLength;
43
+ this.controller.enqueue(bytes);
44
+ if (available <= 0) {
45
+ this.pause();
46
+ }
47
+ } catch (error) {
48
+ this.controller.error(
49
+ new Error(
50
+ "Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object"
51
+ )
52
+ );
53
+ this.cancel();
54
+ }
55
+ }
56
+ }
57
+ pause() {
58
+ if (this.stream.pause) {
59
+ this.stream.pause();
60
+ }
61
+ }
62
+ resume() {
63
+ if (this.stream.readable && this.stream.resume) {
64
+ this.stream.resume();
65
+ }
66
+ }
67
+ close() {
68
+ if (this.controller) {
69
+ this.controller.close();
70
+ delete this.controller;
71
+ }
72
+ }
73
+ error(error) {
74
+ if (this.controller) {
75
+ this.controller.error(error);
76
+ delete this.controller;
77
+ }
78
+ }
79
+ }
80
+ export {
81
+ createReadableStreamFromReadable
82
+ };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  export { createCaptionFile, CreateCaptionFilePayload, type CreateCaptionFileResult, uploadCaptionFile, type LookupCaptionFileByMd5Result, lookupCaptionFileByMd5, } from './resources/caption-file.ts';
2
2
  export { createImageFile, CreateImageFilePayload, type CreateImageFileResult, uploadImageFile, type LookupImageFileByMd5Result, lookupImageFileByMd5, } from './resources/image-file.ts';
3
- export { createISOBMFFFile, CreateISOBMFFFilePayload, type CreateISOBMFFFileResult, uploadFragmentIndex, type LookupISOBMFFFileByMd5Result, lookupISOBMFFFileByMd5, type GetISOBMFFFileTranscriptionResult, getISOBMFFFileTranscription, } from './resources/isobmff-file.ts';
3
+ export { createISOBMFFFile, CreateISOBMFFFilePayload, type CreateISOBMFFFileResult, uploadFragmentIndex, type LookupISOBMFFFileByMd5Result, lookupISOBMFFFileByMd5, type GetISOBMFFFileTranscriptionResult, getISOBMFFFileTranscription, type TranscribeISOBMFFFileResult, transcribeISOBMFFFile, TranscribeISOBMFFFilePayload, } from './resources/isobmff-file.ts';
4
4
  export { createISOBMFFTrack, CreateISOBMFFTrackPayload, type CreateISOBMFFTrackResult, uploadISOBMFFTrack, } from './resources/isobmff-track.ts';
5
5
  export { createRender, CreateRenderPayload, type CreateRenderResult, uploadRender, type LookupRenderByMd5Result, lookupRenderByMd5, } from './resources/renders.ts';
6
6
  export { createTranscription, CreateTranscriptionPayload, type CreateTranscriptionResult, getTranscriptionInfo, getTranscriptionProgress, type TranscriptionInfoResult, } from './resources/transcriptions.ts';
7
7
  export { createURLToken, type URLTokenResult, } from './resources/url-token.ts';
8
- export { createUnprocessedFile, CreateUnprocessedFilePayload, type CreateUnprocessedFileResult, uploadUnprocessedFile, type LookupUnprocessedFileByMd5Result, lookupUnprocessedFileByMd5, processIsobmffFile, type ProcessIsobmffFileResult, } from './resources/unprocessed-file.ts';
8
+ export { createUnprocessedFile, CreateUnprocessedFilePayload, type CreateUnprocessedFileResult, uploadUnprocessedReadableStream, type LookupUnprocessedFileByMd5Result, lookupUnprocessedFileByMd5, processIsobmffFile, type ProcessIsobmffFileResult, createUnprocessedFileFromPath, uploadUnprocessedFile, } from './resources/unprocessed-file.ts';
9
9
  export { getIsobmffProcessInfo, getIsobmffProcessProgress, type IsobmffProcessInfoResult, } from './resources/process-isobmff.ts';
10
10
  export { Client } from './client.ts';
@@ -11,27 +11,33 @@ export declare const CreateImageFilePayload: z.ZodObject<{
11
11
  md5: string;
12
12
  filename: string;
13
13
  byte_size: number;
14
- height: number;
15
14
  width: number;
15
+ height: number;
16
16
  mime_type: "image/jpeg" | "image/png" | "image/jpg" | "image/webp";
17
17
  }, {
18
18
  md5: string;
19
19
  filename: string;
20
20
  byte_size: number;
21
- height: number;
22
21
  width: number;
22
+ height: number;
23
23
  mime_type: "image/jpeg" | "image/png" | "image/jpg" | "image/webp";
24
24
  }>;
25
25
  export interface CreateImageFileResult {
26
26
  complete: boolean | null;
27
+ byte_size: number;
27
28
  id: string;
28
29
  md5: string;
29
30
  }
30
31
  export interface LookupImageFileByMd5Result {
31
32
  complete: boolean | null;
33
+ byte_size: number;
32
34
  id: string;
33
35
  md5: string;
34
36
  }
37
+ export declare const createImageFileFromPath: (client: Client, path: string) => Promise<CreateImageFileResult>;
35
38
  export declare const createImageFile: (client: Client, payload: z.infer<typeof CreateImageFilePayload>) => Promise<CreateImageFileResult>;
36
- export declare const uploadImageFile: (client: Client, fileId: string, fileStream: ReadableStream, fileSize: number) => import('../uploadChunks.ts').IteratorWithPromise<import('../uploadChunks.ts').UploadChunkEvent>;
39
+ export declare const uploadImageFile: (client: Client, uploadDetails: {
40
+ id: string;
41
+ byte_size: number;
42
+ }, fileStream: ReadableStream) => import('../uploadChunks.ts').IteratorWithPromise<import('../uploadChunks.ts').UploadChunkEvent>;
37
43
  export declare const lookupImageFileByMd5: (client: Client, md5: string) => Promise<LookupImageFileByMd5Result | null>;
@@ -33,3 +33,16 @@ export declare const createISOBMFFFile: (client: Client, payload: z.infer<typeof
33
33
  export declare const uploadFragmentIndex: (client: Client, fileId: string, fileStream: ReadableStream, fileSize: number) => Promise<any>;
34
34
  export declare const lookupISOBMFFFileByMd5: (client: Client, md5: string) => Promise<LookupISOBMFFFileByMd5Result | null>;
35
35
  export declare const getISOBMFFFileTranscription: (client: Client, id: string) => Promise<GetISOBMFFFileTranscriptionResult | null>;
36
+ export declare const TranscribeISOBMFFFilePayload: z.ZodObject<{
37
+ trackId: z.ZodOptional<z.ZodString>;
38
+ }, "strip", z.ZodTypeAny, {
39
+ trackId?: string | undefined;
40
+ }, {
41
+ trackId?: string | undefined;
42
+ }>;
43
+ export interface TranscribeISOBMFFFileResult {
44
+ id: string;
45
+ file_id: string;
46
+ track_id: number;
47
+ }
48
+ export declare const transcribeISOBMFFFile: (client: Client, id: string, payload?: z.infer<typeof TranscribeISOBMFFFilePayload>) => Promise<TranscribeISOBMFFFileResult>;
@@ -159,8 +159,6 @@ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type",
159
159
  bit_rate: z.ZodOptional<z.ZodString>;
160
160
  disposition: z.ZodRecord<z.ZodString, z.ZodUnknown>;
161
161
  }, "strip", z.ZodTypeAny, {
162
- height: number;
163
- width: number;
164
162
  index: number;
165
163
  codec_name: string;
166
164
  codec_long_name: string;
@@ -171,6 +169,8 @@ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type",
171
169
  avg_frame_rate: string;
172
170
  time_base: string;
173
171
  disposition: Record<string, unknown>;
172
+ width: number;
173
+ height: number;
174
174
  coded_width: number;
175
175
  coded_height: number;
176
176
  start_pts?: number | undefined;
@@ -179,8 +179,6 @@ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type",
179
179
  duration?: number | undefined;
180
180
  bit_rate?: string | undefined;
181
181
  }, {
182
- height: number;
183
- width: number;
184
182
  index: number;
185
183
  codec_name: string;
186
184
  codec_long_name: string;
@@ -191,6 +189,8 @@ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type",
191
189
  avg_frame_rate: string;
192
190
  time_base: string;
193
191
  disposition: Record<string, unknown>;
192
+ width: number;
193
+ height: number;
194
194
  coded_width: number;
195
195
  coded_height: number;
196
196
  start_pts?: number | undefined;
@@ -209,8 +209,6 @@ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type",
209
209
  file_id: string;
210
210
  track_id: number;
211
211
  probe_info: {
212
- height: number;
213
- width: number;
214
212
  index: number;
215
213
  codec_name: string;
216
214
  codec_long_name: string;
@@ -221,6 +219,8 @@ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type",
221
219
  avg_frame_rate: string;
222
220
  time_base: string;
223
221
  disposition: Record<string, unknown>;
222
+ width: number;
223
+ height: number;
224
224
  coded_width: number;
225
225
  coded_height: number;
226
226
  start_pts?: number | undefined;
@@ -237,8 +237,6 @@ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type",
237
237
  file_id: string;
238
238
  track_id: number;
239
239
  probe_info: {
240
- height: number;
241
- width: number;
242
240
  index: number;
243
241
  codec_name: string;
244
242
  codec_long_name: string;
@@ -249,6 +247,8 @@ export declare const CreateISOBMFFTrackPayload: z.ZodDiscriminatedUnion<"type",
249
247
  avg_frame_rate: string;
250
248
  time_base: string;
251
249
  disposition: Record<string, unknown>;
250
+ width: number;
251
+ height: number;
252
252
  coded_width: number;
253
253
  coded_height: number;
254
254
  start_pts?: number | undefined;
@@ -10,20 +10,20 @@ export declare const CreateRenderPayload: z.ZodObject<{
10
10
  strategy: z.ZodEnum<["v1", "v2"]>;
11
11
  }, "strip", z.ZodTypeAny, {
12
12
  md5: string;
13
- height: number;
14
13
  width: number;
14
+ height: number;
15
+ strategy: "v1" | "v2";
15
16
  duration_ms: number;
16
17
  fps: number;
17
18
  work_slice_ms: number;
18
- strategy: "v1" | "v2";
19
19
  }, {
20
20
  md5: string;
21
- height: number;
22
21
  width: number;
22
+ height: number;
23
+ strategy: "v1" | "v2";
23
24
  duration_ms: number;
24
25
  fps: number;
25
26
  work_slice_ms: number;
26
- strategy: "v1" | "v2";
27
27
  }>;
28
28
  export interface CreateRenderResult {
29
29
  id: string;
@@ -21,6 +21,7 @@ interface UnprocessedFile {
21
21
  id: string;
22
22
  md5: string;
23
23
  }
24
+ type UnprocessedFileUploadDetails = Pick<UnprocessedFile, "id" | "byte_size">;
24
25
  export interface CreateUnprocessedFileResult extends UnprocessedFile {
25
26
  }
26
27
  export interface LookupUnprocessedFileByMd5Result extends UnprocessedFile {
@@ -30,8 +31,10 @@ export interface UpdateUnprocessedFileResult extends UnprocessedFile {
30
31
  export interface ProcessIsobmffFileResult {
31
32
  id: string;
32
33
  }
34
+ export declare const createUnprocessedFileFromPath: (client: Client, path: string) => Promise<CreateUnprocessedFileResult>;
33
35
  export declare const createUnprocessedFile: (client: Client, payload: z.infer<typeof CreateUnprocessedFilePayload>) => Promise<CreateUnprocessedFileResult>;
34
- export declare const uploadUnprocessedFile: (client: Client, fileId: string, fileStream: ReadableStream, fileSize: number) => import('../uploadChunks.ts').IteratorWithPromise<import('../uploadChunks.ts').UploadChunkEvent>;
36
+ export declare const uploadUnprocessedFile: (client: Client, uploadDetails: UnprocessedFileUploadDetails, path: string) => Promise<import('../uploadChunks.ts').IteratorWithPromise<import('../uploadChunks.ts').UploadChunkEvent>>;
37
+ export declare const uploadUnprocessedReadableStream: (client: Client, uploadDetails: UnprocessedFileUploadDetails, fileStream: ReadableStream) => import('../uploadChunks.ts').IteratorWithPromise<import('../uploadChunks.ts').UploadChunkEvent>;
35
38
  export declare const lookupUnprocessedFileByMd5: (client: Client, md5: string) => Promise<LookupUnprocessedFileByMd5Result | null>;
36
- export declare const processIsobmffFile: (client: Client, unprocessedFileId: LookupUnprocessedFileByMd5Result["id"]) => Promise<ProcessIsobmffFileResult>;
39
+ export declare const processIsobmffFile: (client: Client, id: string) => Promise<ProcessIsobmffFileResult>;
37
40
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/api",
3
- "version": "0.12.0-beta.3",
3
+ "version": "0.12.0-beta.8",
4
4
  "description": "API functions for EditFrame",
5
5
  "exports": {
6
6
  ".": {
@@ -29,7 +29,7 @@
29
29
  "vite-tsconfig-paths": "^4.3.2"
30
30
  },
31
31
  "dependencies": {
32
- "@editframe/assets": "0.12.0-beta.3",
32
+ "@editframe/assets": "0.12.0-beta.8",
33
33
  "debug": "^4.3.5",
34
34
  "jsonwebtoken": "^9.0.2",
35
35
  "node-fetch": "^3.3.2",
@@ -97,9 +97,8 @@ describe("ImageFile", () => {
97
97
  await expect(
98
98
  uploadImageFile(
99
99
  client,
100
- "test-file-id",
100
+ { id: "test-file-id", byte_size: 1024 * 1024 * 17 },
101
101
  webReadableFromBuffers(Buffer.from("test")),
102
- 1024 * 1024 * 17,
103
102
  ).whenUploaded(),
104
103
  ).rejects.toThrowError(
105
104
  "File size 17825792 bytes exceeds limit 16777216 bytes",
@@ -118,9 +117,8 @@ describe("ImageFile", () => {
118
117
  await expect(
119
118
  uploadImageFile(
120
119
  client,
121
- "test-file-id",
120
+ { id: "test-file-id", byte_size: 4 },
122
121
  webReadableFromBuffers(Buffer.from("test")),
123
- 4,
124
122
  ).whenUploaded(),
125
123
  ).rejects.toThrowError(
126
124
  "Failed to upload chunk 0 for /api/v1/image_files/test-file-id/upload 500 Internal Server Error",
@@ -139,9 +137,8 @@ describe("ImageFile", () => {
139
137
  await expect(
140
138
  uploadImageFile(
141
139
  client,
142
- "test-file-id",
140
+ { id: "test-file-id", byte_size: 4 },
143
141
  webReadableFromBuffers(Buffer.from("test")),
144
- 4,
145
142
  ).whenUploaded(),
146
143
  ).resolves.toEqual([
147
144
  { type: "progress", progress: 0 },
@@ -1,4 +1,5 @@
1
1
  import debug from "debug";
2
+ import mime from "mime";
2
3
  import { z } from "zod";
3
4
 
4
5
  import type { Client } from "../client.ts";
@@ -19,16 +20,51 @@ export const CreateImageFilePayload = z.object({
19
20
 
20
21
  export interface CreateImageFileResult {
21
22
  complete: boolean | null;
23
+ byte_size: number;
22
24
  id: string;
23
25
  md5: string;
24
26
  }
25
27
 
26
28
  export interface LookupImageFileByMd5Result {
27
29
  complete: boolean | null;
30
+ byte_size: number;
28
31
  id: string;
29
32
  md5: string;
30
33
  }
31
34
 
35
+ export const createImageFileFromPath = async (client: Client, path: string) => {
36
+ const [{ stat }, { basename }, { md5FilePath }] = await Promise.all([
37
+ import("node:fs/promises"),
38
+ import("node:path"),
39
+ import("@editframe/assets"),
40
+ ]).catch((error) => {
41
+ console.error("Error importing modules", error);
42
+ console.error(
43
+ "This is likely because you are bundling for the browser. createImageFileFromPath can only be run in environments that support importing `node:path`",
44
+ );
45
+ throw error;
46
+ });
47
+
48
+ const fileInfo = await stat(path);
49
+
50
+ const byte_size = fileInfo.size;
51
+
52
+ const md5 = await md5FilePath(path);
53
+
54
+ const mime_type = mime.getType(path);
55
+
56
+ return createImageFile(client, {
57
+ ...CreateImageFilePayload.parse({
58
+ md5,
59
+ height: 0,
60
+ width: 0,
61
+ mime_type,
62
+ filename: basename(path),
63
+ byte_size,
64
+ }),
65
+ });
66
+ };
67
+
32
68
  export const createImageFile = async (
33
69
  client: Client,
34
70
  payload: z.infer<typeof CreateImageFilePayload>,
@@ -53,15 +89,17 @@ export const createImageFile = async (
53
89
 
54
90
  export const uploadImageFile = (
55
91
  client: Client,
56
- fileId: string,
92
+ uploadDetails: {
93
+ id: string;
94
+ byte_size: number;
95
+ },
57
96
  fileStream: ReadableStream,
58
- fileSize: number,
59
97
  ) => {
60
- log("Uploading image file", fileId);
98
+ log("Uploading image file", uploadDetails.id);
61
99
 
62
100
  return uploadChunks(client, {
63
- url: `/api/v1/image_files/${fileId}/upload`,
64
- fileSize,
101
+ url: `/api/v1/image_files/${uploadDetails.id}/upload`,
102
+ fileSize: uploadDetails.byte_size,
65
103
  fileStream,
66
104
  maxSize: MAX_IMAGE_SIZE,
67
105
  });
@@ -128,3 +128,35 @@ export const getISOBMFFFileTranscription = async (
128
128
  `Failed to get isobmff file transcription ${id} ${response.status} ${response.statusText}`,
129
129
  );
130
130
  };
131
+
132
+ export const TranscribeISOBMFFFilePayload = z.object({
133
+ trackId: z.string().optional(),
134
+ });
135
+
136
+ export interface TranscribeISOBMFFFileResult {
137
+ id: string;
138
+ file_id: string;
139
+ track_id: number;
140
+ }
141
+
142
+ export const transcribeISOBMFFFile = async (
143
+ client: Client,
144
+ id: string,
145
+ payload: z.infer<typeof TranscribeISOBMFFFilePayload> = {},
146
+ ) => {
147
+ const response = await client.authenticatedFetch(
148
+ `/api/v1/isobmff_files/${id}/transcribe`,
149
+ {
150
+ method: "POST",
151
+ body: JSON.stringify(payload),
152
+ },
153
+ );
154
+
155
+ if (response.ok) {
156
+ return (await response.json()) as TranscribeISOBMFFFileResult;
157
+ }
158
+
159
+ throw new Error(
160
+ `Failed to transcribe isobmff file ${id} ${response.status} ${response.statusText}`,
161
+ );
162
+ };
@@ -8,7 +8,7 @@ import { webReadableFromBuffers } from "../readableFromBuffers.ts";
8
8
  import {
9
9
  createUnprocessedFile,
10
10
  lookupUnprocessedFileByMd5,
11
- uploadUnprocessedFile,
11
+ uploadUnprocessedReadableStream,
12
12
  } from "./unprocessed-file.ts";
13
13
 
14
14
  const server = setupServer();
@@ -96,11 +96,10 @@ describe("Unprocessed File", () => {
96
96
  );
97
97
 
98
98
  await expect(
99
- uploadUnprocessedFile(
99
+ uploadUnprocessedReadableStream(
100
100
  client,
101
- "test-file",
101
+ { id: "test-file", byte_size: 4 },
102
102
  webReadableFromBuffers(Buffer.from("test")),
103
- 4,
104
103
  ).whenUploaded(),
105
104
  ).rejects.toThrowError(
106
105
  "Failed to upload chunk 0 for /api/v1/unprocessed_files/test-file/upload 500 Internal Server Error",
@@ -117,11 +116,10 @@ describe("Unprocessed File", () => {
117
116
  );
118
117
 
119
118
  await expect(
120
- uploadUnprocessedFile(
119
+ uploadUnprocessedReadableStream(
121
120
  client,
122
- "test-file",
121
+ { id: "test-file", byte_size: 4 },
123
122
  webReadableFromBuffers(Buffer.from("test")),
124
- 4,
125
123
  ).whenUploaded(),
126
124
  ).resolves.toEqual([
127
125
  { type: "progress", progress: 0 },
@@ -24,6 +24,8 @@ interface UnprocessedFile {
24
24
  md5: string;
25
25
  }
26
26
 
27
+ type UnprocessedFileUploadDetails = Pick<UnprocessedFile, "id" | "byte_size">;
28
+
27
29
  export interface CreateUnprocessedFileResult extends UnprocessedFile {}
28
30
 
29
31
  export interface LookupUnprocessedFileByMd5Result extends UnprocessedFile {}
@@ -34,6 +36,35 @@ export interface ProcessIsobmffFileResult {
34
36
  id: string;
35
37
  }
36
38
 
39
+ export const createUnprocessedFileFromPath = async (
40
+ client: Client,
41
+ path: string,
42
+ ) => {
43
+ const [{ stat }, { basename }, { md5FilePath }] = await Promise.all([
44
+ import("node:fs/promises"),
45
+ import("node:path"),
46
+ import("@editframe/assets"),
47
+ ]).catch((error) => {
48
+ console.error("Error importing modules", error);
49
+ console.error(
50
+ "This is likely because you are bundling for the browser. createUnprocessedFileFromPath can only be run in environments that support importing `node:path`",
51
+ );
52
+ throw error;
53
+ });
54
+
55
+ const fileInfo = await stat(path);
56
+
57
+ const byte_size = fileInfo.size;
58
+
59
+ const md5 = await md5FilePath(path);
60
+
61
+ return createUnprocessedFile(client, {
62
+ md5,
63
+ filename: basename(path),
64
+ byte_size,
65
+ });
66
+ };
67
+
37
68
  export const createUnprocessedFile = async (
38
69
  client: Client,
39
70
  payload: z.infer<typeof CreateUnprocessedFilePayload>,
@@ -64,17 +95,35 @@ export const createUnprocessedFile = async (
64
95
  );
65
96
  };
66
97
 
67
- export const uploadUnprocessedFile = (
98
+ export const uploadUnprocessedFile = async (
68
99
  client: Client,
69
- fileId: string,
100
+ uploadDetails: UnprocessedFileUploadDetails,
101
+ path: string,
102
+ ) => {
103
+ const { createReadStream } = await import("node:fs");
104
+ const readStream = createReadStream(path);
105
+
106
+ const { createReadableStreamFromReadable } = await import(
107
+ "packages/cli/src/utils/createReadableStreamFromReadable.ts"
108
+ );
109
+
110
+ return uploadUnprocessedReadableStream(
111
+ client,
112
+ uploadDetails,
113
+ createReadableStreamFromReadable(readStream),
114
+ );
115
+ };
116
+
117
+ export const uploadUnprocessedReadableStream = (
118
+ client: Client,
119
+ uploadDetails: UnprocessedFileUploadDetails,
70
120
  fileStream: ReadableStream,
71
- fileSize: number,
72
121
  ) => {
73
- log("Uploading unprocessed file", fileId);
122
+ log("Uploading unprocessed file", uploadDetails.id);
74
123
 
75
124
  return uploadChunks(client, {
76
- url: `/api/v1/unprocessed_files/${fileId}/upload`,
77
- fileSize,
125
+ url: `/api/v1/unprocessed_files/${uploadDetails.id}/upload`,
126
+ fileSize: uploadDetails.byte_size,
78
127
  fileStream,
79
128
  maxSize: MAX_FILE_SIZE,
80
129
  });
@@ -104,12 +153,9 @@ export const lookupUnprocessedFileByMd5 = async (
104
153
  );
105
154
  };
106
155
 
107
- export const processIsobmffFile = async (
108
- client: Client,
109
- unprocessedFileId: LookupUnprocessedFileByMd5Result["id"],
110
- ) => {
156
+ export const processIsobmffFile = async (client: Client, id: string) => {
111
157
  const response = await client.authenticatedFetch(
112
- `/api/v1/unprocessed_files/${unprocessedFileId}/isobmff`,
158
+ `/api/v1/unprocessed_files/${id}/isobmff`,
113
159
  {
114
160
  method: "POST",
115
161
  },
@@ -120,6 +166,6 @@ export const processIsobmffFile = async (
120
166
  }
121
167
 
122
168
  throw new Error(
123
- `Failed to process isobmff file ${response.status} ${response.statusText}`,
169
+ `Failed to process isobmff file ${id} ${response.status} ${response.statusText}`,
124
170
  );
125
171
  };
File without changes