@editframe/cli 0.16.8-beta.0 → 0.17.6-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.
Files changed (36) hide show
  1. package/dist/VERSION.d.ts +1 -1
  2. package/dist/VERSION.js +2 -4
  3. package/dist/commands/auth.js +16 -25
  4. package/dist/commands/check.js +81 -108
  5. package/dist/commands/mux.d.ts +1 -0
  6. package/dist/commands/preview.js +4 -1
  7. package/dist/commands/process-file.js +18 -34
  8. package/dist/commands/process.js +28 -31
  9. package/dist/commands/render.js +117 -157
  10. package/dist/commands/sync.js +3 -5
  11. package/dist/commands/webhook.js +48 -52
  12. package/dist/index.js +3 -7
  13. package/dist/operations/processRenderInfo.js +25 -31
  14. package/dist/operations/syncAssetsDirectory/SubAssetSync.js +11 -18
  15. package/dist/operations/syncAssetsDirectory/SyncCaption.js +46 -73
  16. package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.js +53 -83
  17. package/dist/operations/syncAssetsDirectory/SyncImage.js +72 -99
  18. package/dist/operations/syncAssetsDirectory/SyncStatus.js +30 -37
  19. package/dist/operations/syncAssetsDirectory/SyncTrack.js +107 -143
  20. package/dist/operations/syncAssetsDirectory/doAssetSync.js +42 -46
  21. package/dist/operations/syncAssetsDirectory.js +55 -78
  22. package/dist/utils/createReadableStreamFromReadable.js +61 -78
  23. package/dist/utils/index.js +10 -16
  24. package/dist/utils/launchBrowserAndWaitForSDK.js +31 -43
  25. package/dist/utils/startDevServer.d.ts +1 -1
  26. package/dist/utils/startPreviewServer.d.ts +1 -1
  27. package/dist/utils/startPreviewServer.js +28 -34
  28. package/dist/utils/validateVideoResolution.js +19 -23
  29. package/dist/utils/withSpinner.js +10 -12
  30. package/package.json +8 -8
  31. package/src/commands/check.ts +2 -2
  32. package/src/commands/mux.ts +10 -0
  33. package/src/utils/createReadableStreamFromReadable.ts +3 -7
  34. package/src/utils/startDevServer.ts +5 -5
  35. package/src/utils/startPreviewServer.ts +1 -1
  36. package/test-fixtures/network.ts +38 -9
@@ -1,77 +1,50 @@
1
- import fs from "node:fs/promises";
2
- import { lookupCaptionFileByMd5, createCaptionFile, uploadCaptionFile } from "@editframe/api";
3
- import { Readable } from "node:stream";
4
1
  import { getClient } from "../../utils/index.js";
5
- import { basename } from "node:path";
6
2
  import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
7
3
  import { SyncStatus } from "./SyncStatus.js";
8
- class SyncCaption {
9
- constructor(path, md5) {
10
- this.path = path;
11
- this.md5 = md5;
12
- this.icon = "📝";
13
- this.label = "captions";
14
- this.syncStatus = new SyncStatus(this.path);
15
- this.created = null;
16
- }
17
- async byteSize() {
18
- return (await fs.stat(this.path)).size;
19
- }
20
- async prepare() {
21
- }
22
- async validate() {
23
- }
24
- async create() {
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
- }
38
- }
39
- isComplete() {
40
- return !!this.created?.complete;
41
- }
42
- async upload() {
43
- if (!this.created) {
44
- throw new Error(
45
- "Caption not created. Should have been prevented by .isComplete()"
46
- );
47
- }
48
- await uploadCaptionFile(
49
- getClient(),
50
- this.created.id,
51
- // It's not clear why we need to use Readable.from here, but it seems
52
- // to fix an issue where the request is closed early in tests
53
- createReadableStreamFromReadable(
54
- Readable.from(await fs.readFile(this.path))
55
- ),
56
- await this.byteSize()
57
- );
58
- }
59
- async markSynced() {
60
- if (!this.created) {
61
- throw new Error(
62
- "Caption not created. Should have been prevented by .isComplete()"
63
- );
64
- }
65
- const byteSize = await this.byteSize();
66
- await this.syncStatus.markSynced({
67
- version: "1",
68
- complete: true,
69
- id: this.created.id,
70
- md5: this.md5,
71
- byte_size: byteSize
72
- });
73
- }
74
- }
75
- export {
76
- SyncCaption
4
+ import { createCaptionFile, lookupCaptionFileByMd5, uploadCaptionFile } from "@editframe/api";
5
+ import { basename } from "node:path";
6
+ import fs from "node:fs/promises";
7
+ import { Readable } from "node:stream";
8
+ var SyncCaption = class {
9
+ constructor(path$1, md5) {
10
+ this.path = path$1;
11
+ this.md5 = md5;
12
+ this.icon = "📝";
13
+ this.label = "captions";
14
+ this.syncStatus = new SyncStatus(this.path);
15
+ this.created = null;
16
+ }
17
+ async byteSize() {
18
+ return (await fs.stat(this.path)).size;
19
+ }
20
+ async prepare() {}
21
+ async validate() {}
22
+ async create() {
23
+ const maybeCaptionFile = await lookupCaptionFileByMd5(getClient(), this.md5);
24
+ if (maybeCaptionFile) this.created = maybeCaptionFile;
25
+ else this.created = await createCaptionFile(getClient(), {
26
+ md5: this.md5,
27
+ filename: basename(this.path).replace(/\.captions.json$/, ""),
28
+ byte_size: await this.byteSize()
29
+ });
30
+ }
31
+ isComplete() {
32
+ return !!this.created?.complete;
33
+ }
34
+ async upload() {
35
+ if (!this.created) throw new Error("Caption not created. Should have been prevented by .isComplete()");
36
+ await uploadCaptionFile(getClient(), this.created.id, createReadableStreamFromReadable(Readable.from(await fs.readFile(this.path))), await this.byteSize());
37
+ }
38
+ async markSynced() {
39
+ if (!this.created) throw new Error("Caption not created. Should have been prevented by .isComplete()");
40
+ const byteSize = await this.byteSize();
41
+ await this.syncStatus.markSynced({
42
+ version: "1",
43
+ complete: true,
44
+ id: this.created.id,
45
+ md5: this.md5,
46
+ byte_size: byteSize
47
+ });
48
+ }
77
49
  };
50
+ export { SyncCaption };
@@ -1,86 +1,56 @@
1
- import fs from "node:fs/promises";
2
- import { join, dirname, basename } from "node:path";
3
- import { Readable } from "node:stream";
4
- import { lookupISOBMFFFileByMd5, createISOBMFFFile, uploadFragmentIndex } from "@editframe/api";
5
- import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
6
1
  import { getClient } from "../../utils/index.js";
2
+ import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
7
3
  import { SyncStatus } from "./SyncStatus.js";
8
- class SyncFragmentIndex {
9
- constructor(path, md5) {
10
- this.path = path;
11
- this.md5 = md5;
12
- this.icon = "📋";
13
- this.label = "fragment index";
14
- this.syncStatus = new SyncStatus(this.path);
15
- this.fileSyncStatus = new SyncStatus(join(dirname(this.path), "isobmff"));
16
- this.created = null;
17
- }
18
- async byteSize() {
19
- return (await fs.stat(this.path)).size;
20
- }
21
- async prepare() {
22
- }
23
- async validate() {
24
- }
25
- async create() {
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
- }
38
- }
39
- isComplete() {
40
- return !!this.created?.fragment_index_complete;
41
- }
42
- async upload() {
43
- if (!this.created) {
44
- throw new Error(
45
- "Fragment index not created. Should have been prevented by .isComplete()"
46
- );
47
- }
48
- await uploadFragmentIndex(
49
- getClient(),
50
- this.created.id,
51
- // It is unclear why we need to use Readable.from here
52
- // Tests fail when using createReadStream
53
- createReadableStreamFromReadable(
54
- Readable.from(await fs.readFile(this.path))
55
- ),
56
- await this.byteSize()
57
- );
58
- }
59
- async markSynced() {
60
- if (!this.created) {
61
- throw new Error(
62
- "Fragment index not created. Should have been prevented by .isComplete()"
63
- );
64
- }
65
- const byteSize = await this.byteSize();
66
- await Promise.all([
67
- this.syncStatus.markSynced({
68
- version: "1",
69
- complete: true,
70
- id: this.created.id,
71
- md5: this.md5,
72
- byte_size: byteSize
73
- }),
74
- this.fileSyncStatus.markSynced({
75
- version: "1",
76
- complete: true,
77
- id: this.created.id,
78
- md5: this.md5,
79
- byte_size: byteSize
80
- })
81
- ]);
82
- }
83
- }
84
- export {
85
- SyncFragmentIndex
4
+ import { createISOBMFFFile, lookupISOBMFFFileByMd5, uploadFragmentIndex } from "@editframe/api";
5
+ import { basename, dirname, join } from "node:path";
6
+ import fs from "node:fs/promises";
7
+ import { Readable } from "node:stream";
8
+ var SyncFragmentIndex = class {
9
+ constructor(path$1, md5) {
10
+ this.path = path$1;
11
+ this.md5 = md5;
12
+ this.icon = "📋";
13
+ this.label = "fragment index";
14
+ this.syncStatus = new SyncStatus(this.path);
15
+ this.fileSyncStatus = new SyncStatus(join(dirname(this.path), "isobmff"));
16
+ this.created = null;
17
+ }
18
+ async byteSize() {
19
+ return (await fs.stat(this.path)).size;
20
+ }
21
+ async prepare() {}
22
+ async validate() {}
23
+ async create() {
24
+ const maybeISOBMFFFile = await lookupISOBMFFFileByMd5(getClient(), this.md5);
25
+ if (maybeISOBMFFFile) this.created = maybeISOBMFFFile;
26
+ else this.created = await createISOBMFFFile(getClient(), {
27
+ md5: this.md5,
28
+ filename: basename(this.path).replace(/\.tracks.json$/, "")
29
+ });
30
+ }
31
+ isComplete() {
32
+ return !!this.created?.fragment_index_complete;
33
+ }
34
+ async upload() {
35
+ if (!this.created) throw new Error("Fragment index not created. Should have been prevented by .isComplete()");
36
+ await uploadFragmentIndex(getClient(), this.created.id, createReadableStreamFromReadable(Readable.from(await fs.readFile(this.path))), await this.byteSize());
37
+ }
38
+ async markSynced() {
39
+ if (!this.created) throw new Error("Fragment index not created. Should have been prevented by .isComplete()");
40
+ const byteSize = await this.byteSize();
41
+ await Promise.all([this.syncStatus.markSynced({
42
+ version: "1",
43
+ complete: true,
44
+ id: this.created.id,
45
+ md5: this.md5,
46
+ byte_size: byteSize
47
+ }), this.fileSyncStatus.markSynced({
48
+ version: "1",
49
+ complete: true,
50
+ id: this.created.id,
51
+ md5: this.md5,
52
+ byte_size: byteSize
53
+ })]);
54
+ }
86
55
  };
56
+ export { SyncFragmentIndex };
@@ -1,102 +1,75 @@
1
- import { createReadStream } from "node:fs";
2
- import fs from "node:fs/promises";
3
- import path, { basename } from "node:path";
4
- import { lookupImageFileByMd5, createImageFile, uploadImageFile } from "@editframe/api";
5
- import { Probe } from "@editframe/assets";
6
- import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
7
1
  import { getClient } from "../../utils/index.js";
2
+ import { createReadableStreamFromReadable } from "../../utils/createReadableStreamFromReadable.js";
8
3
  import { SyncStatus } from "./SyncStatus.js";
9
- class SyncImage {
10
- constructor(path2, md5) {
11
- this.path = path2;
12
- this.md5 = md5;
13
- this.icon = "🖼️";
14
- this.label = "image";
15
- this.syncStatus = new SyncStatus(this.path);
16
- this.created = null;
17
- this._probeResult = null;
18
- }
19
- async prepare() {
20
- this._probeResult = await Probe.probePath(this.path);
21
- }
22
- get probeResult() {
23
- if (!this._probeResult) {
24
- throw new Error("Probe result not found. Call prepare() first.");
25
- }
26
- return this._probeResult;
27
- }
28
- get extension() {
29
- return path.extname(this.path).slice(1);
30
- }
31
- async byteSize() {
32
- return (await fs.stat(this.path)).size;
33
- }
34
- async validate() {
35
- const [videoProbe] = this.probeResult.videoStreams;
36
- if (!videoProbe) {
37
- throw new Error(`No media info found in image: ${this.path}`);
38
- }
39
- const ext = this.extension;
40
- if (!(ext === "jpg" || ext === "jpeg" || ext === "png" || ext === "webp")) {
41
- throw new Error(`Invalid image format: ${this.path}`);
42
- }
43
- }
44
- async create() {
45
- const byteSize = (await fs.stat(this.path)).size;
46
- const [videoProbe] = this.probeResult.videoStreams;
47
- if (!videoProbe) {
48
- throw new Error(
49
- "No video stream found in image. Should have been prevented by .validate()"
50
- );
51
- }
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
- }
65
- }
66
- isComplete() {
67
- return !!this.created?.complete;
68
- }
69
- async upload() {
70
- if (!this.created) {
71
- throw new Error(
72
- "Image not created. Should have been prevented by .isComplete()"
73
- );
74
- }
75
- await uploadImageFile(
76
- getClient(),
77
- {
78
- id: this.created.id,
79
- byte_size: Number.parseInt(this.probeResult.format.size || "0")
80
- },
81
- createReadableStreamFromReadable(createReadStream(this.path))
82
- ).whenUploaded();
83
- }
84
- async markSynced() {
85
- if (!this.created) {
86
- throw new Error(
87
- "Image not created. Should have been prevented by .isComplete()"
88
- );
89
- }
90
- const byteSize = await this.byteSize();
91
- return this.syncStatus.markSynced({
92
- version: "1",
93
- complete: true,
94
- id: this.created.id,
95
- md5: this.md5,
96
- byte_size: byteSize
97
- });
98
- }
99
- }
100
- export {
101
- SyncImage
4
+ import { createImageFile, lookupImageFileByMd5, uploadImageFile } from "@editframe/api";
5
+ import path, { basename } from "node:path";
6
+ import fs from "node:fs/promises";
7
+ import { createReadStream } from "node:fs";
8
+ import { Probe } from "@editframe/assets";
9
+ var SyncImage = class {
10
+ constructor(path$1, md5) {
11
+ this.path = path$1;
12
+ this.md5 = md5;
13
+ this.icon = "🖼️";
14
+ this.label = "image";
15
+ this.syncStatus = new SyncStatus(this.path);
16
+ this.created = null;
17
+ this._probeResult = null;
18
+ }
19
+ async prepare() {
20
+ this._probeResult = await Probe.probePath(this.path);
21
+ }
22
+ get probeResult() {
23
+ if (!this._probeResult) throw new Error("Probe result not found. Call prepare() first.");
24
+ return this._probeResult;
25
+ }
26
+ get extension() {
27
+ return path.extname(this.path).slice(1);
28
+ }
29
+ async byteSize() {
30
+ return (await fs.stat(this.path)).size;
31
+ }
32
+ async validate() {
33
+ const [videoProbe] = this.probeResult.videoStreams;
34
+ if (!videoProbe) throw new Error(`No media info found in image: ${this.path}`);
35
+ const ext = this.extension;
36
+ if (!(ext === "jpg" || ext === "jpeg" || ext === "png" || ext === "webp")) throw new Error(`Invalid image format: ${this.path}`);
37
+ }
38
+ async create() {
39
+ const byteSize = (await fs.stat(this.path)).size;
40
+ const [videoProbe] = this.probeResult.videoStreams;
41
+ if (!videoProbe) throw new Error("No video stream found in image. Should have been prevented by .validate()");
42
+ const maybeImageFile = await lookupImageFileByMd5(getClient(), this.md5);
43
+ if (maybeImageFile) this.created = maybeImageFile;
44
+ else this.created = await createImageFile(getClient(), {
45
+ md5: this.md5,
46
+ filename: basename(this.path),
47
+ width: videoProbe.width,
48
+ height: videoProbe.height,
49
+ mime_type: `image/${this.extension}`,
50
+ byte_size: byteSize
51
+ });
52
+ }
53
+ isComplete() {
54
+ return !!this.created?.complete;
55
+ }
56
+ async upload() {
57
+ if (!this.created) throw new Error("Image not created. Should have been prevented by .isComplete()");
58
+ await uploadImageFile(getClient(), {
59
+ id: this.created.id,
60
+ byte_size: Number.parseInt(this.probeResult.format.size || "0")
61
+ }, createReadableStreamFromReadable(createReadStream(this.path))).whenUploaded();
62
+ }
63
+ async markSynced() {
64
+ if (!this.created) throw new Error("Image not created. Should have been prevented by .isComplete()");
65
+ const byteSize = await this.byteSize();
66
+ return this.syncStatus.markSynced({
67
+ version: "1",
68
+ complete: true,
69
+ id: this.created.id,
70
+ md5: this.md5,
71
+ byte_size: byteSize
72
+ });
73
+ }
102
74
  };
75
+ export { SyncImage };
@@ -2,42 +2,35 @@ import fs from "node:fs/promises";
2
2
  import { z } from "zod";
3
3
  const SYNC_VERSION = "1";
4
4
  const SyncStatusSchema = z.object({
5
- version: z.string(),
6
- complete: z.boolean(),
7
- id: z.string(),
8
- md5: z.string(),
9
- byte_size: z.number()
5
+ version: z.string(),
6
+ complete: z.boolean(),
7
+ id: z.string(),
8
+ md5: z.string(),
9
+ byte_size: z.number()
10
10
  });
11
- class SyncStatus {
12
- constructor(basePath) {
13
- this.basePath = basePath;
14
- this.infoPath = `${this.basePath}.info`;
15
- }
16
- async isSynced() {
17
- const syncInfo = await this.readInfo();
18
- if (!syncInfo) {
19
- return false;
20
- }
21
- console.log("syncInfo.infoPath", this.infoPath);
22
- return syncInfo.version === SYNC_VERSION && syncInfo.complete;
23
- }
24
- async readInfo() {
25
- try {
26
- const info = await fs.readFile(this.infoPath, "utf-8");
27
- return SyncStatusSchema.parse(JSON.parse(info));
28
- } catch (error) {
29
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
30
- return null;
31
- }
32
- throw error;
33
- }
34
- }
35
- async markSynced(info) {
36
- process.stderr.write(`✏️ Marking asset as synced: ${this.basePath}
37
- `);
38
- await fs.writeFile(this.infoPath, JSON.stringify(info, null, 2), "utf-8");
39
- }
40
- }
41
- export {
42
- SyncStatus
11
+ var SyncStatus = class {
12
+ constructor(basePath) {
13
+ this.basePath = basePath;
14
+ this.infoPath = `${this.basePath}.info`;
15
+ }
16
+ async isSynced() {
17
+ const syncInfo = await this.readInfo();
18
+ if (!syncInfo) return false;
19
+ console.log("syncInfo.infoPath", this.infoPath);
20
+ return syncInfo.version === SYNC_VERSION && syncInfo.complete;
21
+ }
22
+ async readInfo() {
23
+ try {
24
+ const info = await fs.readFile(this.infoPath, "utf-8");
25
+ return SyncStatusSchema.parse(JSON.parse(info));
26
+ } catch (error) {
27
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
28
+ throw error;
29
+ }
30
+ }
31
+ async markSynced(info) {
32
+ process.stderr.write(`✏️ Marking asset as synced: ${this.basePath}\n`);
33
+ await fs.writeFile(this.infoPath, JSON.stringify(info, null, 2), "utf-8");
34
+ }
43
35
  };
36
+ export { SyncStatus };