@editframe/api 0.9.0-beta.3 → 0.10.0-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.
@@ -2,21 +2,22 @@ import { Readable } from 'node:stream';
2
2
  import { z } from 'zod';
3
3
  import { Client } from '../client.ts';
4
4
  export declare const CreateCaptionFilePayload: z.ZodObject<{
5
- id: z.ZodString;
5
+ md5: z.ZodString;
6
6
  filename: z.ZodString;
7
7
  byte_size: z.ZodNumber;
8
8
  }, "strip", z.ZodTypeAny, {
9
- id: string;
9
+ md5: string;
10
10
  filename: string;
11
11
  byte_size: number;
12
12
  }, {
13
- id: string;
13
+ md5: string;
14
14
  filename: string;
15
15
  byte_size: number;
16
16
  }>;
17
17
  export interface CreateCaptionFileResult {
18
18
  complete: boolean | null;
19
19
  id: string;
20
+ md5: string;
20
21
  asset_id: string;
21
22
  }
22
23
  /**
@@ -3,7 +3,7 @@ import debug from "debug";
3
3
  const log = debug("ef:api:caption-file");
4
4
  const MAX_CAPTION_SIZE = 1024 * 1024 * 2;
5
5
  const CreateCaptionFilePayload = z.object({
6
- id: z.string(),
6
+ md5: z.string(),
7
7
  filename: z.string(),
8
8
  byte_size: z.number().int().max(MAX_CAPTION_SIZE)
9
9
  });
@@ -18,13 +18,10 @@ const restrictSize = (size) => {
18
18
  const createCaptionFile = async (client, payload) => {
19
19
  log("Creating caption file", payload);
20
20
  restrictSize(payload.byte_size);
21
- const response = await client.authenticatedFetch(
22
- "/api/video2/caption_files",
23
- {
24
- method: "POST",
25
- body: JSON.stringify(payload)
26
- }
27
- );
21
+ const response = await client.authenticatedFetch("/api/v1/caption_files", {
22
+ method: "POST",
23
+ body: JSON.stringify(payload)
24
+ });
28
25
  log("Caption file created", response);
29
26
  if (response.ok) {
30
27
  return await response.json();
@@ -37,7 +34,7 @@ const uploadCaptionFile = async (client, fileId, fileStream, fileSize) => {
37
34
  log("Uploading caption file", fileId);
38
35
  restrictSize(fileSize);
39
36
  const response = await client.authenticatedFetch(
40
- `/api/video2/caption_files/${fileId}/upload`,
37
+ `/api/v1/caption_files/${fileId}/upload`,
41
38
  {
42
39
  method: "POST",
43
40
  body: fileStream
@@ -2,21 +2,21 @@ import { Readable } from 'node:stream';
2
2
  import { z } from 'zod';
3
3
  import { Client } from '../client.ts';
4
4
  export declare const CreateImageFilePayload: z.ZodObject<{
5
- id: z.ZodString;
5
+ md5: z.ZodString;
6
6
  height: z.ZodNumber;
7
7
  width: z.ZodNumber;
8
8
  mime_type: z.ZodEnum<["image/jpeg", "image/png", "image/jpg", "image/webp"]>;
9
9
  filename: z.ZodString;
10
10
  byte_size: z.ZodNumber;
11
11
  }, "strip", z.ZodTypeAny, {
12
- id: string;
12
+ md5: string;
13
13
  filename: string;
14
14
  byte_size: number;
15
15
  height: number;
16
16
  width: number;
17
17
  mime_type: "image/jpeg" | "image/png" | "image/jpg" | "image/webp";
18
18
  }, {
19
- id: string;
19
+ md5: string;
20
20
  filename: string;
21
21
  byte_size: number;
22
22
  height: number;
@@ -26,6 +26,7 @@ export declare const CreateImageFilePayload: z.ZodObject<{
26
26
  export interface CreateImageFileResult {
27
27
  complete: boolean | null;
28
28
  id: string;
29
+ md5: string;
29
30
  asset_id: string;
30
31
  }
31
32
  export declare const createImageFile: (client: Client, payload: z.infer<typeof CreateImageFilePayload>) => Promise<CreateImageFileResult>;
@@ -4,7 +4,7 @@ import { uploadChunks } from "../uploadChunks.js";
4
4
  const log = debug("ef:api:image-file");
5
5
  const MAX_IMAGE_SIZE = 1024 * 1024 * 16;
6
6
  const CreateImageFilePayload = z.object({
7
- id: z.string(),
7
+ md5: z.string(),
8
8
  height: z.number().int(),
9
9
  width: z.number().int(),
10
10
  mime_type: z.enum(["image/jpeg", "image/png", "image/jpg", "image/webp"]),
@@ -14,7 +14,7 @@ const CreateImageFilePayload = z.object({
14
14
  const createImageFile = async (client, payload) => {
15
15
  log("Creating image file", payload);
16
16
  CreateImageFilePayload.parse(payload);
17
- const response = await client.authenticatedFetch("/api/video2/image_files", {
17
+ const response = await client.authenticatedFetch("/api/v1/image_files", {
18
18
  method: "POST",
19
19
  body: JSON.stringify(payload)
20
20
  });
@@ -34,7 +34,7 @@ const uploadImageFile = async (client, fileId, fileStream, fileSize) => {
34
34
  );
35
35
  }
36
36
  const result = await uploadChunks(client, {
37
- url: `/api/video2/image_files/${fileId}/upload`,
37
+ url: `/api/v1/image_files/${fileId}/upload`,
38
38
  fileSize,
39
39
  fileStream
40
40
  });
@@ -2,18 +2,20 @@ import { Readable } from 'node:stream';
2
2
  import { z } from 'zod';
3
3
  import { Client } from '../client.ts';
4
4
  export declare const CreateISOBMFFFilePayload: z.ZodObject<{
5
- id: z.ZodString;
5
+ md5: z.ZodString;
6
6
  filename: z.ZodString;
7
7
  }, "strip", z.ZodTypeAny, {
8
- id: string;
8
+ md5: string;
9
9
  filename: string;
10
10
  }, {
11
- id: string;
11
+ md5: string;
12
12
  filename: string;
13
13
  }>;
14
14
  export interface CreateISOBMFFFileResult {
15
15
  fragment_index_complete: boolean;
16
+ filename: string;
16
17
  id: string;
18
+ md5: string;
17
19
  asset_id: string;
18
20
  }
19
21
  export declare const createISOBMFFFile: (client: Client, payload: z.infer<typeof CreateISOBMFFFilePayload>) => Promise<CreateISOBMFFFileResult>;
@@ -3,18 +3,15 @@ import debug from "debug";
3
3
  const log = debug("ef:api:isobmff-file");
4
4
  const FILE_SIZE_LIMIT = 1024 * 1024 * 2;
5
5
  const CreateISOBMFFFilePayload = z.object({
6
- id: z.string(),
6
+ md5: z.string(),
7
7
  filename: z.string()
8
8
  });
9
9
  const createISOBMFFFile = async (client, payload) => {
10
10
  log("Creating isobmff file", payload);
11
- const response = await client.authenticatedFetch(
12
- "/api/video2/isobmff_files",
13
- {
14
- method: "POST",
15
- body: JSON.stringify(payload)
16
- }
17
- );
11
+ const response = await client.authenticatedFetch("/api/v1/isobmff_files", {
12
+ method: "POST",
13
+ body: JSON.stringify(payload)
14
+ });
18
15
  log("ISOBMFF file created", response);
19
16
  if (response.ok) {
20
17
  return await response.json();
@@ -29,7 +26,7 @@ const uploadFragmentIndex = async (client, fileId, fileStream, fileSize) => {
29
26
  throw new Error(`File size exceeds limit of ${FILE_SIZE_LIMIT} bytes`);
30
27
  }
31
28
  const response = await client.authenticatedFetch(
32
- `/api/video2/isobmff_files/${fileId}/index/upload`,
29
+ `/api/v1/isobmff_files/${fileId}/index/upload`,
33
30
  {
34
31
  method: "POST",
35
32
  body: fileStream
@@ -27,13 +27,10 @@ const CreateISOBMFFTrackPayload = z.discriminatedUnion("type", [
27
27
  const createISOBMFFTrack = async (client, payload) => {
28
28
  log("Creating isobmff track", payload);
29
29
  CreateISOBMFFTrackPayload.parse(payload);
30
- const response = await client.authenticatedFetch(
31
- "/api/video2/isobmff_tracks",
32
- {
33
- method: "POST",
34
- body: JSON.stringify(payload)
35
- }
36
- );
30
+ const response = await client.authenticatedFetch("/api/v1/isobmff_tracks", {
31
+ method: "POST",
32
+ body: JSON.stringify(payload)
33
+ });
37
34
  log("ISOBMFF track created", response);
38
35
  if (response.ok) {
39
36
  return await response.json();
@@ -45,7 +42,7 @@ const createISOBMFFTrack = async (client, payload) => {
45
42
  const uploadISOBMFFTrack = async (client, fileId, trackId, fileStream, trackSize) => {
46
43
  log("Uploading fragment track", fileId);
47
44
  await uploadChunks(client, {
48
- url: `/api/video2/isobmff_tracks/${fileId}/${trackId}/upload`,
45
+ url: `/api/v1/isobmff_tracks/${fileId}/${trackId}/upload`,
49
46
  fileStream,
50
47
  fileSize: trackSize
51
48
  });
@@ -2,7 +2,7 @@ import { Readable } from 'node:stream';
2
2
  import { z } from 'zod';
3
3
  import { Client } from '../client.ts';
4
4
  export declare const CreateRenderPayload: z.ZodObject<{
5
- id: z.ZodString;
5
+ md5: z.ZodString;
6
6
  fps: z.ZodNumber;
7
7
  width: z.ZodNumber;
8
8
  height: z.ZodNumber;
@@ -10,7 +10,7 @@ export declare const CreateRenderPayload: z.ZodObject<{
10
10
  duration_ms: z.ZodNumber;
11
11
  strategy: z.ZodEnum<["v1", "v2"]>;
12
12
  }, "strip", z.ZodTypeAny, {
13
- id: string;
13
+ md5: string;
14
14
  height: number;
15
15
  width: number;
16
16
  strategy: "v1" | "v2";
@@ -18,7 +18,7 @@ export declare const CreateRenderPayload: z.ZodObject<{
18
18
  fps: number;
19
19
  work_slice_ms: number;
20
20
  }, {
21
- id: string;
21
+ md5: string;
22
22
  height: number;
23
23
  width: number;
24
24
  strategy: "v1" | "v2";
@@ -27,8 +27,9 @@ export declare const CreateRenderPayload: z.ZodObject<{
27
27
  work_slice_ms: number;
28
28
  }>;
29
29
  export interface CreateRenderResult {
30
- status: "complete" | "created" | "failed" | "pending" | "rendering";
31
30
  id: string;
31
+ md5: string;
32
+ status: "complete" | "created" | "failed" | "pending" | "rendering";
32
33
  }
33
34
  export declare const createRender: (client: Client, payload: z.infer<typeof CreateRenderPayload>) => Promise<CreateRenderResult>;
34
35
  export declare const uploadRender: (client: Client, fileId: string, fileStream: Readable, folderSize: number) => Promise<unknown>;
@@ -3,7 +3,7 @@ import debug from "debug";
3
3
  const log = debug("ef:api:renders");
4
4
  const FILE_SIZE_LIMIT = 1024 * 1024 * 16;
5
5
  const CreateRenderPayload = z.object({
6
- id: z.string().uuid(),
6
+ md5: z.string(),
7
7
  fps: z.number(),
8
8
  width: z.number().int(),
9
9
  height: z.number().int(),
@@ -13,7 +13,7 @@ const CreateRenderPayload = z.object({
13
13
  });
14
14
  const createRender = async (client, payload) => {
15
15
  log("Creating render", payload);
16
- const response = await client.authenticatedFetch("/api/video2/renders", {
16
+ const response = await client.authenticatedFetch("/api/v1/renders", {
17
17
  method: "POST",
18
18
  body: JSON.stringify(payload)
19
19
  });
@@ -32,7 +32,7 @@ const uploadRender = async (client, fileId, fileStream, folderSize) => {
32
32
  throw new Error(`File size exceeds limit of ${FILE_SIZE_LIMIT} bytes`);
33
33
  }
34
34
  const response = await client.authenticatedFetch(
35
- `/api/video2/renders/${fileId}/upload`,
35
+ `/api/v1/renders/${fileId}/upload`,
36
36
  {
37
37
  method: "POST",
38
38
  body: fileStream
@@ -3,17 +3,17 @@ import { z } from 'zod';
3
3
  import { Client } from '../client.ts';
4
4
  declare const FileProcessors: z.ZodEffects<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"isobmff">, z.ZodLiteral<"image">, z.ZodLiteral<"captions">]>, "many">, ("captions" | "image" | "isobmff")[], ("captions" | "image" | "isobmff")[]>;
5
5
  export declare const CreateUnprocessedFilePayload: z.ZodObject<{
6
- id: z.ZodString;
6
+ md5: z.ZodString;
7
7
  filename: z.ZodString;
8
8
  processes: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"isobmff">, z.ZodLiteral<"image">, z.ZodLiteral<"captions">]>, "many">, ("captions" | "image" | "isobmff")[], ("captions" | "image" | "isobmff")[]>>;
9
9
  byte_size: z.ZodNumber;
10
10
  }, "strip", z.ZodTypeAny, {
11
- id: string;
11
+ md5: string;
12
12
  filename: string;
13
13
  byte_size: number;
14
14
  processes?: ("captions" | "image" | "isobmff")[] | undefined;
15
15
  }, {
16
- id: string;
16
+ md5: string;
17
17
  filename: string;
18
18
  byte_size: number;
19
19
  processes?: ("captions" | "image" | "isobmff")[] | undefined;
@@ -28,14 +28,18 @@ export declare const UpdateUnprocessedFilePayload: z.ZodObject<{
28
28
  export interface CreateUnprocessedFileResult {
29
29
  byte_size: number;
30
30
  next_byte: number;
31
+ complete: boolean;
31
32
  id: string;
33
+ md5: string;
32
34
  processes: z.infer<typeof FileProcessors>;
33
35
  asset_id: string;
34
36
  }
35
37
  export interface UpdateUnprocessedFileResult {
36
38
  byte_size?: number;
37
39
  next_byte: number;
40
+ complete: boolean;
38
41
  id: string;
42
+ md5: string;
39
43
  processes: z.infer<typeof FileProcessors>;
40
44
  asset_id: string;
41
45
  }
@@ -22,7 +22,7 @@ const FileProcessors = z.array(FileProcessor).refine(
22
22
  );
23
23
  const MAX_FILE_SIZE = 1024 * 1024 * 1024;
24
24
  const CreateUnprocessedFilePayload = z.object({
25
- id: z.string(),
25
+ md5: z.string(),
26
26
  filename: z.string(),
27
27
  processes: FileProcessors.optional(),
28
28
  byte_size: z.number().int().max(MAX_FILE_SIZE)
@@ -80,55 +80,69 @@ const uploadUnprocessedFile = async (client, fileId, fileStream, fileSize) => {
80
80
  });
81
81
  log("Unprocessed file upload complete");
82
82
  };
83
+ const processResource = async (client, filename, md5, byteSize, processor, doUpload) => {
84
+ log("Processing", { filename, md5, byteSize, processor });
85
+ const unprocessedFile = await createUnprocessedFile(client, {
86
+ md5,
87
+ processes: [],
88
+ filename,
89
+ byte_size: byteSize
90
+ });
91
+ if (unprocessedFile.complete === false) {
92
+ await doUpload(unprocessedFile.id);
93
+ }
94
+ if (unprocessedFile.processes.includes(processor)) {
95
+ log("File already processed", unprocessedFile);
96
+ return unprocessedFile;
97
+ }
98
+ const fileInformation = await updateUnprocessedFile(
99
+ client,
100
+ unprocessedFile.id,
101
+ {
102
+ processes: [processor]
103
+ }
104
+ );
105
+ log("File processed", fileInformation);
106
+ return fileInformation;
107
+ };
83
108
  const buildBufferProcessor = (processor) => {
84
109
  return async (client, buffer, filename = "buffer") => {
85
110
  log(`Processing file buffer: ${processor}`, filename);
86
- const fileId = md5Buffer(buffer);
87
- log("File ID", fileId);
88
- log(`File size: ${buffer.byteLength} bytes`);
89
- await createUnprocessedFile(client, {
90
- id: fileId,
91
- processes: [],
111
+ const md5 = md5Buffer(buffer);
112
+ return await processResource(
113
+ client,
92
114
  filename,
93
- byte_size: buffer.byteLength
94
- });
95
- const readStream = new Readable({
96
- read() {
97
- readStream.push(buffer);
98
- readStream.push(null);
115
+ md5,
116
+ buffer.byteLength,
117
+ processor,
118
+ async (id) => {
119
+ const readStream = new Readable({
120
+ read() {
121
+ readStream.push(buffer);
122
+ readStream.push(null);
123
+ }
124
+ });
125
+ await uploadUnprocessedFile(client, id, readStream, buffer.byteLength);
99
126
  }
100
- });
101
- await uploadUnprocessedFile(client, fileId, readStream, buffer.byteLength);
102
- const fileInformation = await updateUnprocessedFile(client, fileId, {
103
- processes: [processor]
104
- });
105
- log("File processed", fileInformation);
106
- return fileInformation;
127
+ );
107
128
  };
108
129
  };
109
130
  const buildFileProcessor = (processor) => {
110
131
  return async (client, filePath) => {
111
132
  log(`Processing file ${processor}`, filePath);
112
- const fileId = await md5FilePath(filePath);
113
- log("File ID", fileId);
114
- await createUnprocessedFile(client, {
115
- id: fileId,
116
- processes: [],
117
- filename: basename(filePath),
118
- byte_size: (await stat(filePath)).size
119
- });
120
- const readStream = createReadStream(filePath);
121
- await uploadUnprocessedFile(
133
+ const md5 = await md5FilePath(filePath);
134
+ const byteSize = (await stat(filePath)).size;
135
+ return await processResource(
122
136
  client,
123
- fileId,
124
- readStream,
125
- (await stat(filePath)).size
137
+ basename(filePath),
138
+ md5,
139
+ byteSize,
140
+ processor,
141
+ async (id) => {
142
+ const readStream = createReadStream(filePath);
143
+ return await uploadUnprocessedFile(client, id, readStream, byteSize);
144
+ }
126
145
  );
127
- const fileInformation = await updateUnprocessedFile(client, fileId, {
128
- processes: [processor]
129
- });
130
- log("File processed", fileInformation);
131
- return fileInformation;
132
146
  };
133
147
  };
134
148
  const processAVFileBuffer = buildBufferProcessor("isobmff");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/api",
3
- "version": "0.9.0-beta.3",
3
+ "version": "0.10.0-beta.2",
4
4
  "description": "API functions for EditFrame",
5
5
  "exports": {
6
6
  ".": {
@@ -28,7 +28,7 @@
28
28
  "vite-tsconfig-paths": "^4.3.2"
29
29
  },
30
30
  "dependencies": {
31
- "@editframe/assets": "0.9.0-beta.3",
31
+ "@editframe/assets": "0.10.0-beta.2",
32
32
  "debug": "^4.3.5",
33
33
  "jsonwebtoken": "^9.0.2",
34
34
  "node-fetch": "^3.3.2",
@@ -18,7 +18,7 @@ describe("CaptionFile", () => {
18
18
  test("Throws when file is too large", async () => {
19
19
  await expect(
20
20
  createCaptionFile(client, {
21
- id: "test-id",
21
+ md5: "test-md5",
22
22
  filename: "test",
23
23
  byte_size: 1024 * 1024 * 3,
24
24
  }),
@@ -29,14 +29,14 @@ describe("CaptionFile", () => {
29
29
 
30
30
  test("Throws when server returns an error", async () => {
31
31
  server.use(
32
- http.post("http://localhost/api/video2/caption_files", () =>
32
+ http.post("http://localhost/api/v1/caption_files", () =>
33
33
  HttpResponse.text("Internal Server Error", { status: 500 }),
34
34
  ),
35
35
  );
36
36
 
37
37
  await expect(
38
38
  createCaptionFile(client, {
39
- id: "test-id",
39
+ md5: "test-md5",
40
40
  filename: "test",
41
41
  byte_size: 4,
42
42
  }),
@@ -47,7 +47,7 @@ describe("CaptionFile", () => {
47
47
 
48
48
  test("Returns json data from the http response", async () => {
49
49
  server.use(
50
- http.post("http://localhost/api/video2/caption_files", () =>
50
+ http.post("http://localhost/api/v1/caption_files", () =>
51
51
  HttpResponse.json(
52
52
  { id: "test-id" },
53
53
  { status: 200, statusText: "OK" },
@@ -56,7 +56,7 @@ describe("CaptionFile", () => {
56
56
  );
57
57
 
58
58
  const response = await createCaptionFile(client, {
59
- id: "test-id",
59
+ md5: "test-md5",
60
60
  filename: "test",
61
61
  byte_size: 4,
62
62
  });
@@ -81,9 +81,8 @@ describe("CaptionFile", () => {
81
81
 
82
82
  test("Throws when server returns an error", async () => {
83
83
  server.use(
84
- http.post(
85
- "http://localhost/api/video2/caption_files/test-id/upload",
86
- () => HttpResponse.text("Internal Server Error", { status: 500 }),
84
+ http.post("http://localhost/api/v1/caption_files/test-id/upload", () =>
85
+ HttpResponse.text("Internal Server Error", { status: 500 }),
87
86
  ),
88
87
  );
89
88
 
@@ -101,13 +100,11 @@ describe("CaptionFile", () => {
101
100
 
102
101
  test("Returns json data from the http response", async () => {
103
102
  server.use(
104
- http.post(
105
- "http://localhost/api/video2/caption_files/test-id/upload",
106
- () =>
107
- HttpResponse.json(
108
- { id: "test-id" },
109
- { status: 200, statusText: "OK" },
110
- ),
103
+ http.post("http://localhost/api/v1/caption_files/test-id/upload", () =>
104
+ HttpResponse.json(
105
+ { id: "test-id" },
106
+ { status: 200, statusText: "OK" },
107
+ ),
111
108
  ),
112
109
  );
113
110
 
@@ -10,7 +10,7 @@ const log = debug("ef:api:caption-file");
10
10
  const MAX_CAPTION_SIZE = 1024 * 1024 * 2; // 2MB
11
11
 
12
12
  export const CreateCaptionFilePayload = z.object({
13
- id: z.string(),
13
+ md5: z.string(),
14
14
  filename: z.string(),
15
15
  byte_size: z.number().int().max(MAX_CAPTION_SIZE),
16
16
  });
@@ -18,6 +18,7 @@ export const CreateCaptionFilePayload = z.object({
18
18
  export interface CreateCaptionFileResult {
19
19
  complete: boolean | null;
20
20
  id: string;
21
+ md5: string;
21
22
  asset_id: string;
22
23
  }
23
24
 
@@ -52,13 +53,10 @@ export const createCaptionFile = async (
52
53
  ) => {
53
54
  log("Creating caption file", payload);
54
55
  restrictSize(payload.byte_size);
55
- const response = await client.authenticatedFetch(
56
- "/api/video2/caption_files",
57
- {
58
- method: "POST",
59
- body: JSON.stringify(payload),
60
- },
61
- );
56
+ const response = await client.authenticatedFetch("/api/v1/caption_files", {
57
+ method: "POST",
58
+ body: JSON.stringify(payload),
59
+ });
62
60
  log("Caption file created", response);
63
61
 
64
62
  if (response.ok) {
@@ -80,7 +78,7 @@ export const uploadCaptionFile = async (
80
78
  restrictSize(fileSize);
81
79
 
82
80
  const response = await client.authenticatedFetch(
83
- `/api/video2/caption_files/${fileId}/upload`,
81
+ `/api/v1/caption_files/${fileId}/upload`,
84
82
  {
85
83
  method: "POST",
86
84
  body: fileStream,
@@ -19,7 +19,7 @@ describe("ImageFile", () => {
19
19
  test("Throws when file is too large", async () => {
20
20
  await expect(
21
21
  createImageFile(client, {
22
- id: "test-id",
22
+ md5: "test-md5",
23
23
  filename: "test",
24
24
  byte_size: 1024 * 1024 * 17,
25
25
  height: 100,
@@ -43,14 +43,14 @@ describe("ImageFile", () => {
43
43
 
44
44
  test("Throws when server returns an error", async () => {
45
45
  server.use(
46
- http.post("http://localhost/api/video2/image_files", () =>
46
+ http.post("http://localhost/api/v1/image_files", () =>
47
47
  HttpResponse.text("Internal Server Error", { status: 500 }),
48
48
  ),
49
49
  );
50
50
 
51
51
  await expect(
52
52
  createImageFile(client, {
53
- id: "test-id",
53
+ md5: "test-md5",
54
54
  filename: "test",
55
55
  byte_size: 4,
56
56
  height: 100,
@@ -62,16 +62,16 @@ describe("ImageFile", () => {
62
62
 
63
63
  test("Returns json data from the http response", async () => {
64
64
  server.use(
65
- http.post("http://localhost/api/video2/image_files", () =>
65
+ http.post("http://localhost/api/v1/image_files", () =>
66
66
  HttpResponse.json(
67
- { id: "test-id" },
67
+ { md5: "test-md5" },
68
68
  { status: 200, statusText: "OK" },
69
69
  ),
70
70
  ),
71
71
  );
72
72
 
73
73
  const response = await createImageFile(client, {
74
- id: "test-id",
74
+ md5: "test-md5",
75
75
  filename: "test",
76
76
  byte_size: 4,
77
77
  height: 100,
@@ -79,7 +79,7 @@ describe("ImageFile", () => {
79
79
  mime_type: "image/jpeg",
80
80
  });
81
81
 
82
- expect(response).toEqual({ id: "test-id" });
82
+ expect(response).toEqual({ md5: "test-md5" });
83
83
  });
84
84
  });
85
85
 
@@ -100,7 +100,7 @@ describe("ImageFile", () => {
100
100
  test("Throws if upload fails", async () => {
101
101
  server.use(
102
102
  http.post(
103
- "http://localhost/api/video2/image_files/test-file-id/upload",
103
+ "http://localhost/api/v1/image_files/test-file-id/upload",
104
104
  () => HttpResponse.text("Internal Server Error", { status: 500 }),
105
105
  ),
106
106
  );
@@ -113,14 +113,14 @@ describe("ImageFile", () => {
113
113
  4,
114
114
  ),
115
115
  ).rejects.toThrowError(
116
- "Failed to upload chunk 0 for /api/video2/image_files/test-file-id/upload 500 Internal Server Error",
116
+ "Failed to upload chunk 0 for /api/v1/image_files/test-file-id/upload 500 Internal Server Error",
117
117
  );
118
118
  });
119
119
 
120
120
  test("Uploads file", async () => {
121
121
  server.use(
122
122
  http.post(
123
- "http://localhost/api/video2/image_files/test-file-id/upload",
123
+ "http://localhost/api/v1/image_files/test-file-id/upload",
124
124
  () => HttpResponse.json(null, { status: 201 }),
125
125
  ),
126
126
  );
@@ -11,7 +11,7 @@ const log = debug("ef:api:image-file");
11
11
  const MAX_IMAGE_SIZE = 1024 * 1024 * 16; // 16MB
12
12
 
13
13
  export const CreateImageFilePayload = z.object({
14
- id: z.string(),
14
+ md5: z.string(),
15
15
  height: z.number().int(),
16
16
  width: z.number().int(),
17
17
  mime_type: z.enum(["image/jpeg", "image/png", "image/jpg", "image/webp"]),
@@ -22,6 +22,7 @@ export const CreateImageFilePayload = z.object({
22
22
  export interface CreateImageFileResult {
23
23
  complete: boolean | null;
24
24
  id: string;
25
+ md5: string;
25
26
  asset_id: string;
26
27
  }
27
28
 
@@ -31,7 +32,7 @@ export const createImageFile = async (
31
32
  ) => {
32
33
  log("Creating image file", payload);
33
34
  CreateImageFilePayload.parse(payload);
34
- const response = await client.authenticatedFetch("/api/video2/image_files", {
35
+ const response = await client.authenticatedFetch("/api/v1/image_files", {
35
36
  method: "POST",
36
37
  body: JSON.stringify(payload),
37
38
  });
@@ -61,7 +62,7 @@ export const uploadImageFile = async (
61
62
  }
62
63
 
63
64
  const result = await uploadChunks(client, {
64
- url: `/api/video2/image_files/${fileId}/upload`,
65
+ url: `/api/v1/image_files/${fileId}/upload`,
65
66
  fileSize,
66
67
  fileStream,
67
68
  });
@@ -17,14 +17,14 @@ describe("ISOBMFFFile", () => {
17
17
  describe("createISOBMFFFile", () => {
18
18
  test("Throws when server returns an error", async () => {
19
19
  server.use(
20
- http.post("http://localhost/api/video2/isobmff_files", () =>
20
+ http.post("http://localhost/api/v1/isobmff_files", () =>
21
21
  HttpResponse.text("Internal Server Error", { status: 500 }),
22
22
  ),
23
23
  );
24
24
 
25
25
  await expect(
26
26
  createISOBMFFFile(client, {
27
- id: "test-id",
27
+ md5: "test-md5",
28
28
  filename: "test",
29
29
  }),
30
30
  ).rejects.toThrowError(
@@ -34,7 +34,7 @@ describe("ISOBMFFFile", () => {
34
34
 
35
35
  test("Returns json data from the http response", async () => {
36
36
  server.use(
37
- http.post("http://localhost/api/video2/isobmff_files", () =>
37
+ http.post("http://localhost/api/v1/isobmff_files", () =>
38
38
  HttpResponse.json(
39
39
  { id: "test-id" },
40
40
  { status: 200, statusText: "OK" },
@@ -43,7 +43,7 @@ describe("ISOBMFFFile", () => {
43
43
  );
44
44
 
45
45
  const response = await createISOBMFFFile(client, {
46
- id: "test-id",
46
+ md5: "test-md5",
47
47
  filename: "test",
48
48
  });
49
49
 
@@ -66,7 +66,7 @@ describe("ISOBMFFFile", () => {
66
66
  test("Throws when server returns an error", async () => {
67
67
  server.use(
68
68
  http.post(
69
- "http://localhost/api/video2/isobmff_files/test-id/index/upload",
69
+ "http://localhost/api/v1/isobmff_files/test-id/index/upload",
70
70
  () => HttpResponse.text("Internal Server Error", { status: 500 }),
71
71
  ),
72
72
  );
@@ -86,7 +86,7 @@ describe("ISOBMFFFile", () => {
86
86
  test("Returns json data from the http response", async () => {
87
87
  server.use(
88
88
  http.post(
89
- "http://localhost/api/video2/isobmff_files/test-id/index/upload",
89
+ "http://localhost/api/v1/isobmff_files/test-id/index/upload",
90
90
  () =>
91
91
  HttpResponse.json(
92
92
  { fragment_index_complete: true },
@@ -9,13 +9,15 @@ const log = debug("ef:api:isobmff-file");
9
9
  const FILE_SIZE_LIMIT = 1024 * 1024 * 2; // 32MB
10
10
 
11
11
  export const CreateISOBMFFFilePayload = z.object({
12
- id: z.string(),
12
+ md5: z.string(),
13
13
  filename: z.string(),
14
14
  });
15
15
 
16
16
  export interface CreateISOBMFFFileResult {
17
17
  fragment_index_complete: boolean;
18
+ filename: string;
18
19
  id: string;
20
+ md5: string;
19
21
  asset_id: string;
20
22
  }
21
23
 
@@ -24,13 +26,10 @@ export const createISOBMFFFile = async (
24
26
  payload: z.infer<typeof CreateISOBMFFFilePayload>,
25
27
  ) => {
26
28
  log("Creating isobmff file", payload);
27
- const response = await client.authenticatedFetch(
28
- "/api/video2/isobmff_files",
29
- {
30
- method: "POST",
31
- body: JSON.stringify(payload),
32
- },
33
- );
29
+ const response = await client.authenticatedFetch("/api/v1/isobmff_files", {
30
+ method: "POST",
31
+ body: JSON.stringify(payload),
32
+ });
34
33
 
35
34
  log("ISOBMFF file created", response);
36
35
 
@@ -54,7 +53,7 @@ export const uploadFragmentIndex = async (
54
53
  throw new Error(`File size exceeds limit of ${FILE_SIZE_LIMIT} bytes`);
55
54
  }
56
55
  const response = await client.authenticatedFetch(
57
- `/api/video2/isobmff_files/${fileId}/index/upload`,
56
+ `/api/v1/isobmff_files/${fileId}/index/upload`,
58
57
  {
59
58
  method: "POST",
60
59
  body: fileStream,
@@ -39,7 +39,7 @@ describe("ISOBMFF Track", () => {
39
39
 
40
40
  test("Throws when server returns an error", async () => {
41
41
  server.use(
42
- http.post("http://localhost/api/video2/isobmff_tracks", () =>
42
+ http.post("http://localhost/api/v1/isobmff_tracks", () =>
43
43
  HttpResponse.text("Internal Server Error", { status: 500 }),
44
44
  ),
45
45
  );
@@ -53,7 +53,7 @@ describe("ISOBMFF Track", () => {
53
53
 
54
54
  test("Returns json data from the http response", async () => {
55
55
  server.use(
56
- http.post("http://localhost/api/video2/isobmff_tracks", () =>
56
+ http.post("http://localhost/api/v1/isobmff_tracks", () =>
57
57
  HttpResponse.json(
58
58
  { testResponse: "test" },
59
59
  { status: 200, statusText: "OK" },
@@ -74,7 +74,7 @@ describe("ISOBMFF Track", () => {
74
74
  test("Throws when server returns an error", async () => {
75
75
  server.use(
76
76
  http.post(
77
- "http://localhost/api/video2/isobmff_tracks/test-file/1/upload",
77
+ "http://localhost/api/v1/isobmff_tracks/test-file/1/upload",
78
78
  () => HttpResponse.text("Internal Server Error", { status: 500 }),
79
79
  ),
80
80
  );
@@ -88,14 +88,14 @@ describe("ISOBMFF Track", () => {
88
88
  4,
89
89
  ),
90
90
  ).rejects.toThrowError(
91
- "Failed to upload chunk 0 for /api/video2/isobmff_tracks/test-file/1/upload 500 Internal Server Error",
91
+ "Failed to upload chunk 0 for /api/v1/isobmff_tracks/test-file/1/upload 500 Internal Server Error",
92
92
  );
93
93
  });
94
94
 
95
95
  test("Succeeds when server returns a success", async () => {
96
96
  server.use(
97
97
  http.post(
98
- "http://localhost/api/video2/isobmff_tracks/test-file/1/upload",
98
+ "http://localhost/api/v1/isobmff_tracks/test-file/1/upload",
99
99
  () => HttpResponse.json({}, { status: 201 }),
100
100
  ),
101
101
  );
@@ -46,13 +46,10 @@ export const createISOBMFFTrack = async (
46
46
  ) => {
47
47
  log("Creating isobmff track", payload);
48
48
  CreateISOBMFFTrackPayload.parse(payload);
49
- const response = await client.authenticatedFetch(
50
- "/api/video2/isobmff_tracks",
51
- {
52
- method: "POST",
53
- body: JSON.stringify(payload),
54
- },
55
- );
49
+ const response = await client.authenticatedFetch("/api/v1/isobmff_tracks", {
50
+ method: "POST",
51
+ body: JSON.stringify(payload),
52
+ });
56
53
 
57
54
  log("ISOBMFF track created", response);
58
55
  if (response.ok) {
@@ -74,7 +71,7 @@ export const uploadISOBMFFTrack = async (
74
71
  log("Uploading fragment track", fileId);
75
72
 
76
73
  await uploadChunks(client, {
77
- url: `/api/video2/isobmff_tracks/${fileId}/${trackId}/upload`,
74
+ url: `/api/v1/isobmff_tracks/${fileId}/${trackId}/upload`,
78
75
  fileStream,
79
76
  fileSize: trackSize,
80
77
  });
@@ -17,7 +17,7 @@ describe("Renders", () => {
17
17
  describe("createRender", () => {
18
18
  test("throws if server returns an error", async () => {
19
19
  server.use(
20
- http.post("http://localhost/api/video2/renders", () =>
20
+ http.post("http://localhost/api/v1/renders", () =>
21
21
  HttpResponse.text("Internal Server Error", { status: 500 }),
22
22
  ),
23
23
  );
@@ -31,7 +31,7 @@ describe("Renders", () => {
31
31
 
32
32
  test("returns json data from the http response", async () => {
33
33
  server.use(
34
- http.post("http://localhost/api/video2/renders", () =>
34
+ http.post("http://localhost/api/v1/renders", () =>
35
35
  HttpResponse.json(
36
36
  { testResponse: "test" },
37
37
  { status: 200, statusText: "OK" },
@@ -61,7 +61,7 @@ describe("Renders", () => {
61
61
 
62
62
  test("throws if server returns an error", async () => {
63
63
  server.use(
64
- http.post("http://localhost/api/video2/renders/test-id/upload", () =>
64
+ http.post("http://localhost/api/v1/renders/test-id/upload", () =>
65
65
  HttpResponse.text("Internal Server Error", { status: 500 }),
66
66
  ),
67
67
  );
@@ -80,7 +80,7 @@ describe("Renders", () => {
80
80
 
81
81
  test("returns json data from the http response", async () => {
82
82
  server.use(
83
- http.post("http://localhost/api/video2/renders/test-id/upload", () =>
83
+ http.post("http://localhost/api/v1/renders/test-id/upload", () =>
84
84
  HttpResponse.json(
85
85
  { testResponse: "test" },
86
86
  { status: 200, statusText: "OK" },
@@ -102,7 +102,7 @@ describe("Renders", () => {
102
102
 
103
103
  const createTestRender = () =>
104
104
  ({
105
- id: "test-id",
105
+ md5: "test-md5",
106
106
  fps: 30,
107
107
  width: 1920,
108
108
  height: 1080,
@@ -9,7 +9,7 @@ const log = debug("ef:api:renders");
9
9
  const FILE_SIZE_LIMIT = 1024 * 1024 * 16; // 16MiB
10
10
 
11
11
  export const CreateRenderPayload = z.object({
12
- id: z.string().uuid(),
12
+ md5: z.string(),
13
13
  fps: z.number(),
14
14
  width: z.number().int(),
15
15
  height: z.number().int(),
@@ -19,8 +19,9 @@ export const CreateRenderPayload = z.object({
19
19
  });
20
20
 
21
21
  export interface CreateRenderResult {
22
- status: "complete" | "created" | "failed" | "pending" | "rendering";
23
22
  id: string;
23
+ md5: string;
24
+ status: "complete" | "created" | "failed" | "pending" | "rendering";
24
25
  }
25
26
 
26
27
  export const createRender = async (
@@ -28,7 +29,7 @@ export const createRender = async (
28
29
  payload: z.infer<typeof CreateRenderPayload>,
29
30
  ) => {
30
31
  log("Creating render", payload);
31
- const response = await client.authenticatedFetch("/api/video2/renders", {
32
+ const response = await client.authenticatedFetch("/api/v1/renders", {
32
33
  method: "POST",
33
34
  body: JSON.stringify(payload),
34
35
  });
@@ -55,7 +56,7 @@ export const uploadRender = async (
55
56
  throw new Error(`File size exceeds limit of ${FILE_SIZE_LIMIT} bytes`);
56
57
  }
57
58
  const response = await client.authenticatedFetch(
58
- `/api/video2/renders/${fileId}/upload`,
59
+ `/api/v1/renders/${fileId}/upload`,
59
60
  {
60
61
  method: "POST",
61
62
  body: fileStream,
@@ -31,7 +31,7 @@ describe("Unprocessed File", () => {
31
31
  test("Throws when file is too large", async () => {
32
32
  await expect(
33
33
  createUnprocessedFile(client, {
34
- id: "test-file",
34
+ md5: "test-file",
35
35
  filename: "test-file",
36
36
  processes: [],
37
37
  byte_size: 1024 * 1024 * 1025,
@@ -60,7 +60,7 @@ describe("Unprocessed File", () => {
60
60
 
61
61
  await expect(
62
62
  createUnprocessedFile(client, {
63
- id: "test-file",
63
+ md5: "test-file",
64
64
  filename: "test-file",
65
65
  processes: [],
66
66
  byte_size: 1024 * 1024,
@@ -81,7 +81,7 @@ describe("Unprocessed File", () => {
81
81
  );
82
82
 
83
83
  const result = await createUnprocessedFile(client, {
84
- id: "test-file",
84
+ md5: "test-file",
85
85
  filename: "test-file",
86
86
  processes: [],
87
87
  byte_size: 1024 * 1024,
@@ -184,7 +184,14 @@ describe("Unprocessed File", () => {
184
184
  test("Throws when server responds with an error when uploading file", async () => {
185
185
  server.use(
186
186
  http.post("http://localhost/api/v1/unprocessed_files", () =>
187
- HttpResponse.json({}, { status: 200 }),
187
+ HttpResponse.json(
188
+ {
189
+ complete: false,
190
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
191
+ processes: [],
192
+ },
193
+ { status: 200 },
194
+ ),
188
195
  ),
189
196
  http.post(
190
197
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -202,7 +209,14 @@ describe("Unprocessed File", () => {
202
209
  test("Throws when server responds with an error when updating file", async () => {
203
210
  server.use(
204
211
  http.post("http://localhost/api/v1/unprocessed_files", () =>
205
- HttpResponse.json({}, { status: 200 }),
212
+ HttpResponse.json(
213
+ {
214
+ complete: false,
215
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
216
+ processes: [],
217
+ },
218
+ { status: 200 },
219
+ ),
206
220
  ),
207
221
  http.post(
208
222
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -224,7 +238,14 @@ describe("Unprocessed File", () => {
224
238
  test("Returns json data when upload is successful", async () => {
225
239
  server.use(
226
240
  http.post("http://localhost/api/v1/unprocessed_files", () =>
227
- HttpResponse.json({}, { status: 200 }),
241
+ HttpResponse.json(
242
+ {
243
+ complete: false,
244
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
245
+ processes: [],
246
+ },
247
+ { status: 200 },
248
+ ),
228
249
  ),
229
250
  http.post(
230
251
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -258,7 +279,14 @@ describe("Unprocessed File", () => {
258
279
  test("Throws when server responds with an error when uploading file", async () => {
259
280
  server.use(
260
281
  http.post("http://localhost/api/v1/unprocessed_files", () =>
261
- HttpResponse.json({}, { status: 200 }),
282
+ HttpResponse.json(
283
+ {
284
+ complete: false,
285
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
286
+ processes: [],
287
+ },
288
+ { status: 200 },
289
+ ),
262
290
  ),
263
291
  http.post(
264
292
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -274,7 +302,14 @@ describe("Unprocessed File", () => {
274
302
  test("Throws when server responds with an error when updating file", async () => {
275
303
  server.use(
276
304
  http.post("http://localhost/api/v1/unprocessed_files", () =>
277
- HttpResponse.json({}, { status: 200 }),
305
+ HttpResponse.json(
306
+ {
307
+ complete: false,
308
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
309
+ processes: [],
310
+ },
311
+ { status: 200 },
312
+ ),
278
313
  ),
279
314
  http.post(
280
315
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -294,7 +329,14 @@ describe("Unprocessed File", () => {
294
329
  test("Returns json data when upload is successful", async () => {
295
330
  server.use(
296
331
  http.post("http://localhost/api/v1/unprocessed_files", () =>
297
- HttpResponse.json({}, { status: 200 }),
332
+ HttpResponse.json(
333
+ {
334
+ complete: false,
335
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
336
+ processes: [],
337
+ },
338
+ { status: 200 },
339
+ ),
298
340
  ),
299
341
  http.post(
300
342
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -330,7 +372,14 @@ describe("Unprocessed File", () => {
330
372
  test("Throws when server responds with an error when uploading file", async () => {
331
373
  server.use(
332
374
  http.post("http://localhost/api/v1/unprocessed_files", () =>
333
- HttpResponse.json({}, { status: 200 }),
375
+ HttpResponse.json(
376
+ {
377
+ complete: false,
378
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
379
+ processes: [],
380
+ },
381
+ { status: 200 },
382
+ ),
334
383
  ),
335
384
  http.post(
336
385
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -348,7 +397,14 @@ describe("Unprocessed File", () => {
348
397
  test("Throws when server responds with an error when updating file", async () => {
349
398
  server.use(
350
399
  http.post("http://localhost/api/v1/unprocessed_files", () =>
351
- HttpResponse.json({}, { status: 200 }),
400
+ HttpResponse.json(
401
+ {
402
+ complete: false,
403
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
404
+ processes: [],
405
+ },
406
+ { status: 200 },
407
+ ),
352
408
  ),
353
409
  http.post(
354
410
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -370,7 +426,14 @@ describe("Unprocessed File", () => {
370
426
  test("Returns json data when upload is successful", async () => {
371
427
  server.use(
372
428
  http.post("http://localhost/api/v1/unprocessed_files", () =>
373
- HttpResponse.json({}, { status: 200 }),
429
+ HttpResponse.json(
430
+ {
431
+ complete: false,
432
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
433
+ processes: [],
434
+ },
435
+ { status: 200 },
436
+ ),
374
437
  ),
375
438
  http.post(
376
439
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -404,7 +467,14 @@ describe("Unprocessed File", () => {
404
467
  test("Throws when server responds with an error when uploading file", async () => {
405
468
  server.use(
406
469
  http.post("http://localhost/api/v1/unprocessed_files", () =>
407
- HttpResponse.json({}, { status: 200 }),
470
+ HttpResponse.json(
471
+ {
472
+ complete: false,
473
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
474
+ processes: [],
475
+ },
476
+ { status: 200 },
477
+ ),
408
478
  ),
409
479
  http.post(
410
480
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -420,7 +490,14 @@ describe("Unprocessed File", () => {
420
490
  test("Throws when server responds with an error when updating file", async () => {
421
491
  server.use(
422
492
  http.post("http://localhost/api/v1/unprocessed_files", () =>
423
- HttpResponse.json({}, { status: 200 }),
493
+ HttpResponse.json(
494
+ {
495
+ complete: false,
496
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
497
+ processes: [],
498
+ },
499
+ { status: 200 },
500
+ ),
424
501
  ),
425
502
  http.post(
426
503
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -440,7 +517,14 @@ describe("Unprocessed File", () => {
440
517
  test("Returns json data when upload is successful", async () => {
441
518
  server.use(
442
519
  http.post("http://localhost/api/v1/unprocessed_files", () =>
443
- HttpResponse.json({}, { status: 200 }),
520
+ HttpResponse.json(
521
+ {
522
+ complete: false,
523
+ id: "098f6bcd-4621-d373-cade-4e832627b4f6",
524
+ processes: [],
525
+ },
526
+ { status: 200 },
527
+ ),
444
528
  ),
445
529
  http.post(
446
530
  "http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
@@ -31,7 +31,7 @@ const FileProcessors = z.array(FileProcessor).refine(
31
31
  const MAX_FILE_SIZE = 1024 * 1024 * 1024; // 1GiB
32
32
 
33
33
  export const CreateUnprocessedFilePayload = z.object({
34
- id: z.string(),
34
+ md5: z.string(),
35
35
  filename: z.string(),
36
36
  processes: FileProcessors.optional(),
37
37
  byte_size: z.number().int().max(MAX_FILE_SIZE),
@@ -44,7 +44,9 @@ export const UpdateUnprocessedFilePayload = z.object({
44
44
  export interface CreateUnprocessedFileResult {
45
45
  byte_size: number;
46
46
  next_byte: number;
47
+ complete: boolean;
47
48
  id: string;
49
+ md5: string;
48
50
  processes: z.infer<typeof FileProcessors>;
49
51
  asset_id: string;
50
52
  }
@@ -52,7 +54,9 @@ export interface CreateUnprocessedFileResult {
52
54
  export interface UpdateUnprocessedFileResult {
53
55
  byte_size?: number;
54
56
  next_byte: number;
57
+ complete: boolean;
55
58
  id: string;
59
+ md5: string;
56
60
  processes: z.infer<typeof FileProcessors>;
57
61
  asset_id: string;
58
62
  }
@@ -130,67 +134,85 @@ export const uploadUnprocessedFile = async (
130
134
  log("Unprocessed file upload complete");
131
135
  };
132
136
 
133
- const buildBufferProcessor = (processor: z.infer<typeof FileProcessor>) => {
134
- return async (client: Client, buffer: Buffer, filename = "buffer") => {
135
- log(`Processing file buffer: ${processor}`, filename);
136
- const fileId = md5Buffer(buffer);
137
-
138
- log("File ID", fileId);
139
- log(`File size: ${buffer.byteLength} bytes`);
137
+ const processResource = async (
138
+ client: Client,
139
+ filename: string,
140
+ md5: string,
141
+ byteSize: number,
142
+ processor: z.infer<typeof FileProcessor>,
143
+ doUpload: (id: string) => Promise<void>,
144
+ ) => {
145
+ log("Processing", { filename, md5, byteSize, processor });
140
146
 
141
- await createUnprocessedFile(client, {
142
- id: fileId,
143
- processes: [],
144
- filename,
145
- byte_size: buffer.byteLength,
146
- });
147
+ const unprocessedFile = await createUnprocessedFile(client, {
148
+ md5: md5,
149
+ processes: [],
150
+ filename,
151
+ byte_size: byteSize,
152
+ });
147
153
 
148
- const readStream = new Readable({
149
- read() {
150
- readStream.push(buffer);
151
- readStream.push(null);
152
- },
153
- });
154
+ if (unprocessedFile.complete === false) {
155
+ await doUpload(unprocessedFile.id);
156
+ }
154
157
 
155
- await uploadUnprocessedFile(client, fileId, readStream, buffer.byteLength);
158
+ if (unprocessedFile.processes.includes(processor)) {
159
+ log("File already processed", unprocessedFile);
160
+ return unprocessedFile;
161
+ }
156
162
 
157
- const fileInformation = await updateUnprocessedFile(client, fileId, {
163
+ const fileInformation = await updateUnprocessedFile(
164
+ client,
165
+ unprocessedFile.id,
166
+ {
158
167
  processes: [processor],
159
- });
168
+ },
169
+ );
170
+ log("File processed", fileInformation);
171
+ return fileInformation;
172
+ };
160
173
 
161
- log("File processed", fileInformation);
162
- return fileInformation;
174
+ const buildBufferProcessor = (processor: z.infer<typeof FileProcessor>) => {
175
+ return async (client: Client, buffer: Buffer, filename = "buffer") => {
176
+ log(`Processing file buffer: ${processor}`, filename);
177
+ const md5 = md5Buffer(buffer);
178
+
179
+ return await processResource(
180
+ client,
181
+ filename,
182
+ md5,
183
+ buffer.byteLength,
184
+ processor,
185
+ async (id: string) => {
186
+ const readStream = new Readable({
187
+ read() {
188
+ readStream.push(buffer);
189
+ readStream.push(null);
190
+ },
191
+ });
192
+
193
+ await uploadUnprocessedFile(client, id, readStream, buffer.byteLength);
194
+ },
195
+ );
163
196
  };
164
197
  };
165
198
 
166
199
  const buildFileProcessor = (processor: z.infer<typeof FileProcessor>) => {
167
200
  return async (client: Client, filePath: string) => {
168
201
  log(`Processing file ${processor}`, filePath);
169
- const fileId = await md5FilePath(filePath);
170
-
171
- log("File ID", fileId);
172
- await createUnprocessedFile(client, {
173
- id: fileId,
174
- processes: [],
175
- filename: basename(filePath),
176
- byte_size: (await stat(filePath)).size,
177
- });
202
+ const md5 = await md5FilePath(filePath);
203
+ const byteSize = (await stat(filePath)).size;
178
204
 
179
- const readStream = createReadStream(filePath);
180
-
181
- await uploadUnprocessedFile(
205
+ return await processResource(
182
206
  client,
183
- fileId,
184
- readStream,
185
- (await stat(filePath)).size,
207
+ basename(filePath),
208
+ md5,
209
+ byteSize,
210
+ processor,
211
+ async (id: string) => {
212
+ const readStream = createReadStream(filePath);
213
+ return await uploadUnprocessedFile(client, id, readStream, byteSize);
214
+ },
186
215
  );
187
-
188
- const fileInformation = await updateUnprocessedFile(client, fileId, {
189
- processes: [processor],
190
- });
191
-
192
- log("File processed", fileInformation);
193
- return fileInformation;
194
216
  };
195
217
  };
196
218