@editframe/api 0.8.0-beta.4 → 0.8.0-beta.7
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/CHUNK_SIZE_BYTES.d.ts +1 -0
- package/dist/CHUNK_SIZE_BYTES.js +7 -0
- package/dist/client.d.ts +0 -2
- package/dist/client.js +2 -14
- package/dist/client.test.d.ts +1 -0
- package/dist/readableFromBuffers.d.ts +2 -0
- package/dist/resources/caption-file.d.ts +22 -3
- package/dist/resources/caption-file.js +28 -26
- package/dist/resources/caption-file.test.d.ts +1 -0
- package/dist/resources/image-file.d.ts +5 -3
- package/dist/resources/image-file.js +23 -27
- package/dist/resources/image-file.test.d.ts +1 -0
- package/dist/resources/isobmff-file.d.ts +2 -3
- package/dist/resources/isobmff-file.js +17 -23
- package/dist/resources/isobmff-file.test.d.ts +1 -0
- package/dist/resources/isobmff-track.d.ts +27 -28
- package/dist/resources/isobmff-track.js +18 -31
- package/dist/resources/isobmff-track.test.d.ts +1 -0
- package/dist/resources/renders.d.ts +4 -5
- package/dist/resources/renders.js +17 -21
- package/dist/resources/renders.test.d.ts +1 -0
- package/dist/resources/unprocessed-file.d.ts +14 -12
- package/dist/resources/unprocessed-file.js +36 -45
- package/dist/resources/unprocessed-file.test.d.ts +1 -0
- package/dist/resources/url-token.d.ts +0 -1
- package/dist/resources/url-token.test.d.ts +1 -0
- package/dist/streamChunker.d.ts +2 -0
- package/dist/streamChunker.js +17 -0
- package/dist/streamChunker.test.d.ts +1 -0
- package/dist/uploadChunks.d.ts +10 -0
- package/dist/uploadChunks.js +65 -0
- package/dist/uploadChunks.test.d.ts +1 -0
- package/package.json +4 -3
- package/src/resources/caption-file.test.ts +124 -0
- package/src/resources/caption-file.ts +49 -24
- package/src/resources/image-file.test.ts +138 -0
- package/src/resources/image-file.ts +28 -25
- package/src/resources/isobmff-file.test.ts +108 -0
- package/src/resources/isobmff-file.ts +19 -22
- package/src/resources/isobmff-track.test.ts +152 -0
- package/src/resources/isobmff-track.ts +22 -31
- package/src/resources/renders.test.ts +112 -0
- package/src/resources/renders.ts +19 -20
- package/src/resources/test-av-file.txt +1 -0
- package/src/resources/unprocessed-file.test.ts +312 -0
- package/src/resources/unprocessed-file.ts +41 -44
- package/src/resources/url-token.test.ts +46 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Readable } from "node:stream";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import { createReadStream } from "node:fs";
|
|
4
|
+
import { stat } from "node:fs/promises";
|
|
4
5
|
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import debug from "debug";
|
|
@@ -8,6 +9,7 @@ import debug from "debug";
|
|
|
8
9
|
import { md5FilePath, md5Buffer } from "@editframe/assets";
|
|
9
10
|
|
|
10
11
|
import type { Client } from "../client.ts";
|
|
12
|
+
import { uploadChunks } from "../uploadChunks.ts";
|
|
11
13
|
|
|
12
14
|
const log = debug("ef:api:unprocessed-file");
|
|
13
15
|
|
|
@@ -22,10 +24,13 @@ const FileProcessors = z
|
|
|
22
24
|
},
|
|
23
25
|
);
|
|
24
26
|
|
|
27
|
+
const MAX_FILE_SIZE = 1024 * 1024 * 1024; // 1GiB
|
|
28
|
+
|
|
25
29
|
export const CreateUnprocessedFilePayload = z.object({
|
|
26
30
|
id: z.string(),
|
|
27
31
|
filename: z.string(),
|
|
28
32
|
processes: FileProcessors.optional(),
|
|
33
|
+
byte_size: z.number().int().max(MAX_FILE_SIZE),
|
|
29
34
|
});
|
|
30
35
|
|
|
31
36
|
export const UpdateUnprocessedFilePayload = z.object({
|
|
@@ -34,14 +39,14 @@ export const UpdateUnprocessedFilePayload = z.object({
|
|
|
34
39
|
|
|
35
40
|
export interface CreateUnprocessedFileResult {
|
|
36
41
|
byte_size: number;
|
|
37
|
-
|
|
42
|
+
next_byte: number;
|
|
38
43
|
id: string;
|
|
39
44
|
processes: z.infer<typeof FileProcessors>;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
export interface UpdateUnprocessedFileResult {
|
|
43
|
-
byte_size
|
|
44
|
-
|
|
48
|
+
byte_size?: number;
|
|
49
|
+
next_byte: number;
|
|
45
50
|
id: string;
|
|
46
51
|
processes: z.infer<typeof FileProcessors>;
|
|
47
52
|
}
|
|
@@ -51,6 +56,7 @@ export const createUnprocessedFile = async (
|
|
|
51
56
|
payload: z.infer<typeof CreateUnprocessedFilePayload>,
|
|
52
57
|
) => {
|
|
53
58
|
log("Creating an unprocessed file", payload);
|
|
59
|
+
CreateUnprocessedFilePayload.parse(payload);
|
|
54
60
|
const response = await client.authenticatedFetch(
|
|
55
61
|
"/api/v1/unprocessed_files",
|
|
56
62
|
{
|
|
@@ -66,18 +72,13 @@ export const createUnprocessedFile = async (
|
|
|
66
72
|
response.headers,
|
|
67
73
|
);
|
|
68
74
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return (await response.json()) as CreateUnprocessedFileResult;
|
|
72
|
-
}
|
|
73
|
-
default: {
|
|
74
|
-
console.error(
|
|
75
|
-
`Failed to create file ${response.status} ${response.statusText}`,
|
|
76
|
-
);
|
|
77
|
-
console.error(await response.text());
|
|
78
|
-
throw new Error("Failed to create unprocessed file");
|
|
79
|
-
}
|
|
75
|
+
if (response.ok) {
|
|
76
|
+
return (await response.json()) as CreateUnprocessedFileResult;
|
|
80
77
|
}
|
|
78
|
+
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Failed to create unprocessed file ${response.status} ${response.statusText}`,
|
|
81
|
+
);
|
|
81
82
|
};
|
|
82
83
|
|
|
83
84
|
export const updateUnprocessedFile = async (
|
|
@@ -86,6 +87,7 @@ export const updateUnprocessedFile = async (
|
|
|
86
87
|
payload: Partial<z.infer<typeof UpdateUnprocessedFilePayload>>,
|
|
87
88
|
) => {
|
|
88
89
|
log("Updating unprocessed file", fileId, payload);
|
|
90
|
+
UpdateUnprocessedFilePayload.parse(payload);
|
|
89
91
|
const response = await client.authenticatedFetch(
|
|
90
92
|
`/api/v1/unprocessed_files/${fileId}`,
|
|
91
93
|
{
|
|
@@ -96,44 +98,30 @@ export const updateUnprocessedFile = async (
|
|
|
96
98
|
|
|
97
99
|
log("Unprocessed file updated", response);
|
|
98
100
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return (await response.json()) as UpdateUnprocessedFileResult;
|
|
102
|
-
}
|
|
103
|
-
default: {
|
|
104
|
-
console.error(
|
|
105
|
-
`Failed to update file ${response.status} ${response.statusText}`,
|
|
106
|
-
);
|
|
107
|
-
throw new Error("Failed to update unprocessed file");
|
|
108
|
-
}
|
|
101
|
+
if (response.ok) {
|
|
102
|
+
return (await response.json()) as UpdateUnprocessedFileResult;
|
|
109
103
|
}
|
|
104
|
+
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Failed to update unprocessed file ${response.status} ${response.statusText}`,
|
|
107
|
+
);
|
|
110
108
|
};
|
|
111
109
|
|
|
112
110
|
export const uploadUnprocessedFile = async (
|
|
113
111
|
client: Client,
|
|
114
112
|
fileId: string,
|
|
115
113
|
fileStream: Readable,
|
|
114
|
+
fileSize: number,
|
|
116
115
|
) => {
|
|
117
116
|
log("Uploading unprocessed file", fileId);
|
|
118
|
-
const unprocessedFile = await client.authenticatedFetch(
|
|
119
|
-
`/api/v1/unprocessed_files/${fileId}/upload`,
|
|
120
|
-
{
|
|
121
|
-
method: "POST",
|
|
122
|
-
body: fileStream,
|
|
123
|
-
},
|
|
124
|
-
);
|
|
125
117
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
console.error(unprocessedFile.status, unprocessedFile.statusText);
|
|
134
|
-
throw new Error("Failed to upload unprocessed file");
|
|
135
|
-
}
|
|
136
|
-
}
|
|
118
|
+
await uploadChunks(client, {
|
|
119
|
+
url: `/api/v1/unprocessed_files/${fileId}/upload`,
|
|
120
|
+
fileSize,
|
|
121
|
+
fileStream,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
log("Unprocessed file upload complete");
|
|
137
125
|
};
|
|
138
126
|
|
|
139
127
|
export const processAVFileBuffer = async (
|
|
@@ -145,10 +133,13 @@ export const processAVFileBuffer = async (
|
|
|
145
133
|
const fileId = md5Buffer(buffer);
|
|
146
134
|
|
|
147
135
|
log("File ID", fileId);
|
|
136
|
+
log(`File size: ${buffer.byteLength} bytes`);
|
|
137
|
+
|
|
148
138
|
await createUnprocessedFile(client, {
|
|
149
139
|
id: fileId,
|
|
150
140
|
processes: [],
|
|
151
141
|
filename,
|
|
142
|
+
byte_size: buffer.byteLength,
|
|
152
143
|
});
|
|
153
144
|
|
|
154
145
|
const readStream = new Readable({
|
|
@@ -158,7 +149,7 @@ export const processAVFileBuffer = async (
|
|
|
158
149
|
},
|
|
159
150
|
});
|
|
160
151
|
|
|
161
|
-
await uploadUnprocessedFile(client, fileId, readStream);
|
|
152
|
+
await uploadUnprocessedFile(client, fileId, readStream, buffer.byteLength);
|
|
162
153
|
|
|
163
154
|
const fileInformation = await updateUnprocessedFile(client, fileId, {
|
|
164
155
|
processes: ["isobmff"],
|
|
@@ -177,11 +168,17 @@ export const processAVFile = async (client: Client, filePath: string) => {
|
|
|
177
168
|
id: fileId,
|
|
178
169
|
processes: [],
|
|
179
170
|
filename: basename(filePath),
|
|
171
|
+
byte_size: (await stat(filePath)).size,
|
|
180
172
|
});
|
|
181
173
|
|
|
182
174
|
const readStream = createReadStream(filePath);
|
|
183
175
|
|
|
184
|
-
await uploadUnprocessedFile(
|
|
176
|
+
await uploadUnprocessedFile(
|
|
177
|
+
client,
|
|
178
|
+
fileId,
|
|
179
|
+
readStream,
|
|
180
|
+
(await stat(filePath)).size,
|
|
181
|
+
);
|
|
185
182
|
|
|
186
183
|
const fileInformation = await updateUnprocessedFile(client, fileId, {
|
|
187
184
|
processes: ["isobmff"],
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
|
|
2
|
+
import { http, HttpResponse } from "msw";
|
|
3
|
+
import { setupServer } from "msw/node";
|
|
4
|
+
|
|
5
|
+
import { Client } from "../client.ts";
|
|
6
|
+
import { createURLToken } from "./url-token.ts";
|
|
7
|
+
|
|
8
|
+
const server = setupServer();
|
|
9
|
+
const client = new Client("ef_TEST_TOKEN", "http://localhost");
|
|
10
|
+
|
|
11
|
+
describe("URL Token", () => {
|
|
12
|
+
beforeAll(() => server.listen());
|
|
13
|
+
afterEach(() => server.resetHandlers());
|
|
14
|
+
afterAll(() => server.close());
|
|
15
|
+
|
|
16
|
+
describe("createURLToken", () => {
|
|
17
|
+
test("Throws when server returns an error", async () => {
|
|
18
|
+
server.use(
|
|
19
|
+
http.post("http://localhost/api/v1/url-token", () =>
|
|
20
|
+
HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
await expect(
|
|
25
|
+
createURLToken(client, "http://example.com"),
|
|
26
|
+
).rejects.toThrowError(
|
|
27
|
+
"Failed to create signed url: 500 Internal Server Error Internal Server Error",
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("Returns token from the http response", async () => {
|
|
32
|
+
server.use(
|
|
33
|
+
http.post("http://localhost/api/v1/url-token", () =>
|
|
34
|
+
HttpResponse.json(
|
|
35
|
+
{ token: "test-token" },
|
|
36
|
+
{ status: 200, statusText: "OK" },
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
await expect(createURLToken(client, "http://example.com")).resolves.toBe(
|
|
42
|
+
"test-token",
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|