@editframe/api 0.26.3-beta.0 → 0.26.4-beta.0
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/package.json +4 -3
- package/tsdown.config.ts +3 -0
- package/src/resources/caption-file.test.ts +0 -172
- package/src/resources/caption-file.ts +0 -154
- package/src/resources/image-file.test.ts +0 -220
- package/src/resources/image-file.ts +0 -205
- package/src/resources/isobmff-file.test.ts +0 -159
- package/src/resources/isobmff-file.ts +0 -186
- package/src/resources/isobmff-track.test.ts +0 -126
- package/src/resources/isobmff-track.ts +0 -248
- package/src/resources/process-isobmff.test.ts +0 -68
- package/src/resources/process-isobmff.ts +0 -33
- package/src/resources/renders.bundle.ts +0 -53
- package/src/resources/renders.test.ts +0 -202
- package/src/resources/renders.ts +0 -282
- package/src/resources/test-av-file.txt +0 -1
- package/src/resources/transcriptions.test.ts +0 -49
- package/src/resources/transcriptions.ts +0 -67
- package/src/resources/unprocessed-file.test.ts +0 -171
- package/src/resources/unprocessed-file.ts +0 -138
- package/src/resources/url-token.test.ts +0 -46
- package/src/resources/url-token.ts +0 -27
- package/src/utils/assertTypesMatch.ts +0 -10
- package/src/utils/createReadableStreamFromReadable.ts +0 -115
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import debug from "debug";
|
|
2
|
-
import { types } from "mime-types";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
import type { Client } from "../client.js";
|
|
6
|
-
import { uploadChunks } from "../uploadChunks.js";
|
|
7
|
-
|
|
8
|
-
const log = debug("ef:api:image-file");
|
|
9
|
-
|
|
10
|
-
const MAX_IMAGE_SIZE = 1024 * 1024 * 16; // 16MB
|
|
11
|
-
|
|
12
|
-
export const ImageFileMimeTypes = z.enum([
|
|
13
|
-
"image/jpeg",
|
|
14
|
-
"image/png",
|
|
15
|
-
"image/jpg",
|
|
16
|
-
"image/webp",
|
|
17
|
-
"image/svg+xml",
|
|
18
|
-
]);
|
|
19
|
-
|
|
20
|
-
function getFileExtension(path: string) {
|
|
21
|
-
const match = path.match(/\.([^.]+)$/);
|
|
22
|
-
return match ? match[1] : null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const CreateImageFilePayload = z
|
|
26
|
-
.object({
|
|
27
|
-
/**
|
|
28
|
-
* The md5 hash of the image file.
|
|
29
|
-
*/
|
|
30
|
-
md5: z.string().optional(),
|
|
31
|
-
/**
|
|
32
|
-
* The height of the image file in pixels.
|
|
33
|
-
*/
|
|
34
|
-
height: z.number().int().optional(),
|
|
35
|
-
/**
|
|
36
|
-
* The width of the image file in pixels.
|
|
37
|
-
*/
|
|
38
|
-
width: z.number().int().optional(),
|
|
39
|
-
/**
|
|
40
|
-
* The mime type of the image file. Optional if the filename has a known file extension.
|
|
41
|
-
*/
|
|
42
|
-
mime_type: ImageFileMimeTypes.optional(),
|
|
43
|
-
/**
|
|
44
|
-
* The filename of the image file.
|
|
45
|
-
*/
|
|
46
|
-
filename: z.string(),
|
|
47
|
-
/**
|
|
48
|
-
* The byte size of the image file.
|
|
49
|
-
*/
|
|
50
|
-
byte_size: z.number().int().max(MAX_IMAGE_SIZE),
|
|
51
|
-
})
|
|
52
|
-
.superRefine((data, ctx) => {
|
|
53
|
-
const extension = getFileExtension(data.filename);
|
|
54
|
-
const mimeType = extension ? types[extension] : null;
|
|
55
|
-
const parsedMimeType = ImageFileMimeTypes.safeParse(mimeType).data;
|
|
56
|
-
|
|
57
|
-
if (parsedMimeType) {
|
|
58
|
-
data.mime_type = parsedMimeType;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (!parsedMimeType && !data.mime_type) {
|
|
62
|
-
ctx.addIssue({
|
|
63
|
-
code: z.ZodIssueCode.custom,
|
|
64
|
-
message:
|
|
65
|
-
"mime_type is required when filename extension doesn't match a known image type",
|
|
66
|
-
path: ["mime_type"],
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
export type CreateImageFilePayload = z.infer<typeof CreateImageFilePayload>;
|
|
72
|
-
|
|
73
|
-
export interface CreateImageFileResult {
|
|
74
|
-
/**
|
|
75
|
-
* Whether the image file has been fully uploaded.
|
|
76
|
-
*/
|
|
77
|
-
complete: boolean | null;
|
|
78
|
-
/**
|
|
79
|
-
* The byte size of the image file.
|
|
80
|
-
*/
|
|
81
|
-
byte_size: number;
|
|
82
|
-
/**
|
|
83
|
-
* The id of the image file.
|
|
84
|
-
*/
|
|
85
|
-
id: string;
|
|
86
|
-
/**
|
|
87
|
-
* The md5 hash of the image file.
|
|
88
|
-
*/
|
|
89
|
-
md5: string | null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export interface LookupImageFileByMd5Result {
|
|
93
|
-
/**
|
|
94
|
-
* Whether the image file has been fully uploaded.
|
|
95
|
-
*/
|
|
96
|
-
complete: boolean | null;
|
|
97
|
-
/**
|
|
98
|
-
* The byte size of the image file.
|
|
99
|
-
*/
|
|
100
|
-
byte_size: number;
|
|
101
|
-
/**
|
|
102
|
-
* The id of the image file.
|
|
103
|
-
*/
|
|
104
|
-
id: string;
|
|
105
|
-
/**
|
|
106
|
-
* md5 hash of the image file.
|
|
107
|
-
*/
|
|
108
|
-
md5: string | null;
|
|
109
|
-
/**
|
|
110
|
-
* The height of the image file in pixels.
|
|
111
|
-
*/
|
|
112
|
-
height: number | null;
|
|
113
|
-
/**
|
|
114
|
-
* The width of the image file in pixels.
|
|
115
|
-
*/
|
|
116
|
-
width: number | null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export interface GetImageFileMetadataResult
|
|
120
|
-
extends LookupImageFileByMd5Result {}
|
|
121
|
-
|
|
122
|
-
export const createImageFile = async (
|
|
123
|
-
client: Client,
|
|
124
|
-
payload: CreateImageFilePayload,
|
|
125
|
-
) => {
|
|
126
|
-
log("Creating image file", payload);
|
|
127
|
-
CreateImageFilePayload.parse(payload);
|
|
128
|
-
const response = await client.authenticatedFetch("/api/v1/image_files", {
|
|
129
|
-
method: "POST",
|
|
130
|
-
body: JSON.stringify(payload),
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
log("Image file created", response);
|
|
134
|
-
|
|
135
|
-
if (response.ok) {
|
|
136
|
-
return (await response.json()) as CreateImageFileResult;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
throw new Error(
|
|
140
|
-
`Failed to create file ${response.status} ${response.statusText}`,
|
|
141
|
-
);
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
export const uploadImageFile = (
|
|
145
|
-
client: Client,
|
|
146
|
-
uploadDetails: {
|
|
147
|
-
id: string;
|
|
148
|
-
byte_size: number;
|
|
149
|
-
},
|
|
150
|
-
fileStream: ReadableStream,
|
|
151
|
-
chunkSizeBytes?: number,
|
|
152
|
-
) => {
|
|
153
|
-
log("Uploading image file", uploadDetails.id);
|
|
154
|
-
|
|
155
|
-
return uploadChunks(client, {
|
|
156
|
-
url: `/api/v1/image_files/${uploadDetails.id}/upload`,
|
|
157
|
-
fileSize: uploadDetails.byte_size,
|
|
158
|
-
fileStream,
|
|
159
|
-
maxSize: MAX_IMAGE_SIZE,
|
|
160
|
-
chunkSizeBytes,
|
|
161
|
-
});
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
export const getImageFileMetadata = async (
|
|
165
|
-
client: Client,
|
|
166
|
-
id: string,
|
|
167
|
-
): Promise<GetImageFileMetadataResult | null> => {
|
|
168
|
-
const response = await client.authenticatedFetch(
|
|
169
|
-
`/api/v1/image_files/${id}.json`,
|
|
170
|
-
{
|
|
171
|
-
method: "GET",
|
|
172
|
-
},
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
if (response.ok) {
|
|
176
|
-
return (await response.json()) as LookupImageFileByMd5Result;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return null;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
export const lookupImageFileByMd5 = async (
|
|
183
|
-
client: Client,
|
|
184
|
-
md5: string,
|
|
185
|
-
): Promise<LookupImageFileByMd5Result | null> => {
|
|
186
|
-
const response = await client.authenticatedFetch(
|
|
187
|
-
`/api/v1/image_files/md5/${md5}`,
|
|
188
|
-
{
|
|
189
|
-
method: "GET",
|
|
190
|
-
},
|
|
191
|
-
);
|
|
192
|
-
log("Image file lookup", response);
|
|
193
|
-
|
|
194
|
-
if (response.ok) {
|
|
195
|
-
return (await response.json()) as LookupImageFileByMd5Result;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (response.status === 404) {
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
throw new Error(
|
|
203
|
-
`Failed to lookup image by md5 ${md5} ${response.status} ${response.statusText}`,
|
|
204
|
-
);
|
|
205
|
-
};
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { HttpResponse, http } from "msw";
|
|
2
|
-
import { setupServer } from "msw/node";
|
|
3
|
-
import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
|
|
4
|
-
|
|
5
|
-
import { Client } from "../client.js";
|
|
6
|
-
import { webReadableFromBuffers } from "../readableFromBuffers.js";
|
|
7
|
-
import {
|
|
8
|
-
createISOBMFFFile,
|
|
9
|
-
lookupISOBMFFFileByMd5,
|
|
10
|
-
uploadFragmentIndex,
|
|
11
|
-
} from "./isobmff-file.js";
|
|
12
|
-
|
|
13
|
-
const server = setupServer();
|
|
14
|
-
const client = new Client("ef_TEST_TOKEN", "http://localhost");
|
|
15
|
-
|
|
16
|
-
describe("ISOBMFFFile", () => {
|
|
17
|
-
beforeAll(() => server.listen());
|
|
18
|
-
afterEach(() => server.resetHandlers());
|
|
19
|
-
afterAll(() => server.close());
|
|
20
|
-
|
|
21
|
-
describe("createISOBMFFFile", () => {
|
|
22
|
-
test("Throws when server returns an error", async () => {
|
|
23
|
-
server.use(
|
|
24
|
-
http.post("http://localhost/api/v1/isobmff_files", () =>
|
|
25
|
-
HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
26
|
-
),
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
await expect(
|
|
30
|
-
createISOBMFFFile(client, {
|
|
31
|
-
md5: "test-md5",
|
|
32
|
-
filename: "test",
|
|
33
|
-
}),
|
|
34
|
-
).rejects.toThrowError(
|
|
35
|
-
"Failed to create isobmff file 500 Internal Server Error",
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("Returns json data from the http response", async () => {
|
|
40
|
-
server.use(
|
|
41
|
-
http.post("http://localhost/api/v1/isobmff_files", () =>
|
|
42
|
-
HttpResponse.json(
|
|
43
|
-
{ id: "test-id" },
|
|
44
|
-
{ status: 200, statusText: "OK" },
|
|
45
|
-
),
|
|
46
|
-
),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const response = await createISOBMFFFile(client, {
|
|
50
|
-
md5: "test-md5",
|
|
51
|
-
filename: "test",
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
expect(response).toEqual({ id: "test-id" });
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe("uploadFragmentIndex", () => {
|
|
59
|
-
test("Throws when file size exceeds limit", async () => {
|
|
60
|
-
await expect(
|
|
61
|
-
uploadFragmentIndex(
|
|
62
|
-
client,
|
|
63
|
-
"test-id",
|
|
64
|
-
webReadableFromBuffers(Buffer.from("test")),
|
|
65
|
-
1024 * 1024 * 3,
|
|
66
|
-
),
|
|
67
|
-
).rejects.toThrowError("File size exceeds limit of 2097152 bytes");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("Throws when server returns an error", async () => {
|
|
71
|
-
server.use(
|
|
72
|
-
http.post(
|
|
73
|
-
"http://localhost/api/v1/isobmff_files/test-id/index/upload",
|
|
74
|
-
() => HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
75
|
-
),
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
await expect(
|
|
79
|
-
uploadFragmentIndex(
|
|
80
|
-
client,
|
|
81
|
-
"test-id",
|
|
82
|
-
webReadableFromBuffers(Buffer.from("test")),
|
|
83
|
-
4,
|
|
84
|
-
),
|
|
85
|
-
).rejects.toThrowError(
|
|
86
|
-
"Failed to create fragment index 500 Internal Server Error",
|
|
87
|
-
);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("Returns json data from the http response", async () => {
|
|
91
|
-
server.use(
|
|
92
|
-
http.post(
|
|
93
|
-
"http://localhost/api/v1/isobmff_files/test-id/index/upload",
|
|
94
|
-
() =>
|
|
95
|
-
HttpResponse.json(
|
|
96
|
-
{ fragment_index_complete: true },
|
|
97
|
-
{ status: 200, statusText: "OK" },
|
|
98
|
-
),
|
|
99
|
-
),
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const response = await uploadFragmentIndex(
|
|
103
|
-
client,
|
|
104
|
-
"test-id",
|
|
105
|
-
webReadableFromBuffers(Buffer.from("test")),
|
|
106
|
-
4,
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
expect(response).toEqual({ fragment_index_complete: true });
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
describe("lookupISOBMFFFileByMd5", () => {
|
|
114
|
-
test("Returns json data from the http response", async () => {
|
|
115
|
-
server.use(
|
|
116
|
-
http.get("http://localhost/api/v1/isobmff_files/md5/test-md5", () =>
|
|
117
|
-
HttpResponse.json(
|
|
118
|
-
{ id: "test-id", md5: "test-md5", fragment_index_complete: true },
|
|
119
|
-
{ status: 200, statusText: "OK" },
|
|
120
|
-
),
|
|
121
|
-
),
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const response = await lookupISOBMFFFileByMd5(client, "test-md5");
|
|
125
|
-
|
|
126
|
-
expect(response).toEqual({
|
|
127
|
-
id: "test-id",
|
|
128
|
-
md5: "test-md5",
|
|
129
|
-
fragment_index_complete: true,
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test("Returns null when file is not found", async () => {
|
|
134
|
-
server.use(
|
|
135
|
-
http.get("http://localhost/api/v1/isobmff_files/md5/test-md5", () =>
|
|
136
|
-
HttpResponse.json({}, { status: 404 }),
|
|
137
|
-
),
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
const response = await lookupISOBMFFFileByMd5(client, "test-md5");
|
|
141
|
-
|
|
142
|
-
expect(response).toBeNull();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("Throws when server returns an error", async () => {
|
|
146
|
-
server.use(
|
|
147
|
-
http.get("http://localhost/api/v1/isobmff_files/md5/test-md5", () =>
|
|
148
|
-
HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
149
|
-
),
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
await expect(
|
|
153
|
-
lookupISOBMFFFileByMd5(client, "test-md5"),
|
|
154
|
-
).rejects.toThrowError(
|
|
155
|
-
"Failed to lookup isobmff file by md5 test-md5 500 Internal Server Error",
|
|
156
|
-
);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
});
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import debug from "debug";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
|
-
import type { Client } from "../client.js";
|
|
5
|
-
|
|
6
|
-
const log = debug("ef:api:isobmff-file");
|
|
7
|
-
const FILE_SIZE_LIMIT = 1024 * 1024 * 2; // 32MB
|
|
8
|
-
|
|
9
|
-
export const CreateISOBMFFFilePayload = z.object({
|
|
10
|
-
md5: z.string(),
|
|
11
|
-
filename: z.string(),
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export type CreateISOBMFFFilePayload = z.infer<typeof CreateISOBMFFFilePayload>;
|
|
15
|
-
|
|
16
|
-
export interface CreateISOBMFFFileResult {
|
|
17
|
-
/**
|
|
18
|
-
* Whether the fragment index is complete. The fragment index is used internally by editframe to efficiently seek within files.
|
|
19
|
-
*/
|
|
20
|
-
fragment_index_complete: boolean;
|
|
21
|
-
/**
|
|
22
|
-
* The filename of the isobmff file
|
|
23
|
-
*/
|
|
24
|
-
filename: string;
|
|
25
|
-
/**
|
|
26
|
-
* The id of the isobmff file
|
|
27
|
-
*/
|
|
28
|
-
id: string;
|
|
29
|
-
/**
|
|
30
|
-
* The md5 hash of the isobmff file
|
|
31
|
-
*/
|
|
32
|
-
md5: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface LookupISOBMFFFileByMd5Result {
|
|
36
|
-
/**
|
|
37
|
-
* Whether the fragment index is complete
|
|
38
|
-
*/
|
|
39
|
-
fragment_index_complete: boolean;
|
|
40
|
-
/**
|
|
41
|
-
* The filename of the isobmff file
|
|
42
|
-
*/
|
|
43
|
-
filename: string;
|
|
44
|
-
id: string;
|
|
45
|
-
md5: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface GetISOBMFFFileTranscriptionResult {
|
|
49
|
-
id: string;
|
|
50
|
-
work_slice_ms: number;
|
|
51
|
-
isobmff_track: {
|
|
52
|
-
duration_ms: number;
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export const createISOBMFFFile = async (
|
|
57
|
-
client: Client,
|
|
58
|
-
payload: CreateISOBMFFFilePayload,
|
|
59
|
-
) => {
|
|
60
|
-
log("Creating isobmff file", payload);
|
|
61
|
-
const response = await client.authenticatedFetch("/api/v1/isobmff_files", {
|
|
62
|
-
method: "POST",
|
|
63
|
-
body: JSON.stringify(payload),
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
log("ISOBMFF file created", response);
|
|
67
|
-
|
|
68
|
-
if (response.ok) {
|
|
69
|
-
return (await response.json()) as CreateISOBMFFFileResult;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
throw new Error(
|
|
73
|
-
`Failed to create isobmff file ${response.status} ${response.statusText}`,
|
|
74
|
-
);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const uploadFragmentIndex = async (
|
|
78
|
-
client: Client,
|
|
79
|
-
fileId: string,
|
|
80
|
-
fileStream: ReadableStream,
|
|
81
|
-
fileSize: number,
|
|
82
|
-
) => {
|
|
83
|
-
log("Uploading fragment index", fileId);
|
|
84
|
-
if (fileSize > FILE_SIZE_LIMIT) {
|
|
85
|
-
throw new Error(`File size exceeds limit of ${FILE_SIZE_LIMIT} bytes`);
|
|
86
|
-
}
|
|
87
|
-
const response = await client.authenticatedFetch(
|
|
88
|
-
`/api/v1/isobmff_files/${fileId}/index/upload`,
|
|
89
|
-
{
|
|
90
|
-
method: "POST",
|
|
91
|
-
body: fileStream,
|
|
92
|
-
duplex: "half",
|
|
93
|
-
},
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
log("Fragment index uploaded", response);
|
|
97
|
-
if (response.ok) {
|
|
98
|
-
return response.json();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
throw new Error(
|
|
102
|
-
`Failed to create fragment index ${response.status} ${response.statusText}`,
|
|
103
|
-
);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
export const lookupISOBMFFFileByMd5 = async (
|
|
107
|
-
client: Client,
|
|
108
|
-
md5: string,
|
|
109
|
-
): Promise<LookupISOBMFFFileByMd5Result | null> => {
|
|
110
|
-
const response = await client.authenticatedFetch(
|
|
111
|
-
`/api/v1/isobmff_files/md5/${md5}`,
|
|
112
|
-
{
|
|
113
|
-
method: "GET",
|
|
114
|
-
},
|
|
115
|
-
);
|
|
116
|
-
log("ISOBMFF file lookup", response);
|
|
117
|
-
|
|
118
|
-
if (response.ok) {
|
|
119
|
-
return (await response.json()) as LookupISOBMFFFileByMd5Result;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (response.status === 404) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
throw new Error(
|
|
127
|
-
`Failed to lookup isobmff file by md5 ${md5} ${response.status} ${response.statusText}`,
|
|
128
|
-
);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
export const getISOBMFFFileTranscription = async (
|
|
132
|
-
client: Client,
|
|
133
|
-
id: string,
|
|
134
|
-
): Promise<GetISOBMFFFileTranscriptionResult | null> => {
|
|
135
|
-
const response = await client.authenticatedFetch(
|
|
136
|
-
`/api/v1/isobmff_files/${id}/transcription`,
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
if (response.ok) {
|
|
140
|
-
return (await response.json()) as GetISOBMFFFileTranscriptionResult;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (response.status === 404) {
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
throw new Error(
|
|
148
|
-
`Failed to get isobmff file transcription ${id} ${response.status} ${response.statusText}`,
|
|
149
|
-
);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
export const TranscribeISOBMFFFilePayload = z.object({
|
|
153
|
-
trackId: z.string().optional(),
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
export type TranscribeISOBMFFFilePayload = z.infer<
|
|
157
|
-
typeof TranscribeISOBMFFFilePayload
|
|
158
|
-
>;
|
|
159
|
-
|
|
160
|
-
export interface TranscribeISOBMFFFileResult {
|
|
161
|
-
id: string;
|
|
162
|
-
file_id: string;
|
|
163
|
-
track_id: number;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export const transcribeISOBMFFFile = async (
|
|
167
|
-
client: Client,
|
|
168
|
-
id: string,
|
|
169
|
-
payload: TranscribeISOBMFFFilePayload = {},
|
|
170
|
-
) => {
|
|
171
|
-
const response = await client.authenticatedFetch(
|
|
172
|
-
`/api/v1/isobmff_files/${id}/transcribe`,
|
|
173
|
-
{
|
|
174
|
-
method: "POST",
|
|
175
|
-
body: JSON.stringify(payload),
|
|
176
|
-
},
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
if (response.ok) {
|
|
180
|
-
return (await response.json()) as TranscribeISOBMFFFileResult;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
throw new Error(
|
|
184
|
-
`Failed to transcribe isobmff file ${id} ${response.status} ${response.statusText}`,
|
|
185
|
-
);
|
|
186
|
-
};
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { createTestTrack } from "TEST/createTestTrack.js";
|
|
2
|
-
import { HttpResponse, http } from "msw";
|
|
3
|
-
import { setupServer } from "msw/node";
|
|
4
|
-
import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
|
|
5
|
-
import { Client } from "../client.js";
|
|
6
|
-
import { webReadableFromBuffers } from "../readableFromBuffers.js";
|
|
7
|
-
import { createISOBMFFTrack, uploadISOBMFFTrack } from "./isobmff-track.js";
|
|
8
|
-
|
|
9
|
-
const server = setupServer();
|
|
10
|
-
const client = new Client("ef_TEST_TOKEN", "http://localhost");
|
|
11
|
-
|
|
12
|
-
const UploadMustContinue = (fileId = "test-file", trackId = 1) =>
|
|
13
|
-
http.get(
|
|
14
|
-
`http://localhost/api/v1/isobmff_tracks/${fileId}/${trackId}/upload`,
|
|
15
|
-
() => HttpResponse.json({}, { status: 202 }),
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
describe("ISOBMFF Track", () => {
|
|
19
|
-
beforeAll(() => server.listen());
|
|
20
|
-
afterEach(() => server.resetHandlers());
|
|
21
|
-
afterAll(() => server.close());
|
|
22
|
-
|
|
23
|
-
describe("createISOBMFFTrack", () => {
|
|
24
|
-
test("Throws when track is too large", async () => {
|
|
25
|
-
await expect(
|
|
26
|
-
createISOBMFFTrack(
|
|
27
|
-
client,
|
|
28
|
-
createTestTrack({ byte_size: 1024 * 1024 * 1025 }),
|
|
29
|
-
),
|
|
30
|
-
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
31
|
-
[ZodError: [
|
|
32
|
-
{
|
|
33
|
-
"code": "too_big",
|
|
34
|
-
"maximum": 1073741824,
|
|
35
|
-
"type": "number",
|
|
36
|
-
"inclusive": true,
|
|
37
|
-
"exact": false,
|
|
38
|
-
"message": "Number must be less than or equal to 1073741824",
|
|
39
|
-
"path": [
|
|
40
|
-
"byte_size"
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
]]
|
|
44
|
-
`);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("Throws when server returns an error", async () => {
|
|
48
|
-
server.use(
|
|
49
|
-
http.post("http://localhost/api/v1/isobmff_tracks", () =>
|
|
50
|
-
HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
51
|
-
),
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
await expect(
|
|
55
|
-
createISOBMFFTrack(client, createTestTrack()),
|
|
56
|
-
).rejects.toThrowError(
|
|
57
|
-
"Failed to create isobmff track 500 Internal Server Error",
|
|
58
|
-
);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("Returns json data from the http response", async () => {
|
|
62
|
-
server.use(
|
|
63
|
-
http.post("http://localhost/api/v1/isobmff_tracks", () =>
|
|
64
|
-
HttpResponse.json(
|
|
65
|
-
{ testResponse: "test" },
|
|
66
|
-
{ status: 200, statusText: "OK" },
|
|
67
|
-
),
|
|
68
|
-
),
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
const response = await createISOBMFFTrack(
|
|
72
|
-
client,
|
|
73
|
-
createTestTrack({ byte_size: 1024 * 1024 * 5 }),
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
expect(response).toEqual({ testResponse: "test" });
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe("uploadISOBMFFTrack", () => {
|
|
81
|
-
test("Throws when server returns an error", async () => {
|
|
82
|
-
server.use(
|
|
83
|
-
UploadMustContinue("test-file", 1),
|
|
84
|
-
http.post(
|
|
85
|
-
"http://localhost/api/v1/isobmff_tracks/test-file/1/upload",
|
|
86
|
-
() => HttpResponse.text("Internal Server Error", { status: 500 }),
|
|
87
|
-
),
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
await expect(
|
|
91
|
-
uploadISOBMFFTrack(
|
|
92
|
-
client,
|
|
93
|
-
"test-file",
|
|
94
|
-
1,
|
|
95
|
-
webReadableFromBuffers(Buffer.from("test")),
|
|
96
|
-
4,
|
|
97
|
-
).whenUploaded(),
|
|
98
|
-
).rejects.toThrowError(
|
|
99
|
-
"Failed to upload chunk 0 for /api/v1/isobmff_tracks/test-file/1/upload 500 Internal Server Error",
|
|
100
|
-
);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test("Succeeds when server returns a success", async () => {
|
|
104
|
-
server.use(
|
|
105
|
-
UploadMustContinue("test-file", 1),
|
|
106
|
-
http.post(
|
|
107
|
-
"http://localhost/api/v1/isobmff_tracks/test-file/1/upload",
|
|
108
|
-
() => HttpResponse.json({}, { status: 201 }),
|
|
109
|
-
),
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
await expect(
|
|
113
|
-
uploadISOBMFFTrack(
|
|
114
|
-
client,
|
|
115
|
-
"test-file",
|
|
116
|
-
1,
|
|
117
|
-
webReadableFromBuffers(Buffer.from("test")),
|
|
118
|
-
4,
|
|
119
|
-
).whenUploaded(),
|
|
120
|
-
).resolves.toEqual([
|
|
121
|
-
{ type: "progress", progress: 0 },
|
|
122
|
-
{ type: "progress", progress: 1 },
|
|
123
|
-
]);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
});
|