@editframe/cli 0.11.0-beta.9 → 0.12.0-beta.1
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/process-file.js +32 -21
- package/dist/commands/render.js +8 -2
- package/dist/operations/syncAssetsDirectory/SubAssetSync.js +3 -0
- package/dist/operations/syncAssetsDirectory/SyncCaption.d.ts +2 -2
- package/dist/operations/syncAssetsDirectory/SyncCaption.js +19 -8
- package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.d.ts +2 -2
- package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.js +19 -12
- package/dist/operations/syncAssetsDirectory/SyncImage.d.ts +2 -2
- package/dist/operations/syncAssetsDirectory/SyncImage.js +16 -11
- package/dist/operations/syncAssetsDirectory/SyncStatus.d.ts +7 -11
- package/dist/operations/syncAssetsDirectory/SyncStatus.js +1 -1
- package/dist/operations/syncAssetsDirectory/SyncTrack.d.ts +2 -2
- package/dist/operations/syncAssetsDirectory/SyncTrack.js +14 -12
- package/dist/utils/createReadableStreamFromReadable.d.ts +4 -0
- package/dist/utils/createReadableStreamFromReadable.js +82 -0
- package/dist/utils/launchBrowserAndWaitForSDK.js +2 -2
- package/package.json +5 -5
- package/src/commands/process-file.ts +36 -22
- package/src/commands/render.ts +9 -2
- package/src/operations/syncAssetsDirectory/SubAssetSync.ts +4 -0
- package/src/operations/syncAssetsDirectory/SyncCaption.test.ts +29 -3
- package/src/operations/syncAssetsDirectory/SyncCaption.ts +22 -9
- package/src/operations/syncAssetsDirectory/SyncFragmentIndex.test.ts +30 -4
- package/src/operations/syncAssetsDirectory/SyncFragmentIndex.ts +22 -13
- package/src/operations/syncAssetsDirectory/SyncImage.test.ts +27 -3
- package/src/operations/syncAssetsDirectory/SyncImage.ts +23 -15
- package/src/operations/syncAssetsDirectory/SyncStatus.ts +3 -3
- package/src/operations/syncAssetsDirectory/SyncTrack.test.ts +38 -3
- package/src/operations/syncAssetsDirectory/SyncTrack.ts +20 -13
- package/src/operations/syncAssetsDirectory.test.ts +22 -1
- package/src/utils/createReadableStreamFromReadable.ts +117 -0
- package/src/utils/launchBrowserAndWaitForSDK.ts +2 -2
- package/src/utils/startDevServer.ts +1 -1
- package/test-fixtures/fixture.ts +0 -1
- package/test-fixtures/network.ts +72 -29
package/dist/VERSION.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.12.0-beta.1";
|
package/dist/VERSION.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { basename } from "node:path";
|
|
2
|
-
import { stat } from "node:fs/promises";
|
|
3
1
|
import { createReadStream } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
import { withSpinner } from "../utils/withSpinner.js";
|
|
6
|
-
import {
|
|
7
|
-
import { createUnprocessedFile, uploadUnprocessedFile, updateUnprocessedFile } from "@editframe/api";
|
|
6
|
+
import { uploadUnprocessedFile, getIsobmffProcessProgress, getIsobmffProcessInfo, createUnprocessedFile, processIsobmffFile } from "@editframe/api";
|
|
8
7
|
import { md5FilePath } from "@editframe/assets";
|
|
8
|
+
import ora from "ora";
|
|
9
|
+
import { createReadableStreamFromReadable } from "../utils/createReadableStreamFromReadable.js";
|
|
10
|
+
import { getClient } from "../utils/index.js";
|
|
9
11
|
program.command("process-file <file>").description("Upload a audio/video to Editframe for processing.").action(async (path) => {
|
|
10
12
|
const client = getClient();
|
|
11
13
|
const md5 = await md5FilePath(path);
|
|
@@ -14,28 +16,37 @@ program.command("process-file <file>").description("Upload a audio/video to Edit
|
|
|
14
16
|
"Creating unprocessed file record",
|
|
15
17
|
async () => await createUnprocessedFile(client, {
|
|
16
18
|
md5,
|
|
17
|
-
processes: [],
|
|
18
19
|
filename: basename(path),
|
|
19
20
|
byte_size
|
|
20
21
|
})
|
|
21
22
|
);
|
|
22
23
|
const readStream = createReadStream(path);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
const upload = uploadUnprocessedFile(
|
|
25
|
+
client,
|
|
26
|
+
unprocessedFile.id,
|
|
27
|
+
createReadableStreamFromReadable(readStream),
|
|
28
|
+
byte_size
|
|
29
|
+
);
|
|
30
|
+
const uploadSpinner = ora("Uploading file");
|
|
31
|
+
for await (const event of upload) {
|
|
32
|
+
uploadSpinner.text = `Uploading file: ${(100 * event.progress).toFixed(2)}%`;
|
|
33
|
+
}
|
|
34
|
+
uploadSpinner.succeed("Upload complete");
|
|
35
|
+
const processor = await withSpinner(
|
|
32
36
|
"Marking for processing",
|
|
33
|
-
async () =>
|
|
34
|
-
return await updateUnprocessedFile(client, unprocessedFile.id, {
|
|
35
|
-
processes: ["isobmff"]
|
|
36
|
-
});
|
|
37
|
-
}
|
|
37
|
+
async () => await processIsobmffFile(client, unprocessedFile.id)
|
|
38
38
|
);
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const processSpinner = ora("Waiting for processing to complete");
|
|
40
|
+
processSpinner.start();
|
|
41
|
+
const progress = await getIsobmffProcessProgress(client, processor.id);
|
|
42
|
+
for await (const event of progress) {
|
|
43
|
+
if (event.type === "progress") {
|
|
44
|
+
processSpinner.text = `Processing: ${(100 * event.data.progress).toFixed(2)}%`;
|
|
45
|
+
} else if (event.type === "complete") {
|
|
46
|
+
processSpinner.succeed("Processing complete");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const info = await getIsobmffProcessInfo(client, processor.id);
|
|
50
|
+
console.log("Processed file info");
|
|
51
|
+
console.log(info);
|
|
41
52
|
});
|
package/dist/commands/render.js
CHANGED
|
@@ -13,6 +13,7 @@ import { RenderInfo, getRenderInfo } from "../operations/getRenderInfo.js";
|
|
|
13
13
|
import { processRenderInfo } from "../operations/processRenderInfo.js";
|
|
14
14
|
import { syncAssetDirectory } from "../operations/syncAssetsDirectory.js";
|
|
15
15
|
import { SyncStatus } from "../operations/syncAssetsDirectory/SyncStatus.js";
|
|
16
|
+
import { createReadableStreamFromReadable } from "../utils/createReadableStreamFromReadable.js";
|
|
16
17
|
import { getFolderSize } from "../utils/getFolderSize.js";
|
|
17
18
|
import { getClient } from "../utils/index.js";
|
|
18
19
|
import { launchBrowserAndWaitForSDK } from "../utils/launchBrowserAndWaitForSDK.js";
|
|
@@ -32,7 +33,7 @@ const buildAssetId = async (srcDir, src, basename2) => {
|
|
|
32
33
|
if (!info) {
|
|
33
34
|
throw new Error(`SyncStatus info is not found for ${syncStatus.infoPath}`);
|
|
34
35
|
}
|
|
35
|
-
return info.
|
|
36
|
+
return info.id;
|
|
36
37
|
};
|
|
37
38
|
program.command("render [directory]").description(
|
|
38
39
|
"Render a directory's index.html file as a video in the editframe cloud"
|
|
@@ -152,7 +153,12 @@ program.command("render [directory]").description(
|
|
|
152
153
|
const readable = new PassThrough();
|
|
153
154
|
tarStream.pipe(readable);
|
|
154
155
|
const folderSize = await getFolderSize(distDir);
|
|
155
|
-
await uploadRender(
|
|
156
|
+
await uploadRender(
|
|
157
|
+
getClient(),
|
|
158
|
+
render.id,
|
|
159
|
+
createReadableStreamFromReadable(readable),
|
|
160
|
+
folderSize
|
|
161
|
+
);
|
|
156
162
|
process.stderr.write("Render assets uploaded\n");
|
|
157
163
|
process.stderr.write(inspect(render));
|
|
158
164
|
process.stderr.write("\n");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import debug from "debug";
|
|
1
2
|
import { SyncCaption } from "./SyncCaption.js";
|
|
2
3
|
import { SyncFragmentIndex } from "./SyncFragmentIndex.js";
|
|
3
4
|
import { SyncImage } from "./SyncImage.js";
|
|
@@ -6,7 +7,9 @@ const trackMatch = /\.track-[\d]+.mp4$/i;
|
|
|
6
7
|
const fragmentIndexMatch = /\.tracks.json$/i;
|
|
7
8
|
const captionsMatch = /\.captions.json$/i;
|
|
8
9
|
const imageMatch = /\.(png|jpe?g|gif|webp)$/i;
|
|
10
|
+
const log = debug("ef:SubAssetSync");
|
|
9
11
|
const getAssetSync = (subAssetPath, md5) => {
|
|
12
|
+
log("getAssetSync", { subAssetPath, md5 });
|
|
10
13
|
if (imageMatch.test(subAssetPath)) {
|
|
11
14
|
return new SyncImage(subAssetPath, md5);
|
|
12
15
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateCaptionFileResult } from '../../../../api/src/index.ts';
|
|
1
|
+
import { CreateCaptionFileResult, LookupCaptionFileByMd5Result } from '../../../../api/src/index.ts';
|
|
2
2
|
import { SubAssetSync } from './SubAssetSync.ts';
|
|
3
3
|
import { SyncStatus } from './SyncStatus.ts';
|
|
4
4
|
export declare class SyncCaption implements SubAssetSync<CreateCaptionFileResult> {
|
|
@@ -7,7 +7,7 @@ export declare class SyncCaption implements SubAssetSync<CreateCaptionFileResult
|
|
|
7
7
|
icon: string;
|
|
8
8
|
label: string;
|
|
9
9
|
syncStatus: SyncStatus;
|
|
10
|
-
created: CreateCaptionFileResult | null;
|
|
10
|
+
created: CreateCaptionFileResult | LookupCaptionFileByMd5Result | null;
|
|
11
11
|
constructor(path: string, md5: string);
|
|
12
12
|
byteSize(): Promise<number>;
|
|
13
13
|
prepare(): Promise<void>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
-
import { createCaptionFile, uploadCaptionFile } from "@editframe/api";
|
|
2
|
+
import { lookupCaptionFileByMd5, createCaptionFile, uploadCaptionFile } from "@editframe/api";
|
|
3
3
|
import { Readable } from "node:stream";
|
|
4
4
|
import { getClient } from "../../utils/index.js";
|
|
5
|
+
import { basename } from "node:path";
|
|
6
|
+
import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
|
|
5
7
|
import { SyncStatus } from "./SyncStatus.js";
|
|
6
8
|
class SyncCaption {
|
|
7
9
|
constructor(path, md5) {
|
|
@@ -20,11 +22,19 @@ class SyncCaption {
|
|
|
20
22
|
async validate() {
|
|
21
23
|
}
|
|
22
24
|
async create() {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const maybeCaptionFile = await lookupCaptionFileByMd5(
|
|
26
|
+
getClient(),
|
|
27
|
+
this.md5
|
|
28
|
+
);
|
|
29
|
+
if (maybeCaptionFile) {
|
|
30
|
+
this.created = maybeCaptionFile;
|
|
31
|
+
} else {
|
|
32
|
+
this.created = await createCaptionFile(getClient(), {
|
|
33
|
+
md5: this.md5,
|
|
34
|
+
filename: basename(this.path).replace(/\.captions.json$/, ""),
|
|
35
|
+
byte_size: await this.byteSize()
|
|
36
|
+
});
|
|
37
|
+
}
|
|
28
38
|
}
|
|
29
39
|
isComplete() {
|
|
30
40
|
return !!this.created?.complete;
|
|
@@ -40,7 +50,9 @@ class SyncCaption {
|
|
|
40
50
|
this.created.id,
|
|
41
51
|
// It's not clear why we need to use Readable.from here, but it seems
|
|
42
52
|
// to fix an issue where the request is closed early in tests
|
|
43
|
-
|
|
53
|
+
createReadableStreamFromReadable(
|
|
54
|
+
Readable.from(await fs.readFile(this.path))
|
|
55
|
+
),
|
|
44
56
|
await this.byteSize()
|
|
45
57
|
);
|
|
46
58
|
}
|
|
@@ -56,7 +68,6 @@ class SyncCaption {
|
|
|
56
68
|
complete: true,
|
|
57
69
|
id: this.created.id,
|
|
58
70
|
md5: this.md5,
|
|
59
|
-
asset_id: this.created.asset_id,
|
|
60
71
|
byte_size: byteSize
|
|
61
72
|
});
|
|
62
73
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateISOBMFFFileResult } from '../../../../api/src/index.ts';
|
|
1
|
+
import { CreateISOBMFFFileResult, LookupISOBMFFFileByMd5Result } from '../../../../api/src/index.ts';
|
|
2
2
|
import { SubAssetSync } from './SubAssetSync.ts';
|
|
3
3
|
import { SyncStatus } from './SyncStatus.ts';
|
|
4
4
|
export declare class SyncFragmentIndex implements SubAssetSync<CreateISOBMFFFileResult> {
|
|
@@ -8,7 +8,7 @@ export declare class SyncFragmentIndex implements SubAssetSync<CreateISOBMFFFile
|
|
|
8
8
|
label: string;
|
|
9
9
|
syncStatus: SyncStatus;
|
|
10
10
|
fileSyncStatus: SyncStatus;
|
|
11
|
-
created: CreateISOBMFFFileResult | null;
|
|
11
|
+
created: CreateISOBMFFFileResult | LookupISOBMFFFileByMd5Result | null;
|
|
12
12
|
constructor(path: string, md5: string);
|
|
13
13
|
byteSize(): Promise<number>;
|
|
14
14
|
prepare(): Promise<void>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
-
import { join, dirname } from "node:path";
|
|
2
|
+
import { join, dirname, basename } from "node:path";
|
|
3
3
|
import { Readable } from "node:stream";
|
|
4
|
-
import { createISOBMFFFile, uploadFragmentIndex } from "@editframe/api";
|
|
4
|
+
import { lookupISOBMFFFileByMd5, createISOBMFFFile, uploadFragmentIndex } from "@editframe/api";
|
|
5
|
+
import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
|
|
5
6
|
import { getClient } from "../../utils/index.js";
|
|
6
7
|
import { SyncStatus } from "./SyncStatus.js";
|
|
7
8
|
class SyncFragmentIndex {
|
|
@@ -11,9 +12,7 @@ class SyncFragmentIndex {
|
|
|
11
12
|
this.icon = "📋";
|
|
12
13
|
this.label = "fragment index";
|
|
13
14
|
this.syncStatus = new SyncStatus(this.path);
|
|
14
|
-
this.fileSyncStatus = new SyncStatus(
|
|
15
|
-
join(dirname(this.path), "isobmff")
|
|
16
|
-
);
|
|
15
|
+
this.fileSyncStatus = new SyncStatus(join(dirname(this.path), "isobmff"));
|
|
17
16
|
this.created = null;
|
|
18
17
|
}
|
|
19
18
|
async byteSize() {
|
|
@@ -24,10 +23,18 @@ class SyncFragmentIndex {
|
|
|
24
23
|
async validate() {
|
|
25
24
|
}
|
|
26
25
|
async create() {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
const maybeISOBMFFFile = await lookupISOBMFFFileByMd5(
|
|
27
|
+
getClient(),
|
|
28
|
+
this.md5
|
|
29
|
+
);
|
|
30
|
+
if (maybeISOBMFFFile) {
|
|
31
|
+
this.created = maybeISOBMFFFile;
|
|
32
|
+
} else {
|
|
33
|
+
this.created = await createISOBMFFFile(getClient(), {
|
|
34
|
+
md5: this.md5,
|
|
35
|
+
filename: basename(this.path).replace(/\.tracks.json$/, "")
|
|
36
|
+
});
|
|
37
|
+
}
|
|
31
38
|
}
|
|
32
39
|
isComplete() {
|
|
33
40
|
return !!this.created?.fragment_index_complete;
|
|
@@ -43,7 +50,9 @@ class SyncFragmentIndex {
|
|
|
43
50
|
this.created.id,
|
|
44
51
|
// It is unclear why we need to use Readable.from here
|
|
45
52
|
// Tests fail when using createReadStream
|
|
46
|
-
|
|
53
|
+
createReadableStreamFromReadable(
|
|
54
|
+
Readable.from(await fs.readFile(this.path))
|
|
55
|
+
),
|
|
47
56
|
await this.byteSize()
|
|
48
57
|
);
|
|
49
58
|
}
|
|
@@ -60,7 +69,6 @@ class SyncFragmentIndex {
|
|
|
60
69
|
complete: true,
|
|
61
70
|
id: this.created.id,
|
|
62
71
|
md5: this.md5,
|
|
63
|
-
asset_id: this.created.asset_id,
|
|
64
72
|
byte_size: byteSize
|
|
65
73
|
}),
|
|
66
74
|
this.fileSyncStatus.markSynced({
|
|
@@ -68,7 +76,6 @@ class SyncFragmentIndex {
|
|
|
68
76
|
complete: true,
|
|
69
77
|
id: this.created.id,
|
|
70
78
|
md5: this.md5,
|
|
71
|
-
asset_id: this.created.asset_id,
|
|
72
79
|
byte_size: byteSize
|
|
73
80
|
})
|
|
74
81
|
]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateImageFileResult } from '../../../../api/src/index.ts';
|
|
1
|
+
import { CreateImageFileResult, LookupImageFileByMd5Result } from '../../../../api/src/index.ts';
|
|
2
2
|
import { Probe } from '../../../../assets/src/index.ts';
|
|
3
3
|
import { SubAssetSync } from './SubAssetSync.ts';
|
|
4
4
|
import { SyncStatus } from './SyncStatus.ts';
|
|
@@ -8,7 +8,7 @@ export declare class SyncImage implements SubAssetSync<CreateImageFileResult> {
|
|
|
8
8
|
icon: string;
|
|
9
9
|
label: string;
|
|
10
10
|
syncStatus: SyncStatus;
|
|
11
|
-
created: CreateImageFileResult | null;
|
|
11
|
+
created: CreateImageFileResult | LookupImageFileByMd5Result | null;
|
|
12
12
|
constructor(path: string, md5: string);
|
|
13
13
|
private _probeResult;
|
|
14
14
|
prepare(): Promise<void>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createReadStream } from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path, { basename } from "node:path";
|
|
4
|
-
import { createImageFile, uploadImageFile } from "@editframe/api";
|
|
4
|
+
import { lookupImageFileByMd5, createImageFile, uploadImageFile } from "@editframe/api";
|
|
5
5
|
import { Probe } from "@editframe/assets";
|
|
6
|
+
import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
|
|
6
7
|
import { getClient } from "../../utils/index.js";
|
|
7
8
|
import { SyncStatus } from "./SyncStatus.js";
|
|
8
9
|
class SyncImage {
|
|
@@ -48,14 +49,19 @@ class SyncImage {
|
|
|
48
49
|
"No video stream found in image. Should have been prevented by .validate()"
|
|
49
50
|
);
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
const maybeImageFile = await lookupImageFileByMd5(getClient(), this.md5);
|
|
53
|
+
if (maybeImageFile) {
|
|
54
|
+
this.created = maybeImageFile;
|
|
55
|
+
} else {
|
|
56
|
+
this.created = await createImageFile(getClient(), {
|
|
57
|
+
md5: this.md5,
|
|
58
|
+
filename: basename(this.path),
|
|
59
|
+
width: videoProbe.width,
|
|
60
|
+
height: videoProbe.height,
|
|
61
|
+
mime_type: `image/${this.extension}`,
|
|
62
|
+
byte_size: byteSize
|
|
63
|
+
});
|
|
64
|
+
}
|
|
59
65
|
}
|
|
60
66
|
isComplete() {
|
|
61
67
|
return !!this.created?.complete;
|
|
@@ -69,7 +75,7 @@ class SyncImage {
|
|
|
69
75
|
await uploadImageFile(
|
|
70
76
|
getClient(),
|
|
71
77
|
this.created.id,
|
|
72
|
-
createReadStream(this.path),
|
|
78
|
+
createReadableStreamFromReadable(createReadStream(this.path)),
|
|
73
79
|
Number.parseInt(this.probeResult.format.size || "0")
|
|
74
80
|
).whenUploaded();
|
|
75
81
|
}
|
|
@@ -85,7 +91,6 @@ class SyncImage {
|
|
|
85
91
|
complete: true,
|
|
86
92
|
id: this.created.id,
|
|
87
93
|
md5: this.md5,
|
|
88
|
-
asset_id: this.created.asset_id,
|
|
89
94
|
byte_size: byteSize
|
|
90
95
|
});
|
|
91
96
|
}
|
|
@@ -4,37 +4,33 @@ declare const SyncStatusSchema: z.ZodObject<{
|
|
|
4
4
|
complete: z.ZodBoolean;
|
|
5
5
|
id: z.ZodString;
|
|
6
6
|
md5: z.ZodString;
|
|
7
|
-
asset_id: z.ZodString;
|
|
8
7
|
byte_size: z.ZodNumber;
|
|
9
8
|
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
complete: boolean;
|
|
10
|
+
id: string;
|
|
10
11
|
md5: string;
|
|
11
12
|
byte_size: number;
|
|
12
|
-
complete: boolean;
|
|
13
13
|
version: string;
|
|
14
|
-
id: string;
|
|
15
|
-
asset_id: string;
|
|
16
14
|
}, {
|
|
15
|
+
complete: boolean;
|
|
16
|
+
id: string;
|
|
17
17
|
md5: string;
|
|
18
18
|
byte_size: number;
|
|
19
|
-
complete: boolean;
|
|
20
19
|
version: string;
|
|
21
|
-
id: string;
|
|
22
|
-
asset_id: string;
|
|
23
20
|
}>;
|
|
24
21
|
export interface SyncStatusInfo extends z.infer<typeof SyncStatusSchema> {
|
|
25
22
|
}
|
|
26
23
|
export declare class SyncStatus {
|
|
27
24
|
private basePath;
|
|
28
|
-
constructor(basePath: string);
|
|
29
25
|
infoPath: string;
|
|
26
|
+
constructor(basePath: string);
|
|
30
27
|
isSynced(): Promise<boolean>;
|
|
31
28
|
readInfo(): Promise<{
|
|
29
|
+
complete: boolean;
|
|
30
|
+
id: string;
|
|
32
31
|
md5: string;
|
|
33
32
|
byte_size: number;
|
|
34
|
-
complete: boolean;
|
|
35
33
|
version: string;
|
|
36
|
-
id: string;
|
|
37
|
-
asset_id: string;
|
|
38
34
|
} | null>;
|
|
39
35
|
markSynced(info: SyncStatusInfo): Promise<void>;
|
|
40
36
|
}
|
|
@@ -6,7 +6,6 @@ const SyncStatusSchema = z.object({
|
|
|
6
6
|
complete: z.boolean(),
|
|
7
7
|
id: z.string(),
|
|
8
8
|
md5: z.string(),
|
|
9
|
-
asset_id: z.string(),
|
|
10
9
|
byte_size: z.number()
|
|
11
10
|
});
|
|
12
11
|
class SyncStatus {
|
|
@@ -19,6 +18,7 @@ class SyncStatus {
|
|
|
19
18
|
if (!syncInfo) {
|
|
20
19
|
return false;
|
|
21
20
|
}
|
|
21
|
+
console.log("syncInfo.infoPath", this.infoPath);
|
|
22
22
|
return syncInfo.version === SYNC_VERSION && syncInfo.complete;
|
|
23
23
|
}
|
|
24
24
|
async readInfo() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateISOBMFFFileResult, CreateISOBMFFTrackResult } from '../../../../api/src/index.ts';
|
|
1
|
+
import { CreateISOBMFFFileResult, CreateISOBMFFTrackResult, LookupISOBMFFFileByMd5Result } from '../../../../api/src/index.ts';
|
|
2
2
|
import { Probe } from '../../../../assets/src/index.ts';
|
|
3
3
|
import { SubAssetSync } from './SubAssetSync.ts';
|
|
4
4
|
import { SyncStatus } from './SyncStatus.ts';
|
|
@@ -12,7 +12,7 @@ export declare class SyncTrack implements SubAssetSync<CreateISOBMFFTrackResult>
|
|
|
12
12
|
created: CreateISOBMFFTrackResult | null;
|
|
13
13
|
constructor(path: string, md5: string);
|
|
14
14
|
private _isoFile;
|
|
15
|
-
get isoFile(): CreateISOBMFFFileResult;
|
|
15
|
+
get isoFile(): CreateISOBMFFFileResult | LookupISOBMFFFileByMd5Result;
|
|
16
16
|
byteSize(): Promise<number>;
|
|
17
17
|
private _probeResult;
|
|
18
18
|
get probeResult(): Probe;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createReadStream } from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
-
import { join, dirname } from "node:path";
|
|
4
|
-
import { createISOBMFFFile, createISOBMFFTrack, uploadISOBMFFTrack } from "@editframe/api";
|
|
3
|
+
import { join, dirname, basename } from "node:path";
|
|
4
|
+
import { lookupISOBMFFFileByMd5, createISOBMFFFile, createISOBMFFTrack, uploadISOBMFFTrack } from "@editframe/api";
|
|
5
5
|
import { Probe } from "@editframe/assets";
|
|
6
|
+
import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
|
|
6
7
|
import { getClient } from "../../utils/index.js";
|
|
7
8
|
import { SyncStatus } from "./SyncStatus.js";
|
|
8
9
|
class SyncTrack {
|
|
@@ -12,9 +13,7 @@ class SyncTrack {
|
|
|
12
13
|
this.icon = "📼";
|
|
13
14
|
this.label = "track";
|
|
14
15
|
this.syncStatus = new SyncStatus(this.path);
|
|
15
|
-
this.fileSyncStatus = new SyncStatus(
|
|
16
|
-
join(dirname(this.path), "isobmff")
|
|
17
|
-
);
|
|
16
|
+
this.fileSyncStatus = new SyncStatus(join(dirname(this.path), "isobmff"));
|
|
18
17
|
this.created = null;
|
|
19
18
|
this._isoFile = null;
|
|
20
19
|
this._probeResult = null;
|
|
@@ -42,10 +41,15 @@ class SyncTrack {
|
|
|
42
41
|
throw new Error(`No track found in track: ${this.path}`);
|
|
43
42
|
}
|
|
44
43
|
async prepare() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
44
|
+
const maybeIsoFile = await lookupISOBMFFFileByMd5(getClient(), this.md5);
|
|
45
|
+
if (maybeIsoFile) {
|
|
46
|
+
this._isoFile = maybeIsoFile;
|
|
47
|
+
} else {
|
|
48
|
+
this._isoFile = await createISOBMFFFile(getClient(), {
|
|
49
|
+
md5: this.md5,
|
|
50
|
+
filename: basename(this.path).replace(/\.track-[\d]+.mp4$/, "")
|
|
51
|
+
});
|
|
52
|
+
}
|
|
49
53
|
this._probeResult = await Probe.probePath(this.path);
|
|
50
54
|
}
|
|
51
55
|
get trackId() {
|
|
@@ -102,7 +106,7 @@ class SyncTrack {
|
|
|
102
106
|
getClient(),
|
|
103
107
|
this.isoFile.id,
|
|
104
108
|
Number(this.trackId),
|
|
105
|
-
createReadStream(this.path),
|
|
109
|
+
createReadableStreamFromReadable(createReadStream(this.path)),
|
|
106
110
|
this.created?.byte_size
|
|
107
111
|
).whenUploaded();
|
|
108
112
|
}
|
|
@@ -119,7 +123,6 @@ class SyncTrack {
|
|
|
119
123
|
complete: true,
|
|
120
124
|
id: `${this.created.file_id}:${this.created.track_id}`,
|
|
121
125
|
md5: this.md5,
|
|
122
|
-
asset_id: this.created.asset_id,
|
|
123
126
|
byte_size: byteSize
|
|
124
127
|
}),
|
|
125
128
|
this.fileSyncStatus.markSynced({
|
|
@@ -127,7 +130,6 @@ class SyncTrack {
|
|
|
127
130
|
complete: true,
|
|
128
131
|
id: this.created.file_id,
|
|
129
132
|
md5: this.md5,
|
|
130
|
-
asset_id: this.created.asset_id,
|
|
131
133
|
byte_size: byteSize
|
|
132
134
|
})
|
|
133
135
|
]);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Stream } from "node:stream";
|
|
2
|
+
const createReadableStreamFromReadable = (source) => {
|
|
3
|
+
const pump = new StreamPump(source);
|
|
4
|
+
const stream = new ReadableStream(pump, pump);
|
|
5
|
+
return stream;
|
|
6
|
+
};
|
|
7
|
+
class StreamPump {
|
|
8
|
+
constructor(stream) {
|
|
9
|
+
this.highWaterMark = stream.readableHighWaterMark || new Stream.Readable().readableHighWaterMark;
|
|
10
|
+
this.accumalatedSize = 0;
|
|
11
|
+
this.stream = stream;
|
|
12
|
+
this.enqueue = this.enqueue.bind(this);
|
|
13
|
+
this.error = this.error.bind(this);
|
|
14
|
+
this.close = this.close.bind(this);
|
|
15
|
+
}
|
|
16
|
+
size(chunk) {
|
|
17
|
+
return chunk?.byteLength || 0;
|
|
18
|
+
}
|
|
19
|
+
start(controller) {
|
|
20
|
+
this.controller = controller;
|
|
21
|
+
this.stream.on("data", this.enqueue);
|
|
22
|
+
this.stream.once("error", this.error);
|
|
23
|
+
this.stream.once("end", this.close);
|
|
24
|
+
this.stream.once("close", this.close);
|
|
25
|
+
}
|
|
26
|
+
pull() {
|
|
27
|
+
this.resume();
|
|
28
|
+
}
|
|
29
|
+
cancel(reason) {
|
|
30
|
+
if (this.stream.destroy) {
|
|
31
|
+
this.stream.destroy(reason);
|
|
32
|
+
}
|
|
33
|
+
this.stream.off("data", this.enqueue);
|
|
34
|
+
this.stream.off("error", this.error);
|
|
35
|
+
this.stream.off("end", this.close);
|
|
36
|
+
this.stream.off("close", this.close);
|
|
37
|
+
}
|
|
38
|
+
enqueue(chunk) {
|
|
39
|
+
if (this.controller) {
|
|
40
|
+
try {
|
|
41
|
+
const bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk);
|
|
42
|
+
const available = (this.controller.desiredSize || 0) - bytes.byteLength;
|
|
43
|
+
this.controller.enqueue(bytes);
|
|
44
|
+
if (available <= 0) {
|
|
45
|
+
this.pause();
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
this.controller.error(
|
|
49
|
+
new Error(
|
|
50
|
+
"Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object"
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
this.cancel();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
pause() {
|
|
58
|
+
if (this.stream.pause) {
|
|
59
|
+
this.stream.pause();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
resume() {
|
|
63
|
+
if (this.stream.readable && this.stream.resume) {
|
|
64
|
+
this.stream.resume();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
close() {
|
|
68
|
+
if (this.controller) {
|
|
69
|
+
this.controller.close();
|
|
70
|
+
delete this.controller;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
error(error) {
|
|
74
|
+
if (this.controller) {
|
|
75
|
+
this.controller.error(error);
|
|
76
|
+
delete this.controller;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
createReadableStreamFromReadable
|
|
82
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { chromium } from "playwright";
|
|
3
2
|
import debug from "debug";
|
|
3
|
+
import { chromium } from "playwright";
|
|
4
4
|
import { withSpinner } from "./withSpinner.js";
|
|
5
5
|
const browserLog = debug("ef:cli::browser");
|
|
6
6
|
async function launchBrowserAndWaitForSDK(options, fn) {
|
|
@@ -12,7 +12,7 @@ async function launchBrowserAndWaitForSDK(options, fn) {
|
|
|
12
12
|
devtools: options.interactive === true
|
|
13
13
|
});
|
|
14
14
|
});
|
|
15
|
-
const page = await withSpinner("Loading
|
|
15
|
+
const page = await withSpinner("Loading Editframe SDK", async () => {
|
|
16
16
|
const pageOptions = {};
|
|
17
17
|
if (options.interactive === true) {
|
|
18
18
|
pageOptions.viewport = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@editframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0-beta.1",
|
|
4
4
|
"description": "Command line interface for EditFrame",
|
|
5
5
|
"bin": {
|
|
6
6
|
"editframe": "./dist/index.js"
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"vite-tsconfig-paths": "^4.3.2"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@editframe/api": "0.
|
|
27
|
-
"@editframe/assets": "0.
|
|
28
|
-
"@editframe/elements": "0.
|
|
29
|
-
"@editframe/vite-plugin": "0.
|
|
26
|
+
"@editframe/api": "0.12.0-beta.1",
|
|
27
|
+
"@editframe/assets": "0.12.0-beta.1",
|
|
28
|
+
"@editframe/elements": "0.12.0-beta.1",
|
|
29
|
+
"@editframe/vite-plugin": "0.12.0-beta.1",
|
|
30
30
|
"@inquirer/prompts": "^5.3.8",
|
|
31
31
|
"axios": "^1.6.8",
|
|
32
32
|
"chalk": "^5.3.0",
|