@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.
- package/dist/resources/caption-file.d.ts +4 -3
- package/dist/resources/caption-file.js +6 -9
- package/dist/resources/image-file.d.ts +4 -3
- package/dist/resources/image-file.js +3 -3
- package/dist/resources/isobmff-file.d.ts +5 -3
- package/dist/resources/isobmff-file.js +6 -9
- package/dist/resources/isobmff-track.js +5 -8
- package/dist/resources/renders.d.ts +5 -4
- package/dist/resources/renders.js +3 -3
- package/dist/resources/unprocessed-file.d.ts +7 -3
- package/dist/resources/unprocessed-file.js +52 -38
- package/package.json +2 -2
- package/src/resources/caption-file.test.ts +12 -15
- package/src/resources/caption-file.ts +7 -9
- package/src/resources/image-file.test.ts +10 -10
- package/src/resources/image-file.ts +4 -3
- package/src/resources/isobmff-file.test.ts +6 -6
- package/src/resources/isobmff-file.ts +8 -9
- package/src/resources/isobmff-track.test.ts +5 -5
- package/src/resources/isobmff-track.ts +5 -8
- package/src/resources/renders.test.ts +5 -5
- package/src/resources/renders.ts +5 -4
- package/src/resources/unprocessed-file.test.ts +99 -15
- package/src/resources/unprocessed-file.ts +69 -47
|
@@ -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
|
-
|
|
5
|
+
md5: z.ZodString;
|
|
6
6
|
filename: z.ZodString;
|
|
7
7
|
byte_size: z.ZodNumber;
|
|
8
8
|
}, "strip", z.ZodTypeAny, {
|
|
9
|
-
|
|
9
|
+
md5: string;
|
|
10
10
|
filename: string;
|
|
11
11
|
byte_size: number;
|
|
12
12
|
}, {
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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
|
-
|
|
5
|
+
md5: z.ZodString;
|
|
6
6
|
filename: z.ZodString;
|
|
7
7
|
}, "strip", z.ZodTypeAny, {
|
|
8
|
-
|
|
8
|
+
md5: string;
|
|
9
9
|
filename: string;
|
|
10
10
|
}, {
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
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/
|
|
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
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
11
|
+
md5: string;
|
|
12
12
|
filename: string;
|
|
13
13
|
byte_size: number;
|
|
14
14
|
processes?: ("captions" | "image" | "isobmff")[] | undefined;
|
|
15
15
|
}, {
|
|
16
|
-
|
|
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
|
-
|
|
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
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
await createUnprocessedFile(client, {
|
|
90
|
-
id: fileId,
|
|
91
|
-
processes: [],
|
|
111
|
+
const md5 = md5Buffer(buffer);
|
|
112
|
+
return await processResource(
|
|
113
|
+
client,
|
|
92
114
|
filename,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
readStream
|
|
98
|
-
|
|
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
|
|
113
|
-
|
|
114
|
-
await
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
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/
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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/
|
|
65
|
+
http.post("http://localhost/api/v1/image_files", () =>
|
|
66
66
|
HttpResponse.json(
|
|
67
|
-
{
|
|
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
|
-
|
|
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({
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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
|
-
|
|
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
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
-
|
|
105
|
+
md5: "test-md5",
|
|
106
106
|
fps: 30,
|
|
107
107
|
width: 1920,
|
|
108
108
|
height: 1080,
|
package/src/resources/renders.ts
CHANGED
|
@@ -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
|
-
|
|
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/
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
const unprocessedFile = await createUnprocessedFile(client, {
|
|
148
|
+
md5: md5,
|
|
149
|
+
processes: [],
|
|
150
|
+
filename,
|
|
151
|
+
byte_size: byteSize,
|
|
152
|
+
});
|
|
147
153
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
readStream.push(null);
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
+
if (unprocessedFile.complete === false) {
|
|
155
|
+
await doUpload(unprocessedFile.id);
|
|
156
|
+
}
|
|
154
157
|
|
|
155
|
-
|
|
158
|
+
if (unprocessedFile.processes.includes(processor)) {
|
|
159
|
+
log("File already processed", unprocessedFile);
|
|
160
|
+
return unprocessedFile;
|
|
161
|
+
}
|
|
156
162
|
|
|
157
|
-
|
|
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
|
-
|
|
162
|
-
|
|
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
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
await uploadUnprocessedFile(
|
|
205
|
+
return await processResource(
|
|
182
206
|
client,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
|