@editframe/api 0.8.0-beta.1 → 0.8.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/index.d.ts +1 -1
- package/dist/index.js +2 -2
- 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 +5 -0
- package/dist/resources/{signed-url.js → url-token.js} +5 -5
- 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
- package/src/resources/{signed-url.ts → url-token.ts} +6 -6
- package/dist/resources/signed-url.d.ts +0 -6
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { test, expect, beforeAll, afterEach, afterAll, describe } from "vitest";
|
|
4
|
+
import { http, HttpResponse } from "msw";
|
|
5
|
+
import { setupServer } from "msw/node";
|
|
6
|
+
import { ZodError } from "zod";
|
|
7
|
+
|
|
8
|
+
import { Client } from "../client.ts";
|
|
9
|
+
import {
|
|
10
|
+
createUnprocessedFile,
|
|
11
|
+
processAVFile,
|
|
12
|
+
processAVFileBuffer,
|
|
13
|
+
updateUnprocessedFile,
|
|
14
|
+
uploadUnprocessedFile,
|
|
15
|
+
} from "./unprocessed-file.ts";
|
|
16
|
+
import { readableFromBuffers } from "../readableFromBuffers.ts";
|
|
17
|
+
|
|
18
|
+
const server = setupServer();
|
|
19
|
+
const client = new Client("ef_TEST_TOKEN", "http://localhost");
|
|
20
|
+
|
|
21
|
+
const TEST_AV_FILE = join(__dirname, "test-av-file.txt");
|
|
22
|
+
|
|
23
|
+
describe("Unprocessed File", () => {
|
|
24
|
+
beforeAll(() => server.listen());
|
|
25
|
+
afterEach(() => server.resetHandlers());
|
|
26
|
+
afterAll(() => server.close());
|
|
27
|
+
|
|
28
|
+
describe("createUnprocessedFile", () => {
|
|
29
|
+
test("Throws when file is too large", async () => {
|
|
30
|
+
await expect(
|
|
31
|
+
createUnprocessedFile(client, {
|
|
32
|
+
id: "test-file",
|
|
33
|
+
filename: "test-file",
|
|
34
|
+
processes: [],
|
|
35
|
+
byte_size: 1024 * 1024 * 1025,
|
|
36
|
+
}),
|
|
37
|
+
).rejects.toThrowError(
|
|
38
|
+
new ZodError([
|
|
39
|
+
{
|
|
40
|
+
code: "too_big",
|
|
41
|
+
maximum: 1073741824,
|
|
42
|
+
type: "number",
|
|
43
|
+
inclusive: true,
|
|
44
|
+
exact: false,
|
|
45
|
+
message: "Number must be less than or equal to 1073741824",
|
|
46
|
+
path: ["byte_size"],
|
|
47
|
+
},
|
|
48
|
+
]),
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("Throws when server returns an error", async () => {
|
|
53
|
+
server.use(
|
|
54
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
55
|
+
HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
await expect(
|
|
60
|
+
createUnprocessedFile(client, {
|
|
61
|
+
id: "test-file",
|
|
62
|
+
filename: "test-file",
|
|
63
|
+
processes: [],
|
|
64
|
+
byte_size: 1024 * 1024,
|
|
65
|
+
}),
|
|
66
|
+
).rejects.toThrowError(
|
|
67
|
+
"Failed to create unprocessed file 500 Internal Server Error",
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("Returns json data from the http response", async () => {
|
|
72
|
+
server.use(
|
|
73
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
74
|
+
HttpResponse.json(
|
|
75
|
+
{ testResponse: "test" },
|
|
76
|
+
{ status: 200, statusText: "OK" },
|
|
77
|
+
),
|
|
78
|
+
),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const result = await createUnprocessedFile(client, {
|
|
82
|
+
id: "test-file",
|
|
83
|
+
filename: "test-file",
|
|
84
|
+
processes: [],
|
|
85
|
+
byte_size: 1024 * 1024,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result).toEqual({ testResponse: "test" });
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("updateUnprocessedFile", () => {
|
|
93
|
+
test("Throws when server responds with an error", async () => {
|
|
94
|
+
server.use(
|
|
95
|
+
http.post("http://localhost/api/v1/unprocessed_files/test-file", () =>
|
|
96
|
+
HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
97
|
+
),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
await expect(
|
|
101
|
+
updateUnprocessedFile(client, "test-file", {
|
|
102
|
+
processes: [],
|
|
103
|
+
}),
|
|
104
|
+
).rejects.toThrowError(
|
|
105
|
+
"Failed to update unprocessed file 500 Internal Server Error",
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("Returns json data from the http response", async () => {
|
|
110
|
+
server.use(
|
|
111
|
+
http.post("http://localhost/api/v1/unprocessed_files/test-file", () =>
|
|
112
|
+
HttpResponse.json(
|
|
113
|
+
{ testResponse: "test" },
|
|
114
|
+
{ status: 200, statusText: "OK" },
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const result = await updateUnprocessedFile(client, "test-file", {
|
|
120
|
+
processes: [],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(result).toEqual({ testResponse: "test" });
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("uploadUnprocessedFile", () => {
|
|
128
|
+
test("Throws when server responds with an error", async () => {
|
|
129
|
+
server.use(
|
|
130
|
+
http.post(
|
|
131
|
+
"http://localhost/api/v1/unprocessed_files/test-file/upload",
|
|
132
|
+
() => HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
await expect(
|
|
137
|
+
uploadUnprocessedFile(
|
|
138
|
+
client,
|
|
139
|
+
"test-file",
|
|
140
|
+
readableFromBuffers(Buffer.from("test")),
|
|
141
|
+
4,
|
|
142
|
+
),
|
|
143
|
+
).rejects.toThrowError(
|
|
144
|
+
"Failed to upload chunk 0 for /api/v1/unprocessed_files/test-file/upload 500 Internal Server Error",
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("Succeeds when server returns a success", async () => {
|
|
149
|
+
server.use(
|
|
150
|
+
http.post(
|
|
151
|
+
"http://localhost/api/v1/unprocessed_files/test-file/upload",
|
|
152
|
+
() => HttpResponse.json({}, { status: 201 }),
|
|
153
|
+
),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
await expect(
|
|
157
|
+
uploadUnprocessedFile(
|
|
158
|
+
client,
|
|
159
|
+
"test-file",
|
|
160
|
+
readableFromBuffers(Buffer.from("test")),
|
|
161
|
+
4,
|
|
162
|
+
),
|
|
163
|
+
).resolves.toBeUndefined();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("processAVFileBuffer", () => {
|
|
168
|
+
test("Throws when server responds with an error when creating file", async () => {
|
|
169
|
+
server.use(
|
|
170
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
171
|
+
HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
172
|
+
),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
await expect(
|
|
176
|
+
processAVFileBuffer(client, Buffer.from("test"), "test-file"),
|
|
177
|
+
).rejects.toThrowError(
|
|
178
|
+
"Failed to create unprocessed file 500 Internal Server Error",
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("Throws when server responds with an error when uploading file", async () => {
|
|
183
|
+
server.use(
|
|
184
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
185
|
+
HttpResponse.json({}, { status: 200 }),
|
|
186
|
+
),
|
|
187
|
+
http.post(
|
|
188
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
|
|
189
|
+
() => HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
190
|
+
),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
await expect(
|
|
194
|
+
processAVFileBuffer(client, Buffer.from("test"), "test-file"),
|
|
195
|
+
).rejects.toThrowError(
|
|
196
|
+
"Failed to upload chunk 0 for /api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload 500 Internal Server Error",
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("Throws when server responds with an error when updating file", async () => {
|
|
201
|
+
server.use(
|
|
202
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
203
|
+
HttpResponse.json({}, { status: 200 }),
|
|
204
|
+
),
|
|
205
|
+
http.post(
|
|
206
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
|
|
207
|
+
() => HttpResponse.json({ test }, { status: 201 }),
|
|
208
|
+
),
|
|
209
|
+
http.post(
|
|
210
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6",
|
|
211
|
+
() => HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
212
|
+
),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
await expect(
|
|
216
|
+
processAVFileBuffer(client, Buffer.from("test"), "test-file"),
|
|
217
|
+
).rejects.toThrowError(
|
|
218
|
+
"Failed to update unprocessed file 500 Internal Server Error",
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("Returns json data when upload is successful", async () => {
|
|
223
|
+
server.use(
|
|
224
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
225
|
+
HttpResponse.json({}, { status: 200 }),
|
|
226
|
+
),
|
|
227
|
+
http.post(
|
|
228
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
|
|
229
|
+
() => HttpResponse.json({}, { status: 201 }),
|
|
230
|
+
),
|
|
231
|
+
http.post(
|
|
232
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6",
|
|
233
|
+
() => HttpResponse.json({ test: "response" }, { status: 200 }),
|
|
234
|
+
),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
await expect(
|
|
238
|
+
processAVFileBuffer(client, Buffer.from("test"), "test-file"),
|
|
239
|
+
).resolves.toEqual({ test: "response" });
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("processAVFile", () => {
|
|
244
|
+
test("Throws when server responds with an error when creating file", async () => {
|
|
245
|
+
server.use(
|
|
246
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
247
|
+
HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
248
|
+
),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
await expect(processAVFile(client, TEST_AV_FILE)).rejects.toThrowError(
|
|
252
|
+
"Failed to create unprocessed file 500 Internal Server Error",
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("Throws when server responds with an error when uploading file", async () => {
|
|
257
|
+
server.use(
|
|
258
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
259
|
+
HttpResponse.json({}, { status: 200 }),
|
|
260
|
+
),
|
|
261
|
+
http.post(
|
|
262
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
|
|
263
|
+
() => HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
264
|
+
),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
await expect(processAVFile(client, TEST_AV_FILE)).rejects.toThrowError(
|
|
268
|
+
"Failed to upload chunk 0 for /api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload 500 Internal Server Error",
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("Throws when server responds with an error when updating file", async () => {
|
|
273
|
+
server.use(
|
|
274
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
275
|
+
HttpResponse.json({}, { status: 200 }),
|
|
276
|
+
),
|
|
277
|
+
http.post(
|
|
278
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
|
|
279
|
+
() => HttpResponse.json({ test }, { status: 201 }),
|
|
280
|
+
),
|
|
281
|
+
http.post(
|
|
282
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6",
|
|
283
|
+
() => HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
284
|
+
),
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
await expect(processAVFile(client, TEST_AV_FILE)).rejects.toThrowError(
|
|
288
|
+
"Failed to update unprocessed file 500 Internal Server Error",
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("Returns json data when upload is successful", async () => {
|
|
293
|
+
server.use(
|
|
294
|
+
http.post("http://localhost/api/v1/unprocessed_files", () =>
|
|
295
|
+
HttpResponse.json({}, { status: 200 }),
|
|
296
|
+
),
|
|
297
|
+
http.post(
|
|
298
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6/upload",
|
|
299
|
+
() => HttpResponse.json({}, { status: 201 }),
|
|
300
|
+
),
|
|
301
|
+
http.post(
|
|
302
|
+
"http://localhost/api/v1/unprocessed_files/098f6bcd-4621-d373-cade-4e832627b4f6",
|
|
303
|
+
() => HttpResponse.json({ test: "response" }, { status: 200 }),
|
|
304
|
+
),
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
await expect(processAVFile(client, TEST_AV_FILE)).resolves.toEqual({
|
|
308
|
+
test: "response",
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -2,15 +2,15 @@ import debug from "debug";
|
|
|
2
2
|
|
|
3
3
|
import type { Client } from "../client.ts";
|
|
4
4
|
|
|
5
|
-
const log = debug("ef:api:
|
|
5
|
+
const log = debug("ef:api:url-token");
|
|
6
6
|
|
|
7
|
-
export interface
|
|
8
|
-
|
|
7
|
+
export interface URLTokenResult {
|
|
8
|
+
token: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export const
|
|
11
|
+
export const createURLToken = async (client: Client, url: string) => {
|
|
12
12
|
log("Creating signed url for", url);
|
|
13
|
-
const response = await client.authenticatedFetch("/api/v1/
|
|
13
|
+
const response = await client.authenticatedFetch("/api/v1/url-token", {
|
|
14
14
|
method: "POST",
|
|
15
15
|
body: JSON.stringify({
|
|
16
16
|
url,
|
|
@@ -23,5 +23,5 @@ export const createSignedURL = async (client: Client, url: string) => {
|
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
return ((await response.json()) as
|
|
26
|
+
return ((await response.json()) as URLTokenResult).token;
|
|
27
27
|
};
|