@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 @@
1
+ export declare let CHUNK_SIZE_BYTES: number;
@@ -0,0 +1,7 @@
1
+ let CHUNK_SIZE_BYTES = 8 * 1024 * 1024;
2
+ if (process.env.CHUNK_SIZE_BYTES) {
3
+ CHUNK_SIZE_BYTES = Number.parseInt(process.env.CHUNK_SIZE_BYTES, 10);
4
+ }
5
+ export {
6
+ CHUNK_SIZE_BYTES
7
+ };
@@ -0,0 +1,6 @@
1
+ import { RequestInit } from 'node-fetch';
2
+ export declare class Client {
3
+ #private;
4
+ constructor(token: string, efHost?: string);
5
+ authenticatedFetch: (path: string, init?: RequestInit) => Promise<import('node-fetch').Response>;
6
+ }
package/dist/client.js CHANGED
@@ -2,27 +2,33 @@ import debug from "debug";
2
2
  import fetch from "node-fetch";
3
3
  const log = debug("ef:api:client");
4
4
  class Client {
5
- constructor(token, efHost) {
6
- this.token = token;
7
- this.efHost = efHost;
5
+ constructor(token, efHost = "https://editframe.dev") {
8
6
  this.authenticatedFetch = async (path, init = {}) => {
9
7
  init.headers ||= {};
8
+ const url = new URL(path, this.#efHost);
10
9
  log(
11
10
  "Authenticated fetch",
12
- { path, init },
11
+ { url: url.toString(), init },
13
12
  "(Token will be added as Bearer token)"
14
13
  );
15
14
  Object.assign(init.headers, {
16
- Authorization: `Bearer ${this.token}`,
15
+ Authorization: `Bearer ${this.#token}`,
17
16
  "Content-Type": "application/json"
18
17
  });
19
- const url = new URL(path, this.efHost);
20
18
  const response = await fetch(url, init);
21
19
  log("Authenticated fetch response", response.status, response.statusText);
22
20
  return response;
23
21
  };
24
- log("Creating client with efHost", efHost, "and !!token", !!token);
22
+ log("Creating client with efHost", { efHost, tokenIsSet: !!token });
23
+ this.#token = token;
24
+ this.#efHost = efHost;
25
+ const { apiKey, apiSecret } = token.match(/^(?<apiSecret>ef_[^_]+)_(?<apiKey>.+)$/)?.groups ?? {};
26
+ if (!apiKey || !apiSecret) {
27
+ throw new Error("Invalid token format. Must look like: ef_{}_{}");
28
+ }
25
29
  }
30
+ #token;
31
+ #efHost;
26
32
  }
27
33
  export {
28
34
  Client
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export { createCaptionFile, CreateCaptionFilePayload, type CreateCaptionFileResult, uploadCaptionFile, } from './resources/caption-file.ts';
2
+ export { createImageFile, CreateImageFilePayload, type CreateImageFileResult, uploadImageFile, } from './resources/image-file.ts';
3
+ export { createISOBMFFFile, CreateISOBMFFFilePayload, type CreateISOBMFFFileResult, uploadFragmentIndex, } from './resources/isobmff-file.ts';
4
+ export { createISOBMFFTrack, CreateISOBMFFTrackPayload, type CreateISOBMFFTrackResult, uploadISOBMFFTrack, } from './resources/isobmff-track.ts';
5
+ export { createRender, CreateRenderPayload, type CreateRenderResult, uploadRender, } from './resources/renders.ts';
6
+ export { createURLToken, type URLTokenResult, } from './resources/url-token.ts';
7
+ export { createUnprocessedFile, CreateUnprocessedFilePayload, type CreateUnprocessedFileResult, updateUnprocessedFile, UpdateUnprocessedFilePayload, type UpdateUnprocessedFileResult, uploadUnprocessedFile, processAVFile, processAVFileBuffer, } from './resources/unprocessed-file.ts';
8
+ export { Client } from './client.ts';
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ import { CreateImageFilePayload, createImageFile, uploadImageFile } from "./reso
3
3
  import { CreateISOBMFFFilePayload, createISOBMFFFile, uploadFragmentIndex } from "./resources/isobmff-file.js";
4
4
  import { CreateISOBMFFTrackPayload, createISOBMFFTrack, uploadISOBMFFTrack } from "./resources/isobmff-track.js";
5
5
  import { CreateRenderPayload, createRender, uploadRender } from "./resources/renders.js";
6
+ import { createURLToken } from "./resources/url-token.js";
7
+ import { CreateUnprocessedFilePayload, UpdateUnprocessedFilePayload, createUnprocessedFile, processAVFile, processAVFileBuffer, updateUnprocessedFile, uploadUnprocessedFile } from "./resources/unprocessed-file.js";
6
8
  import { Client } from "./client.js";
7
9
  export {
8
10
  Client,
@@ -11,14 +13,22 @@ export {
11
13
  CreateISOBMFFTrackPayload,
12
14
  CreateImageFilePayload,
13
15
  CreateRenderPayload,
16
+ CreateUnprocessedFilePayload,
17
+ UpdateUnprocessedFilePayload,
14
18
  createCaptionFile,
15
19
  createISOBMFFFile,
16
20
  createISOBMFFTrack,
17
21
  createImageFile,
18
22
  createRender,
23
+ createURLToken,
24
+ createUnprocessedFile,
25
+ processAVFile,
26
+ processAVFileBuffer,
27
+ updateUnprocessedFile,
19
28
  uploadCaptionFile,
20
29
  uploadFragmentIndex,
21
30
  uploadISOBMFFTrack,
22
31
  uploadImageFile,
23
- uploadRender
32
+ uploadRender,
33
+ uploadUnprocessedFile
24
34
  };
@@ -0,0 +1,2 @@
1
+ import { Readable } from 'node:stream';
2
+ export declare const readableFromBuffers: (...buffers: Buffer[]) => Readable;
@@ -0,0 +1,39 @@
1
+ import { Readable } from 'node:stream';
2
+ import { z } from 'zod';
3
+ import { Client } from '../client.ts';
4
+ export declare const CreateCaptionFilePayload: z.ZodObject<{
5
+ id: z.ZodString;
6
+ filename: z.ZodString;
7
+ byte_size: z.ZodNumber;
8
+ }, "strip", z.ZodTypeAny, {
9
+ id: string;
10
+ filename: string;
11
+ byte_size: number;
12
+ }, {
13
+ id: string;
14
+ filename: string;
15
+ byte_size: number;
16
+ }>;
17
+ export interface CreateCaptionFileResult {
18
+ complete: boolean | null;
19
+ id: string;
20
+ }
21
+ /**
22
+ * Create a caption file
23
+ * @param client - The authenticated client to use for the request
24
+ * @param payload - The payload to send to the server
25
+ * @returns The result of the request
26
+ * @example
27
+ * ```ts
28
+ * const result = await createCaptionFile(client, {
29
+ * id: "123",
30
+ * filename: "caption.srt",
31
+ * });
32
+ * console.log(result);
33
+ * ```
34
+ * @category CaptionFile
35
+ * @resource
36
+ * @beta
37
+ */
38
+ export declare const createCaptionFile: (client: Client, payload: z.infer<typeof CreateCaptionFilePayload>) => Promise<CreateCaptionFileResult>;
39
+ export declare const uploadCaptionFile: (client: Client, fileId: string, fileStream: Readable, fileSize: number) => Promise<unknown>;
@@ -1,53 +1,55 @@
1
1
  import { z } from "zod";
2
2
  import debug from "debug";
3
3
  const log = debug("ef:api:caption-file");
4
+ const MAX_CAPTION_SIZE = 1024 * 1024 * 2;
4
5
  const CreateCaptionFilePayload = z.object({
5
6
  id: z.string(),
6
- filename: z.string()
7
+ filename: z.string(),
8
+ byte_size: z.number().int().max(MAX_CAPTION_SIZE)
7
9
  });
10
+ const restrictSize = (size) => {
11
+ if (size > MAX_CAPTION_SIZE) {
12
+ throw new Error(
13
+ `File size ${size} bytes exceeds limit ${MAX_CAPTION_SIZE} bytes
14
+ `
15
+ );
16
+ }
17
+ };
8
18
  const createCaptionFile = async (client, payload) => {
9
19
  log("Creating caption file", payload);
10
- const fileCreation = await client.authenticatedFetch(
20
+ restrictSize(payload.byte_size);
21
+ const response = await client.authenticatedFetch(
11
22
  "/api/video2/caption_files",
12
23
  {
13
24
  method: "POST",
14
25
  body: JSON.stringify(payload)
15
26
  }
16
27
  );
17
- log("Caption file created", fileCreation);
18
- switch (fileCreation.status) {
19
- case 200: {
20
- return await fileCreation.json();
21
- }
22
- default: {
23
- console.error(
24
- `Failed to create file ${fileCreation.status} ${fileCreation.statusText}`
25
- );
26
- return;
27
- }
28
+ log("Caption file created", response);
29
+ if (response.ok) {
30
+ return await response.json();
28
31
  }
32
+ throw new Error(
33
+ `Failed to create caption ${response.status} ${response.statusText}`
34
+ );
29
35
  };
30
- const uploadCaptionFile = async (client, fileId, fileStream) => {
36
+ const uploadCaptionFile = async (client, fileId, fileStream, fileSize) => {
31
37
  log("Uploading caption file", fileId);
32
- const fileIndex = await client.authenticatedFetch(
38
+ restrictSize(fileSize);
39
+ const response = await client.authenticatedFetch(
33
40
  `/api/video2/caption_files/${fileId}/upload`,
34
41
  {
35
42
  method: "POST",
36
43
  body: fileStream
37
44
  }
38
45
  );
39
- log("Caption file uploaded", fileIndex);
40
- switch (fileIndex.status) {
41
- case 200: {
42
- return fileIndex.json();
43
- }
44
- default: {
45
- console.error(
46
- `Failed to upload caption ${fileIndex.status} ${fileIndex.statusText}`
47
- );
48
- return;
49
- }
46
+ log("Caption file uploaded", response);
47
+ if (response.ok) {
48
+ return response.json();
50
49
  }
50
+ throw new Error(
51
+ `Failed to upload caption ${response.status} ${response.statusText}`
52
+ );
51
53
  };
52
54
  export {
53
55
  CreateCaptionFilePayload,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { Readable } from 'node:stream';
2
+ import { z } from 'zod';
3
+ import { Client } from '../client.ts';
4
+ export declare const CreateImageFilePayload: z.ZodObject<{
5
+ id: z.ZodString;
6
+ height: z.ZodNumber;
7
+ width: z.ZodNumber;
8
+ mime_type: z.ZodEnum<["image/jpeg", "image/png", "image/jpg", "image/webp"]>;
9
+ filename: z.ZodString;
10
+ byte_size: z.ZodNumber;
11
+ }, "strip", z.ZodTypeAny, {
12
+ id: string;
13
+ filename: string;
14
+ byte_size: number;
15
+ height: number;
16
+ width: number;
17
+ mime_type: "image/jpeg" | "image/png" | "image/jpg" | "image/webp";
18
+ }, {
19
+ id: string;
20
+ filename: string;
21
+ byte_size: number;
22
+ height: number;
23
+ width: number;
24
+ mime_type: "image/jpeg" | "image/png" | "image/jpg" | "image/webp";
25
+ }>;
26
+ export interface CreateImageFileResult {
27
+ complete: boolean | null;
28
+ id: string;
29
+ }
30
+ export declare const createImageFile: (client: Client, payload: z.infer<typeof CreateImageFilePayload>) => Promise<CreateImageFileResult>;
31
+ export declare const uploadImageFile: (client: Client, fileId: string, fileStream: Readable, fileSize: number) => Promise<void>;
@@ -1,49 +1,45 @@
1
1
  import { z } from "zod";
2
2
  import debug from "debug";
3
+ import { uploadChunks } from "../uploadChunks.js";
3
4
  const log = debug("ef:api:image-file");
5
+ const MAX_IMAGE_SIZE = 1024 * 1024 * 16;
4
6
  const CreateImageFilePayload = z.object({
5
7
  id: z.string(),
6
8
  height: z.number().int(),
7
9
  width: z.number().int(),
8
10
  mime_type: z.enum(["image/jpeg", "image/png", "image/jpg", "image/webp"]),
9
- filename: z.string()
11
+ filename: z.string(),
12
+ byte_size: z.number().int().max(MAX_IMAGE_SIZE)
10
13
  });
11
14
  const createImageFile = async (client, payload) => {
12
15
  log("Creating image file", payload);
16
+ CreateImageFilePayload.parse(payload);
13
17
  const response = await client.authenticatedFetch("/api/video2/image_files", {
14
18
  method: "POST",
15
19
  body: JSON.stringify(payload)
16
20
  });
17
21
  log("Image file created", response);
18
- switch (response.status) {
19
- case 200: {
20
- return await response.json();
21
- }
22
- default: {
23
- console.error(
24
- `Failed to create file ${response.status} ${response.statusText}`
25
- );
26
- return;
27
- }
22
+ if (response.ok) {
23
+ return await response.json();
28
24
  }
29
- };
30
- const uploadImageFile = async (client, fileId, fileStream) => {
31
- const fileIndex = await client.authenticatedFetch(
32
- `/api/video2/image_files/${fileId}/upload`,
33
- {
34
- method: "POST",
35
- body: fileStream
36
- }
25
+ throw new Error(
26
+ `Failed to create file ${response.status} ${response.statusText}`
37
27
  );
38
- switch (fileIndex.status) {
39
- case 200: {
40
- return fileIndex.json();
41
- }
42
- default: {
43
- console.error("Failed to upload image");
44
- return;
45
- }
28
+ };
29
+ const uploadImageFile = async (client, fileId, fileStream, fileSize) => {
30
+ log("Uploading image file", fileId);
31
+ if (fileSize > MAX_IMAGE_SIZE) {
32
+ throw new Error(
33
+ `File size ${fileSize} bytes exceeds limit ${MAX_IMAGE_SIZE} bytes`
34
+ );
46
35
  }
36
+ const result = await uploadChunks(client, {
37
+ url: `/api/video2/image_files/${fileId}/upload`,
38
+ fileSize,
39
+ fileStream
40
+ });
41
+ log("Image file upload complete");
42
+ return result;
47
43
  };
48
44
  export {
49
45
  CreateImageFilePayload,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { Readable } from 'node:stream';
2
+ import { z } from 'zod';
3
+ import { Client } from '../client.ts';
4
+ export declare const CreateISOBMFFFilePayload: z.ZodObject<{
5
+ id: z.ZodString;
6
+ filename: z.ZodString;
7
+ }, "strip", z.ZodTypeAny, {
8
+ id: string;
9
+ filename: string;
10
+ }, {
11
+ id: string;
12
+ filename: string;
13
+ }>;
14
+ export interface CreateISOBMFFFileResult {
15
+ fragment_index_complete: boolean;
16
+ id: string;
17
+ }
18
+ export declare const createISOBMFFFile: (client: Client, payload: z.infer<typeof CreateISOBMFFFilePayload>) => Promise<CreateISOBMFFFileResult>;
19
+ export declare const uploadFragmentIndex: (client: Client, fileId: string, fileStream: Readable, fileSize: 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:isobmff-file");
4
+ const FILE_SIZE_LIMIT = 1024 * 1024 * 2;
4
5
  const CreateISOBMFFFilePayload = z.object({
5
6
  id: z.string(),
6
7
  filename: z.string()
@@ -15,39 +16,32 @@ const createISOBMFFFile = async (client, payload) => {
15
16
  }
16
17
  );
17
18
  log("ISOBMFF file created", response);
18
- switch (response.status) {
19
- case 200: {
20
- return await response.json();
21
- }
22
- default: {
23
- console.error(
24
- `Failed to create file ${response.status} ${response.statusText}`
25
- );
26
- return;
27
- }
19
+ if (response.ok) {
20
+ return await response.json();
28
21
  }
22
+ throw new Error(
23
+ `Failed to create isobmff file ${response.status} ${response.statusText}`
24
+ );
29
25
  };
30
- const uploadFragmentIndex = async (client, fileId, fileStream) => {
26
+ const uploadFragmentIndex = async (client, fileId, fileStream, fileSize) => {
31
27
  log("Uploading fragment index", fileId);
32
- const fileIndex = await client.authenticatedFetch(
28
+ if (fileSize > FILE_SIZE_LIMIT) {
29
+ throw new Error(`File size exceeds limit of ${FILE_SIZE_LIMIT} bytes`);
30
+ }
31
+ const response = await client.authenticatedFetch(
33
32
  `/api/video2/isobmff_files/${fileId}/index/upload`,
34
33
  {
35
34
  method: "POST",
36
35
  body: fileStream
37
36
  }
38
37
  );
39
- log("Fragment index uploaded", fileIndex);
40
- switch (fileIndex.status) {
41
- case 200: {
42
- return fileIndex.json();
43
- }
44
- default: {
45
- console.error(
46
- `Failed to create fragment index ${fileIndex.status} ${fileIndex.statusText}`
47
- );
48
- return;
49
- }
38
+ log("Fragment index uploaded", response);
39
+ if (response.ok) {
40
+ return response.json();
50
41
  }
42
+ throw new Error(
43
+ `Failed to create fragment index ${response.status} ${response.statusText}`
44
+ );
51
45
  };
52
46
  export {
53
47
  CreateISOBMFFFilePayload,
@@ -0,0 +1 @@
1
+ export {};