@editframe/cli 0.10.0-beta.5 → 0.10.0-beta.6
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/VERSION.d.ts +1 -1
- package/dist/VERSION.js +1 -1
- package/dist/commands/render.d.ts +1 -1
- package/dist/commands/render.js +55 -43
- package/dist/commands/sync.js +5 -2
- package/dist/operations/syncAssetsDirectory/SubAssetSync.d.ts +20 -0
- package/dist/operations/syncAssetsDirectory/SubAssetSync.js +26 -0
- package/dist/operations/syncAssetsDirectory/SyncCaption.d.ts +19 -0
- package/dist/operations/syncAssetsDirectory/SyncCaption.js +66 -0
- package/dist/operations/syncAssetsDirectory/SyncCaption.test.d.ts +1 -0
- package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.d.ts +20 -0
- package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.js +79 -0
- package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.test.d.ts +1 -0
- package/dist/operations/syncAssetsDirectory/SyncImage.d.ts +23 -0
- package/dist/operations/syncAssetsDirectory/SyncImage.js +95 -0
- package/dist/operations/syncAssetsDirectory/SyncImage.test.d.ts +1 -0
- package/dist/operations/syncAssetsDirectory/SyncStatus.d.ts +41 -0
- package/dist/operations/syncAssetsDirectory/SyncStatus.js +43 -0
- package/dist/operations/syncAssetsDirectory/SyncTrack.d.ts +70 -0
- package/dist/operations/syncAssetsDirectory/SyncTrack.js +138 -0
- package/dist/operations/syncAssetsDirectory/SyncTrack.test.d.ts +1 -0
- package/dist/operations/syncAssetsDirectory/doAssetSync.d.ts +5 -0
- package/dist/operations/syncAssetsDirectory/doAssetSync.js +48 -0
- package/dist/operations/syncAssetsDirectory/doAssetSync.test.d.ts +1 -0
- package/dist/operations/syncAssetsDirectory.d.ts +1 -1
- package/dist/operations/syncAssetsDirectory.js +20 -240
- package/dist/test-fixtures/fixture.d.ts +26 -0
- package/dist/utils/index.js +4 -1
- package/package.json +5 -5
- package/src/commands/render.ts +61 -52
- package/src/commands/sync.ts +5 -2
- package/src/operations/syncAssetsDirectory/SubAssetSync.ts +42 -0
- package/src/operations/syncAssetsDirectory/SyncCaption.test.ts +145 -0
- package/src/operations/syncAssetsDirectory/SyncCaption.ts +76 -0
- package/src/operations/syncAssetsDirectory/SyncFragmentIndex.test.ts +151 -0
- package/src/operations/syncAssetsDirectory/SyncFragmentIndex.ts +92 -0
- package/src/operations/syncAssetsDirectory/SyncImage.test.ts +131 -0
- package/src/operations/syncAssetsDirectory/SyncImage.ts +112 -0
- package/src/operations/syncAssetsDirectory/SyncStatus.ts +51 -0
- package/src/operations/syncAssetsDirectory/SyncTrack.test.ts +222 -0
- package/src/operations/syncAssetsDirectory/SyncTrack.ts +164 -0
- package/src/operations/syncAssetsDirectory/doAssetSync.test.ts +134 -0
- package/src/operations/syncAssetsDirectory/doAssetSync.ts +62 -0
- package/src/operations/syncAssetsDirectory.test.ts +482 -0
- package/src/operations/syncAssetsDirectory.ts +22 -283
- package/src/utils/index.ts +4 -1
- package/test-fixtures/fixture.ts +141 -0
- package/test-fixtures/network.ts +181 -0
- package/test-fixtures/test-captions.json +9 -0
- package/test-fixtures/test.mp4 +0 -0
- package/test-fixtures/test.png +0 -0
- package/src/commands/render.test.ts +0 -34
- /package/dist/{commands/render.test.d.ts → operations/syncAssetsDirectory.test.d.ts} +0 -0
|
@@ -1,63 +1,16 @@
|
|
|
1
|
-
import { Probe } from "@editframe/assets";
|
|
2
1
|
import fs from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
createImageFile,
|
|
7
|
-
createISOBMFFFile,
|
|
8
|
-
createISOBMFFTrack,
|
|
9
|
-
uploadCaptionFile,
|
|
10
|
-
uploadFragmentIndex,
|
|
11
|
-
uploadImageFile,
|
|
12
|
-
uploadISOBMFFTrack,
|
|
13
|
-
type CreateISOBMFFTrackPayload,
|
|
14
|
-
} from "@editframe/api";
|
|
15
|
-
import { statSync } from "node:fs";
|
|
16
|
-
|
|
17
|
-
import { createReadStream } from "node:fs";
|
|
18
|
-
import type { z } from "zod";
|
|
19
|
-
import { getClient } from "../utils/index.ts";
|
|
20
|
-
|
|
21
|
-
const imageMatch = /\.(png|jpe?g|gif|webp)$/i;
|
|
22
|
-
const trackMatch = /\.track-[\d]+.mp4$/i;
|
|
23
|
-
const fragmentIndexMatch = /\.tracks.json$/i;
|
|
24
|
-
const captionsMatch = /\.captions.json$/i;
|
|
25
|
-
|
|
26
|
-
class SyncStatus {
|
|
27
|
-
constructor(private assetPath: string) {}
|
|
28
|
-
|
|
29
|
-
infoPath = `${this.assetPath}.info`;
|
|
30
|
-
|
|
31
|
-
async isSynced() {
|
|
32
|
-
const result = await fs.stat(this.infoPath).catch((error) => {
|
|
33
|
-
if (error.code === "ENOENT") {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
throw error;
|
|
37
|
-
});
|
|
38
|
-
return !!result;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async markSynced() {
|
|
42
|
-
process.stderr.write(`✏️ Marking asset as synced: ${this.assetPath}\n`);
|
|
43
|
-
await fs.writeFile(this.infoPath, "{}", "utf-8");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
3
|
+
import { getAssetSync } from "./syncAssetsDirectory/SubAssetSync.ts";
|
|
4
|
+
import { doAssetSync } from "./syncAssetsDirectory/doAssetSync.ts";
|
|
46
5
|
|
|
47
6
|
export const syncAssetDirectory = async (
|
|
48
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Project directory will be used as the base to find an assets directory.
|
|
49
9
|
* Assets will be synced from `<projectDirectory>/src/assets`
|
|
50
10
|
*/
|
|
51
|
-
|
|
11
|
+
cacheDir: string,
|
|
52
12
|
) => {
|
|
53
|
-
const
|
|
54
|
-
process.cwd(),
|
|
55
|
-
projectDirectory,
|
|
56
|
-
"src",
|
|
57
|
-
"assets",
|
|
58
|
-
".cache",
|
|
59
|
-
);
|
|
60
|
-
const stat = await fs.stat(fullPath).catch((error) => {
|
|
13
|
+
const stat = await fs.stat(cacheDir).catch((error) => {
|
|
61
14
|
if (error.code === "ENOENT") {
|
|
62
15
|
return;
|
|
63
16
|
}
|
|
@@ -65,12 +18,12 @@ export const syncAssetDirectory = async (
|
|
|
65
18
|
});
|
|
66
19
|
|
|
67
20
|
if (!stat?.isDirectory()) {
|
|
68
|
-
console.error(`No assets cache directory found at ${
|
|
21
|
+
console.error(`No assets cache directory found at ${cacheDir}`);
|
|
69
22
|
return;
|
|
70
23
|
}
|
|
71
|
-
const assets = await fs.readdir(
|
|
24
|
+
const assets = await fs.readdir(cacheDir);
|
|
72
25
|
|
|
73
|
-
process.stderr.write(`Syncing asset dir: ${
|
|
26
|
+
process.stderr.write(`Syncing asset dir: ${cacheDir}\n`);
|
|
74
27
|
|
|
75
28
|
const errors: Record<string, string[]> = {};
|
|
76
29
|
|
|
@@ -90,7 +43,7 @@ export const syncAssetDirectory = async (
|
|
|
90
43
|
|
|
91
44
|
for (const assetMd5 of assets) {
|
|
92
45
|
reportInfo(assetMd5, `Syncing asset: ${assetMd5}`);
|
|
93
|
-
const assetDir = path.join(
|
|
46
|
+
const assetDir = path.join(cacheDir, assetMd5);
|
|
94
47
|
const stat = await fs.stat(assetDir);
|
|
95
48
|
if (!stat.isDirectory()) {
|
|
96
49
|
reportError(assetMd5, "Invalid asset. Did not find asset directory.");
|
|
@@ -104,235 +57,21 @@ export const syncAssetDirectory = async (
|
|
|
104
57
|
continue;
|
|
105
58
|
}
|
|
106
59
|
const subAssetPath = path.join(assetDir, subAsset);
|
|
107
|
-
const syncStatus = new SyncStatus(subAssetPath);
|
|
108
|
-
if (await syncStatus.isSynced()) {
|
|
109
|
-
reportInfo(
|
|
110
|
-
subAsset,
|
|
111
|
-
`✔ Sub-asset has already been synced: ${subAsset}`,
|
|
112
|
-
);
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
switch (true) {
|
|
116
|
-
case imageMatch.test(subAsset): {
|
|
117
|
-
const probeResult = await Probe.probePath(subAssetPath);
|
|
118
|
-
const [videoProbe] = probeResult.videoStreams;
|
|
119
|
-
const { format } = probeResult;
|
|
120
|
-
if (!videoProbe) {
|
|
121
|
-
reportError(subAsset, `No media info found in image: ${subAsset}`);
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
const ext = path.extname(subAsset).slice(1);
|
|
125
|
-
if (
|
|
126
|
-
!(
|
|
127
|
-
ext === "jpg" ||
|
|
128
|
-
ext === "jpeg" ||
|
|
129
|
-
ext === "png" ||
|
|
130
|
-
ext === "webp"
|
|
131
|
-
)
|
|
132
|
-
) {
|
|
133
|
-
reportError(subAsset, `Invalid image format: ${subAsset}`);
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
reportInfo(subAsset, `🖼️ Syncing image: ${subAsset}`);
|
|
137
|
-
const created = await createImageFile(getClient(), {
|
|
138
|
-
md5: assetMd5,
|
|
139
|
-
filename: subAsset,
|
|
140
|
-
width: videoProbe.width,
|
|
141
|
-
height: videoProbe.height,
|
|
142
|
-
mime_type: `image/${ext}`,
|
|
143
|
-
byte_size: (await fs.stat(subAssetPath)).size,
|
|
144
|
-
});
|
|
145
|
-
if (created) {
|
|
146
|
-
if (created.complete) {
|
|
147
|
-
reportInfo(subAsset, " ✔ Image has already been synced");
|
|
148
|
-
await syncStatus.markSynced();
|
|
149
|
-
} else {
|
|
150
|
-
await uploadImageFile(
|
|
151
|
-
getClient(),
|
|
152
|
-
created.id,
|
|
153
|
-
createReadStream(subAssetPath),
|
|
154
|
-
Number.parseInt(format.size || "0"),
|
|
155
|
-
)
|
|
156
|
-
.then(() => {
|
|
157
|
-
reportSuccess(subAsset, "Image has been synced.");
|
|
158
|
-
return syncStatus.markSynced();
|
|
159
|
-
})
|
|
160
|
-
.catch((error) => {
|
|
161
|
-
reportError(
|
|
162
|
-
subAsset,
|
|
163
|
-
`Error uploading image: ${error.message}`,
|
|
164
|
-
);
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
case trackMatch.test(subAsset): {
|
|
171
|
-
reportInfo(subAsset, `📼 Syncing a/v track: ${subAsset}`);
|
|
172
|
-
const createdFile = await createISOBMFFFile(getClient(), {
|
|
173
|
-
md5: assetMd5,
|
|
174
|
-
filename: subAsset.replace(/\.track-[\d]+.mp4$/, ""),
|
|
175
|
-
});
|
|
176
|
-
if (createdFile) {
|
|
177
|
-
const probe = await Probe.probePath(subAssetPath);
|
|
178
|
-
const trackId = subAsset.match(/track-([\d]+).mp4/)?.[1];
|
|
179
|
-
if (!trackId) {
|
|
180
|
-
reportError(subAsset, `No track ID found for track: ${subAsset}`);
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
const [track] = probe.streams;
|
|
184
|
-
if (!track) {
|
|
185
|
-
reportError(
|
|
186
|
-
subAsset,
|
|
187
|
-
`No track stream found in track: ${subAsset}`,
|
|
188
|
-
);
|
|
189
|
-
break;
|
|
190
|
-
}
|
|
191
|
-
if (track.duration === undefined) {
|
|
192
|
-
reportError(subAsset, `No duration found in track: ${subAsset}`);
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const stat = await fs.stat(subAssetPath);
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Because the payload is a discriminated union, we need to create these objects
|
|
200
|
-
* only after the value for type has been narrowed, otherwise the object will
|
|
201
|
-
* have type: "audio" | "video" which doesnt' match the payload type that
|
|
202
|
-
* looks more like { type: "audio", ... } | { type: "video", ... }
|
|
203
|
-
*/
|
|
204
|
-
const createPayload: z.infer<typeof CreateISOBMFFTrackPayload> =
|
|
205
|
-
track.codec_type === "audio"
|
|
206
|
-
? {
|
|
207
|
-
type: track.codec_type,
|
|
208
|
-
file_id: createdFile.id,
|
|
209
|
-
track_id: Number(trackId),
|
|
210
|
-
probe_info: track,
|
|
211
|
-
duration_ms: Math.round(track.duration * 1000),
|
|
212
|
-
codec_name: track.codec_name,
|
|
213
|
-
byte_size: stat.size,
|
|
214
|
-
}
|
|
215
|
-
: {
|
|
216
|
-
type: track.codec_type,
|
|
217
|
-
file_id: createdFile.id,
|
|
218
|
-
track_id: Number(trackId),
|
|
219
|
-
probe_info: track,
|
|
220
|
-
duration_ms: Math.round(track.duration * 1000),
|
|
221
|
-
codec_name: track.codec_name,
|
|
222
|
-
byte_size: stat.size,
|
|
223
|
-
};
|
|
224
|
-
const createdTrack = await createISOBMFFTrack(
|
|
225
|
-
getClient(),
|
|
226
|
-
createPayload,
|
|
227
|
-
);
|
|
228
60
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
createdFile.id,
|
|
237
|
-
Number(trackId),
|
|
238
|
-
createReadStream(subAssetPath),
|
|
239
|
-
createdTrack.byte_size,
|
|
240
|
-
)
|
|
241
|
-
.then(() => {
|
|
242
|
-
reportSuccess(subAsset, "Track has been synced.");
|
|
243
|
-
return syncStatus.markSynced();
|
|
244
|
-
})
|
|
245
|
-
.catch((error) => {
|
|
246
|
-
reportError(
|
|
247
|
-
subAsset,
|
|
248
|
-
`Error uploading track: ${error.message}`,
|
|
249
|
-
);
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
break;
|
|
256
|
-
}
|
|
257
|
-
case fragmentIndexMatch.test(subAsset): {
|
|
258
|
-
reportInfo(subAsset, `📋 Syncing fragment index: ${subAsset}`);
|
|
259
|
-
const createdFile = await createISOBMFFFile(getClient(), {
|
|
260
|
-
md5: assetMd5,
|
|
261
|
-
filename: subAsset.replace(/\.tracks.json$/, ""),
|
|
262
|
-
});
|
|
263
|
-
if (createdFile) {
|
|
264
|
-
if (createdFile.fragment_index_complete) {
|
|
265
|
-
reportInfo(subAsset, "✔ Fragment index has already been synced.");
|
|
266
|
-
await syncStatus.markSynced();
|
|
267
|
-
} else {
|
|
268
|
-
const stats = statSync(subAssetPath);
|
|
269
|
-
const readStream = createReadStream(subAssetPath);
|
|
270
|
-
await uploadFragmentIndex(
|
|
271
|
-
getClient(),
|
|
272
|
-
createdFile.id,
|
|
273
|
-
readStream,
|
|
274
|
-
stats.size,
|
|
275
|
-
)
|
|
276
|
-
.then(() => {
|
|
277
|
-
reportSuccess(subAsset, "Fragment index has been synced.");
|
|
278
|
-
return syncStatus.markSynced();
|
|
279
|
-
})
|
|
280
|
-
.catch((error) => {
|
|
281
|
-
reportError(
|
|
282
|
-
subAsset,
|
|
283
|
-
`Error uploading fragment index: ${error.message}`,
|
|
284
|
-
);
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
} else {
|
|
288
|
-
reportError(
|
|
289
|
-
subAsset,
|
|
290
|
-
`No file found for fragment index: ${subAsset}`,
|
|
291
|
-
);
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
break;
|
|
295
|
-
}
|
|
296
|
-
case captionsMatch.test(subAsset): {
|
|
297
|
-
reportInfo(subAsset, `📝 Syncing captions: ${subAsset}`);
|
|
298
|
-
const createdFile = await createCaptionFile(getClient(), {
|
|
299
|
-
md5: assetMd5,
|
|
300
|
-
filename: subAsset.replace(/\.captions.json$/, ""),
|
|
301
|
-
byte_size: (await fs.stat(subAsset)).size,
|
|
302
|
-
});
|
|
303
|
-
if (createdFile) {
|
|
304
|
-
if (createdFile.complete) {
|
|
305
|
-
reportInfo(subAsset, "✔ Captions have already been synced.");
|
|
306
|
-
await syncStatus.markSynced();
|
|
307
|
-
} else {
|
|
308
|
-
const readStream = createReadStream(subAssetPath);
|
|
309
|
-
const stats = statSync(subAssetPath);
|
|
310
|
-
await uploadCaptionFile(
|
|
311
|
-
getClient(),
|
|
312
|
-
createdFile.id,
|
|
313
|
-
readStream,
|
|
314
|
-
stats.size,
|
|
315
|
-
)
|
|
316
|
-
.then(() => {
|
|
317
|
-
reportSuccess(subAsset, "Captions have been synced.");
|
|
318
|
-
return syncStatus.markSynced();
|
|
319
|
-
})
|
|
320
|
-
.catch((error) => {
|
|
321
|
-
reportError(
|
|
322
|
-
subAsset,
|
|
323
|
-
`Error uploading captions: ${error.message}`,
|
|
324
|
-
);
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
} else {
|
|
328
|
-
reportError(subAsset, `No file found for captions: ${subAsset}`);
|
|
329
|
-
break;
|
|
61
|
+
try {
|
|
62
|
+
const assetSync = getAssetSync(subAssetPath, assetMd5);
|
|
63
|
+
for await (const { status, message } of doAssetSync(assetSync)) {
|
|
64
|
+
if (status === "success") {
|
|
65
|
+
reportSuccess(subAsset, message);
|
|
66
|
+
} else if (status === "info") {
|
|
67
|
+
reportInfo(subAsset, message);
|
|
330
68
|
}
|
|
331
|
-
break;
|
|
332
69
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (error instanceof Error) {
|
|
72
|
+
reportError(subAsset, error.message);
|
|
73
|
+
} else {
|
|
74
|
+
reportError(subAsset, "Unknown error");
|
|
336
75
|
}
|
|
337
76
|
}
|
|
338
77
|
}
|
package/src/utils/index.ts
CHANGED
|
@@ -7,7 +7,10 @@ let client: Client;
|
|
|
7
7
|
export const getClient = () => {
|
|
8
8
|
if (!client) {
|
|
9
9
|
const programOpts = program.opts();
|
|
10
|
-
client = new Client(
|
|
10
|
+
client = new Client(
|
|
11
|
+
programOpts.token || process.env.EF_TOKEN,
|
|
12
|
+
programOpts.efHost || process.env.EF_HOST,
|
|
13
|
+
);
|
|
11
14
|
}
|
|
12
15
|
return client;
|
|
13
16
|
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
import { v4 } from "uuid";
|
|
6
|
+
import { expect } from "vitest";
|
|
7
|
+
|
|
8
|
+
import { md5Buffer } from "@editframe/assets";
|
|
9
|
+
|
|
10
|
+
import { syncAssetDirectory } from "../src/operations/syncAssetsDirectory.ts";
|
|
11
|
+
import type { SyncStatusInfo } from "../src/operations/syncAssetsDirectory/SyncStatus.ts";
|
|
12
|
+
|
|
13
|
+
export const fixture = async (
|
|
14
|
+
fixture: string,
|
|
15
|
+
name: string,
|
|
16
|
+
): Promise<Fixture> => {
|
|
17
|
+
const originalPath = join(__dirname, fixture);
|
|
18
|
+
const content = await readFile(originalPath);
|
|
19
|
+
return {
|
|
20
|
+
name,
|
|
21
|
+
fixture,
|
|
22
|
+
content,
|
|
23
|
+
originalPath,
|
|
24
|
+
md5: md5Buffer(content),
|
|
25
|
+
write: async (dir: string) => {
|
|
26
|
+
await writeFile(join(dir, name), content);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export interface Fixture {
|
|
31
|
+
name: string;
|
|
32
|
+
fixture: string;
|
|
33
|
+
md5: string;
|
|
34
|
+
content: Buffer;
|
|
35
|
+
originalPath: string;
|
|
36
|
+
write: (dir: string) => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export const withFixtures = async (
|
|
39
|
+
fixtures: Promise<Fixture>[],
|
|
40
|
+
fn: (props: {
|
|
41
|
+
files: Fixture[];
|
|
42
|
+
rootDir: string;
|
|
43
|
+
srcDir: string;
|
|
44
|
+
assetsDir: string;
|
|
45
|
+
cacheDir: string;
|
|
46
|
+
expectCacheFiles: (fixture: Fixture, files: string[]) => Promise<void>;
|
|
47
|
+
expectInfoFileContent: (
|
|
48
|
+
fileName: string,
|
|
49
|
+
fixture: Fixture,
|
|
50
|
+
expectedContent: Pick<SyncStatusInfo, "complete" | "id"> & {
|
|
51
|
+
byte_size?: number;
|
|
52
|
+
},
|
|
53
|
+
) => Promise<void>;
|
|
54
|
+
syncAssetsDirectory: () => Promise<void>;
|
|
55
|
+
cacheImage: (fixture: Fixture) => Promise<string>;
|
|
56
|
+
generateTrack: (fixture: Fixture, trackId: number) => Promise<string>;
|
|
57
|
+
generateTrackFragmentIndex: (fixture: Fixture) => Promise<string>;
|
|
58
|
+
generateCaptions: (fixture: Fixture) => Promise<string>;
|
|
59
|
+
}) => Promise<void>,
|
|
60
|
+
) => {
|
|
61
|
+
const files = await Promise.all(fixtures);
|
|
62
|
+
const tempDir = `${tmpdir()}/${v4()}`;
|
|
63
|
+
const srcDir = join(tempDir, "src");
|
|
64
|
+
const assetsDir = join(srcDir, "assets");
|
|
65
|
+
const cacheDir = join(assetsDir, ".cache");
|
|
66
|
+
await mkdir(assetsDir, { recursive: true });
|
|
67
|
+
await Promise.all(files.map(async (file) => file.write(assetsDir)));
|
|
68
|
+
|
|
69
|
+
await fn({
|
|
70
|
+
files,
|
|
71
|
+
rootDir: tempDir,
|
|
72
|
+
srcDir,
|
|
73
|
+
assetsDir,
|
|
74
|
+
cacheDir,
|
|
75
|
+
expectCacheFiles: async (fixture, expectedFiles) => {
|
|
76
|
+
const files = await readdir(join(assetsDir, ".cache", fixture.md5));
|
|
77
|
+
expect(files).toEqual(expectedFiles);
|
|
78
|
+
},
|
|
79
|
+
expectInfoFileContent: async (fileName, fixture, expectedContent) => {
|
|
80
|
+
const filePath = join(cacheDir, fixture.md5, fileName);
|
|
81
|
+
const content = JSON.parse(await readFile(filePath, "utf-8"));
|
|
82
|
+
expect(content).toMatchObject({
|
|
83
|
+
asset_id: `${fixture.md5}:${fixture.name}`,
|
|
84
|
+
md5: fixture.md5,
|
|
85
|
+
version: "1",
|
|
86
|
+
byte_size: fixture.content.length,
|
|
87
|
+
...expectedContent,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
syncAssetsDirectory: () => {
|
|
91
|
+
return syncAssetDirectory(cacheDir);
|
|
92
|
+
},
|
|
93
|
+
cacheImage: async (fixture) => {
|
|
94
|
+
await mkdir(join(cacheDir, fixture.md5), { recursive: true });
|
|
95
|
+
const filePath = join(cacheDir, fixture.md5, fixture.name);
|
|
96
|
+
await writeFile(filePath, fixture.content);
|
|
97
|
+
return filePath;
|
|
98
|
+
},
|
|
99
|
+
generateTrack: async (fixture, trackId: number) => {
|
|
100
|
+
await mkdir(join(cacheDir, fixture.md5), { recursive: true });
|
|
101
|
+
const filePath = join(
|
|
102
|
+
cacheDir,
|
|
103
|
+
fixture.md5,
|
|
104
|
+
`${fixture.name}.track-${trackId}.mp4`,
|
|
105
|
+
);
|
|
106
|
+
await writeFile(filePath, fixture.content);
|
|
107
|
+
return filePath;
|
|
108
|
+
},
|
|
109
|
+
generateTrackFragmentIndex: async (fixture) => {
|
|
110
|
+
await mkdir(join(cacheDir, fixture.md5), { recursive: true });
|
|
111
|
+
const filePath = join(
|
|
112
|
+
cacheDir,
|
|
113
|
+
fixture.md5,
|
|
114
|
+
`${fixture.name}.tracks.json`,
|
|
115
|
+
);
|
|
116
|
+
await writeFile(
|
|
117
|
+
filePath,
|
|
118
|
+
JSON.stringify({
|
|
119
|
+
tracks: ["FAKE_TRACK_INDEX"],
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
return filePath;
|
|
123
|
+
},
|
|
124
|
+
generateCaptions: async (fixture) => {
|
|
125
|
+
await mkdir(join(cacheDir, fixture.md5), { recursive: true });
|
|
126
|
+
const filePath = join(
|
|
127
|
+
cacheDir,
|
|
128
|
+
fixture.md5,
|
|
129
|
+
`${fixture.name}.captions.json`,
|
|
130
|
+
);
|
|
131
|
+
await writeFile(
|
|
132
|
+
filePath,
|
|
133
|
+
JSON.stringify({
|
|
134
|
+
captions: ["FAKE_CAPTION_INDEX"],
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
return filePath;
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
await rm(tempDir, { recursive: true });
|
|
141
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { http, HttpResponse } from "msw";
|
|
2
|
+
import { setupServer } from "msw/node";
|
|
3
|
+
import { afterAll, afterEach, beforeAll } from "vitest";
|
|
4
|
+
import type { Fixture } from "./fixture.ts";
|
|
5
|
+
|
|
6
|
+
const server = setupServer();
|
|
7
|
+
|
|
8
|
+
export const useMSW = () => {
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
server.listen();
|
|
11
|
+
process.env.EF_TOKEN = "ef_SECRET_TOKEN";
|
|
12
|
+
process.env.EF_HOST = "http://localhost:3000";
|
|
13
|
+
});
|
|
14
|
+
afterAll(() => server.close());
|
|
15
|
+
afterEach(() => server.resetHandlers());
|
|
16
|
+
return server;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const mockCreateImageFile = ({
|
|
20
|
+
complete = true,
|
|
21
|
+
id = "123",
|
|
22
|
+
filename = "test.png",
|
|
23
|
+
fixture,
|
|
24
|
+
}: { complete?: boolean; id?: string; filename?: string; fixture: Fixture }) =>
|
|
25
|
+
http.post(
|
|
26
|
+
"http://localhost:3000/api/v1/image_files",
|
|
27
|
+
async () => {
|
|
28
|
+
return HttpResponse.json({
|
|
29
|
+
id,
|
|
30
|
+
asset_id: `${fixture.md5}:${filename}`,
|
|
31
|
+
complete,
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
{ once: true },
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
export const mockGetUploadImageFile = ({
|
|
38
|
+
complete = true,
|
|
39
|
+
id = "123",
|
|
40
|
+
filename = "test.png",
|
|
41
|
+
fixture,
|
|
42
|
+
}: { complete?: boolean; id?: string; filename?: string; fixture: Fixture }) =>
|
|
43
|
+
http.get(
|
|
44
|
+
`http://localhost:3000/api/v1/image_files/${id}/upload`,
|
|
45
|
+
async () => {
|
|
46
|
+
return HttpResponse.json({
|
|
47
|
+
id,
|
|
48
|
+
complete,
|
|
49
|
+
filename,
|
|
50
|
+
asset_id: `${fixture.md5}:${filename}`,
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export const mockCreateIsobmffFile = ({
|
|
56
|
+
complete = true,
|
|
57
|
+
id = "123",
|
|
58
|
+
filename = "test.mp4",
|
|
59
|
+
fixture,
|
|
60
|
+
}: {
|
|
61
|
+
complete?: boolean;
|
|
62
|
+
id?: string;
|
|
63
|
+
filename?: string;
|
|
64
|
+
fixture: Fixture;
|
|
65
|
+
}) =>
|
|
66
|
+
http.post(
|
|
67
|
+
"http://localhost:3000/api/v1/isobmff_files",
|
|
68
|
+
async () => {
|
|
69
|
+
return HttpResponse.json({
|
|
70
|
+
id,
|
|
71
|
+
asset_id: `${fixture.md5}:${filename}`,
|
|
72
|
+
fragment_index_complete: complete,
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
{ once: true },
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export const mockCreateIsobmffTrack = ({
|
|
79
|
+
complete = true,
|
|
80
|
+
id = "123",
|
|
81
|
+
fileId = "123",
|
|
82
|
+
filename = "test.mp4",
|
|
83
|
+
fixture,
|
|
84
|
+
}: {
|
|
85
|
+
complete?: boolean;
|
|
86
|
+
id?: string;
|
|
87
|
+
fileId?: string;
|
|
88
|
+
filename?: string;
|
|
89
|
+
fixture: Fixture;
|
|
90
|
+
}) =>
|
|
91
|
+
http.post(
|
|
92
|
+
"http://localhost:3000/api/v1/isobmff_tracks",
|
|
93
|
+
async () => {
|
|
94
|
+
return HttpResponse.json({
|
|
95
|
+
id,
|
|
96
|
+
asset_id: `${fixture.md5}:${filename}`,
|
|
97
|
+
complete,
|
|
98
|
+
file_id: fileId,
|
|
99
|
+
track_id: id,
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
{ once: true },
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
export const mockGetIsobmffTrackUpload = ({
|
|
106
|
+
complete = true,
|
|
107
|
+
fileId = "123",
|
|
108
|
+
trackId = 1,
|
|
109
|
+
id = "123",
|
|
110
|
+
}: {
|
|
111
|
+
complete?: boolean;
|
|
112
|
+
id?: string;
|
|
113
|
+
fileId?: string;
|
|
114
|
+
trackId?: number;
|
|
115
|
+
}) =>
|
|
116
|
+
http.get(
|
|
117
|
+
`http://localhost:3000/api/v1/isobmff_tracks/${fileId}/${trackId}/upload`,
|
|
118
|
+
async () => {
|
|
119
|
+
return HttpResponse.json({
|
|
120
|
+
id,
|
|
121
|
+
complete,
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
export const mockUploadIsobmffFileIndex = ({
|
|
126
|
+
complete = true,
|
|
127
|
+
id = "123",
|
|
128
|
+
}: { complete?: boolean; id?: string }) =>
|
|
129
|
+
http.post(
|
|
130
|
+
"http://localhost:3000/api/v1/isobmff_files/123/index/upload",
|
|
131
|
+
async () => {
|
|
132
|
+
return HttpResponse.json({
|
|
133
|
+
id,
|
|
134
|
+
complete,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
{ once: true },
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
export const mockCreateCaptionFile = ({
|
|
141
|
+
complete = true,
|
|
142
|
+
id = "123",
|
|
143
|
+
filename = "test.captions.json",
|
|
144
|
+
fixture,
|
|
145
|
+
}: {
|
|
146
|
+
complete?: boolean;
|
|
147
|
+
id?: string;
|
|
148
|
+
filename?: string;
|
|
149
|
+
fixture: Fixture;
|
|
150
|
+
}) =>
|
|
151
|
+
http.post(
|
|
152
|
+
"http://localhost:3000/api/v1/caption_files",
|
|
153
|
+
async () => {
|
|
154
|
+
return HttpResponse.json({
|
|
155
|
+
id,
|
|
156
|
+
complete,
|
|
157
|
+
asset_id: `${fixture.md5}:${filename}`,
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
{ once: true },
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
export const mockUploadCaptionFile = ({
|
|
164
|
+
complete = true,
|
|
165
|
+
id = "123",
|
|
166
|
+
}: {
|
|
167
|
+
complete?: boolean;
|
|
168
|
+
id?: string;
|
|
169
|
+
filename?: string;
|
|
170
|
+
fixture: Fixture;
|
|
171
|
+
}) =>
|
|
172
|
+
http.post(
|
|
173
|
+
"http://localhost:3000/api/v1/caption_files/123/upload",
|
|
174
|
+
async () => {
|
|
175
|
+
return HttpResponse.json({
|
|
176
|
+
id,
|
|
177
|
+
complete,
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
{ once: true },
|
|
181
|
+
);
|
|
Binary file
|
|
Binary file
|