@editframe/cli 0.10.0-beta.5 → 0.10.0-beta.7

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 (53) hide show
  1. package/dist/VERSION.d.ts +1 -1
  2. package/dist/VERSION.js +1 -1
  3. package/dist/commands/render.d.ts +1 -1
  4. package/dist/commands/render.js +55 -43
  5. package/dist/commands/sync.js +5 -2
  6. package/dist/operations/syncAssetsDirectory/SubAssetSync.d.ts +20 -0
  7. package/dist/operations/syncAssetsDirectory/SubAssetSync.js +26 -0
  8. package/dist/operations/syncAssetsDirectory/SyncCaption.d.ts +19 -0
  9. package/dist/operations/syncAssetsDirectory/SyncCaption.js +66 -0
  10. package/dist/operations/syncAssetsDirectory/SyncCaption.test.d.ts +1 -0
  11. package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.d.ts +20 -0
  12. package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.js +79 -0
  13. package/dist/operations/syncAssetsDirectory/SyncFragmentIndex.test.d.ts +1 -0
  14. package/dist/operations/syncAssetsDirectory/SyncImage.d.ts +23 -0
  15. package/dist/operations/syncAssetsDirectory/SyncImage.js +95 -0
  16. package/dist/operations/syncAssetsDirectory/SyncImage.test.d.ts +1 -0
  17. package/dist/operations/syncAssetsDirectory/SyncStatus.d.ts +41 -0
  18. package/dist/operations/syncAssetsDirectory/SyncStatus.js +43 -0
  19. package/dist/operations/syncAssetsDirectory/SyncTrack.d.ts +70 -0
  20. package/dist/operations/syncAssetsDirectory/SyncTrack.js +138 -0
  21. package/dist/operations/syncAssetsDirectory/SyncTrack.test.d.ts +1 -0
  22. package/dist/operations/syncAssetsDirectory/doAssetSync.d.ts +5 -0
  23. package/dist/operations/syncAssetsDirectory/doAssetSync.js +48 -0
  24. package/dist/operations/syncAssetsDirectory/doAssetSync.test.d.ts +1 -0
  25. package/dist/operations/syncAssetsDirectory.d.ts +1 -1
  26. package/dist/operations/syncAssetsDirectory.js +20 -240
  27. package/dist/test-fixtures/fixture.d.ts +26 -0
  28. package/dist/utils/index.js +4 -1
  29. package/package.json +5 -5
  30. package/src/commands/render.ts +61 -52
  31. package/src/commands/sync.ts +5 -2
  32. package/src/operations/syncAssetsDirectory/SubAssetSync.ts +42 -0
  33. package/src/operations/syncAssetsDirectory/SyncCaption.test.ts +145 -0
  34. package/src/operations/syncAssetsDirectory/SyncCaption.ts +76 -0
  35. package/src/operations/syncAssetsDirectory/SyncFragmentIndex.test.ts +151 -0
  36. package/src/operations/syncAssetsDirectory/SyncFragmentIndex.ts +92 -0
  37. package/src/operations/syncAssetsDirectory/SyncImage.test.ts +131 -0
  38. package/src/operations/syncAssetsDirectory/SyncImage.ts +112 -0
  39. package/src/operations/syncAssetsDirectory/SyncStatus.ts +51 -0
  40. package/src/operations/syncAssetsDirectory/SyncTrack.test.ts +222 -0
  41. package/src/operations/syncAssetsDirectory/SyncTrack.ts +164 -0
  42. package/src/operations/syncAssetsDirectory/doAssetSync.test.ts +134 -0
  43. package/src/operations/syncAssetsDirectory/doAssetSync.ts +62 -0
  44. package/src/operations/syncAssetsDirectory.test.ts +482 -0
  45. package/src/operations/syncAssetsDirectory.ts +22 -283
  46. package/src/utils/index.ts +4 -1
  47. package/test-fixtures/fixture.ts +141 -0
  48. package/test-fixtures/network.ts +181 -0
  49. package/test-fixtures/test-captions.json +9 -0
  50. package/test-fixtures/test.mp4 +0 -0
  51. package/test-fixtures/test.png +0 -0
  52. package/src/commands/render.test.ts +0 -34
  53. /package/dist/{commands/render.test.d.ts → operations/syncAssetsDirectory.test.d.ts} +0 -0
@@ -0,0 +1,70 @@
1
+ import { CreateISOBMFFFileResult, CreateISOBMFFTrackResult } from '../../../../api/src';
2
+ import { Probe } from '../../../../assets/src';
3
+ import { SubAssetSync } from './SubAssetSync.ts';
4
+ import { SyncStatus } from './SyncStatus.ts';
5
+ export declare class SyncTrack implements SubAssetSync<CreateISOBMFFTrackResult> {
6
+ path: string;
7
+ md5: string;
8
+ icon: string;
9
+ label: string;
10
+ syncStatus: SyncStatus;
11
+ fileSyncStatus: SyncStatus;
12
+ created: CreateISOBMFFTrackResult | null;
13
+ constructor(path: string, md5: string);
14
+ private _isoFile;
15
+ get isoFile(): CreateISOBMFFFileResult;
16
+ byteSize(): Promise<number>;
17
+ private _probeResult;
18
+ get probeResult(): Probe;
19
+ get track(): {
20
+ index: number;
21
+ codec_name: string;
22
+ codec_long_name: string;
23
+ codec_type: "audio";
24
+ codec_tag_string: string;
25
+ codec_tag: string;
26
+ sample_fmt: string;
27
+ sample_rate: string;
28
+ channels: number;
29
+ channel_layout: string;
30
+ bits_per_sample: number;
31
+ r_frame_rate: string;
32
+ avg_frame_rate: string;
33
+ time_base: string;
34
+ start_pts: number;
35
+ start_time: number;
36
+ duration_ts: number;
37
+ duration: number;
38
+ bit_rate: string;
39
+ disposition: Record<string, unknown>;
40
+ initial_padding?: number | undefined;
41
+ } | {
42
+ height: number;
43
+ width: number;
44
+ index: number;
45
+ codec_name: string;
46
+ codec_long_name: string;
47
+ codec_type: "video";
48
+ codec_tag_string: string;
49
+ codec_tag: string;
50
+ r_frame_rate: string;
51
+ avg_frame_rate: string;
52
+ time_base: string;
53
+ disposition: Record<string, unknown>;
54
+ coded_width: number;
55
+ coded_height: number;
56
+ start_pts?: number | undefined;
57
+ start_time?: number | undefined;
58
+ duration_ts?: number | undefined;
59
+ duration?: number | undefined;
60
+ bit_rate?: string | undefined;
61
+ };
62
+ prepare(): Promise<void>;
63
+ get trackId(): string;
64
+ get trackDuration(): number;
65
+ validate(): Promise<void>;
66
+ create(): Promise<void>;
67
+ isComplete(): boolean;
68
+ upload(): Promise<void>;
69
+ markSynced(): Promise<void>;
70
+ }
@@ -0,0 +1,138 @@
1
+ import { createReadStream } from "node:fs";
2
+ import fs from "node:fs/promises";
3
+ import { join, dirname } from "node:path";
4
+ import { createISOBMFFFile, createISOBMFFTrack, uploadISOBMFFTrack } from "@editframe/api";
5
+ import { Probe } from "@editframe/assets";
6
+ import { getClient } from "../../utils/index.js";
7
+ import { SyncStatus } from "./SyncStatus.js";
8
+ class SyncTrack {
9
+ constructor(path, md5) {
10
+ this.path = path;
11
+ this.md5 = md5;
12
+ this.icon = "📼";
13
+ this.label = "track";
14
+ this.syncStatus = new SyncStatus(this.path);
15
+ this.fileSyncStatus = new SyncStatus(
16
+ join(dirname(this.path), "isobmff")
17
+ );
18
+ this.created = null;
19
+ this._isoFile = null;
20
+ this._probeResult = null;
21
+ }
22
+ get isoFile() {
23
+ if (this._isoFile) {
24
+ return this._isoFile;
25
+ }
26
+ throw new Error("ISOBMFF file not found. Call prepare() first.");
27
+ }
28
+ async byteSize() {
29
+ return (await fs.stat(this.path)).size;
30
+ }
31
+ get probeResult() {
32
+ if (this._probeResult) {
33
+ return this._probeResult;
34
+ }
35
+ throw new Error("Probe result not found. Call prepare() first.");
36
+ }
37
+ get track() {
38
+ const [track] = this.probeResult.streams;
39
+ if (track) {
40
+ return track;
41
+ }
42
+ throw new Error(`No track found in track: ${this.path}`);
43
+ }
44
+ async prepare() {
45
+ this._isoFile = await createISOBMFFFile(getClient(), {
46
+ md5: this.md5,
47
+ filename: this.path.replace(/\.track-[\d]+.mp4$/, "")
48
+ });
49
+ this._probeResult = await Probe.probePath(this.path);
50
+ }
51
+ get trackId() {
52
+ const trackId = this.path.match(/track-([\d]+).mp4/)?.[1];
53
+ if (!trackId) {
54
+ throw new Error(`No track ID found for track: ${this.path}`);
55
+ }
56
+ return trackId;
57
+ }
58
+ get trackDuration() {
59
+ const track = this.track;
60
+ if (!track.duration) {
61
+ throw new Error(`No duration found in track: ${this.path}`);
62
+ }
63
+ return track.duration;
64
+ }
65
+ async validate() {
66
+ this.trackId;
67
+ this.isoFile;
68
+ this.trackDuration;
69
+ }
70
+ async create() {
71
+ const track = this.track;
72
+ const isoFile = this.isoFile;
73
+ const createPayload = track.codec_type === "audio" ? {
74
+ type: track.codec_type,
75
+ file_id: isoFile.id,
76
+ track_id: Number(this.trackId),
77
+ probe_info: track,
78
+ duration_ms: Math.round(this.trackDuration * 1e3),
79
+ codec_name: track.codec_name,
80
+ byte_size: await this.byteSize()
81
+ } : {
82
+ type: track.codec_type,
83
+ file_id: isoFile.id,
84
+ track_id: Number(this.trackId),
85
+ probe_info: track,
86
+ duration_ms: Math.round(this.trackDuration * 1e3),
87
+ codec_name: track.codec_name,
88
+ byte_size: await this.byteSize()
89
+ };
90
+ this.created = await createISOBMFFTrack(getClient(), createPayload);
91
+ }
92
+ isComplete() {
93
+ return !!this.created?.complete;
94
+ }
95
+ async upload() {
96
+ if (!this.created) {
97
+ throw new Error(
98
+ "Track not created. Should have been prevented by .isComplete()"
99
+ );
100
+ }
101
+ return uploadISOBMFFTrack(
102
+ getClient(),
103
+ this.isoFile.id,
104
+ Number(this.trackId),
105
+ createReadStream(this.path),
106
+ this.created?.byte_size
107
+ );
108
+ }
109
+ async markSynced() {
110
+ if (!this.created) {
111
+ throw new Error(
112
+ "Track not created. Should have been prevented by .isComplete()"
113
+ );
114
+ }
115
+ const byteSize = await this.byteSize();
116
+ await Promise.all([
117
+ this.syncStatus.markSynced({
118
+ version: "1",
119
+ complete: true,
120
+ id: `${this.created.file_id}:${this.created.track_id}`,
121
+ md5: this.md5,
122
+ asset_id: this.created.asset_id,
123
+ byte_size: byteSize
124
+ }),
125
+ this.fileSyncStatus.markSynced({
126
+ version: "1",
127
+ complete: true,
128
+ id: this.created.file_id,
129
+ md5: this.md5,
130
+ asset_id: this.created.asset_id,
131
+ byte_size: byteSize
132
+ })
133
+ ]);
134
+ }
135
+ }
136
+ export {
137
+ SyncTrack
138
+ };
@@ -0,0 +1,5 @@
1
+ import { SubAssetSync } from './SubAssetSync.ts';
2
+ export declare const doAssetSync: (assetSync: SubAssetSync<unknown>) => AsyncGenerator<{
3
+ status: "info" | "success";
4
+ message: string;
5
+ }>;
@@ -0,0 +1,48 @@
1
+ const doAssetSync = async function* (assetSync) {
2
+ if (await assetSync.syncStatus.isSynced()) {
3
+ yield {
4
+ status: "info",
5
+ message: `Sub-asset has already been synced: ${assetSync.path}`
6
+ };
7
+ return;
8
+ }
9
+ try {
10
+ await assetSync.prepare();
11
+ await assetSync.validate();
12
+ } catch (error) {
13
+ const message = error instanceof Error ? error.message : "Unknown error";
14
+ throw new Error(`Error validating ${assetSync.label}: ${message}`);
15
+ }
16
+ yield {
17
+ status: "info",
18
+ message: `${assetSync.icon} Syncing ${assetSync.label}: ${assetSync.path}`
19
+ };
20
+ try {
21
+ await assetSync.create();
22
+ } catch (error) {
23
+ const message = error instanceof Error ? error.message : "Unknown error";
24
+ throw new Error(`Error creating ${assetSync.label}: ${message}`);
25
+ }
26
+ if (!assetSync.isComplete()) {
27
+ try {
28
+ await assetSync.upload();
29
+ } catch (error) {
30
+ const message = error instanceof Error ? error.message : "Unknown error";
31
+ throw new Error(`Error uploading ${assetSync.label}: ${message}`);
32
+ }
33
+ }
34
+ try {
35
+ await assetSync.markSynced();
36
+ } catch (error) {
37
+ const message = error instanceof Error ? error.message : "Unknown error";
38
+ throw new Error(`Error marking ${assetSync.label} as synced: ${message}`);
39
+ }
40
+ yield {
41
+ status: "success",
42
+ message: `Synced ${assetSync.label}: ${assetSync.path}`
43
+ };
44
+ return;
45
+ };
46
+ export {
47
+ doAssetSync
48
+ };
@@ -1 +1 @@
1
- export declare const syncAssetDirectory: (projectDirectory: string) => Promise<void>;
1
+ export declare const syncAssetDirectory: (cacheDir: string) => Promise<void>;
@@ -1,53 +1,20 @@
1
- import { Probe } from "@editframe/assets";
2
1
  import fs from "node:fs/promises";
3
2
  import path from "node:path";
4
- import { createCaptionFile, uploadCaptionFile, createISOBMFFFile, uploadFragmentIndex, createISOBMFFTrack, uploadISOBMFFTrack, createImageFile, uploadImageFile } from "@editframe/api";
5
- import { createReadStream, statSync } from "node:fs";
6
- import { getClient } from "../utils/index.js";
7
- const imageMatch = /\.(png|jpe?g|gif|webp)$/i;
8
- const trackMatch = /\.track-[\d]+.mp4$/i;
9
- const fragmentIndexMatch = /\.tracks.json$/i;
10
- const captionsMatch = /\.captions.json$/i;
11
- class SyncStatus {
12
- constructor(assetPath) {
13
- this.assetPath = assetPath;
14
- this.infoPath = `${this.assetPath}.info`;
15
- }
16
- async isSynced() {
17
- const result = await fs.stat(this.infoPath).catch((error) => {
18
- if (error.code === "ENOENT") {
19
- return false;
20
- }
21
- throw error;
22
- });
23
- return !!result;
24
- }
25
- async markSynced() {
26
- process.stderr.write(`✏️ Marking asset as synced: ${this.assetPath}
27
- `);
28
- await fs.writeFile(this.infoPath, "{}", "utf-8");
29
- }
30
- }
31
- const syncAssetDirectory = async (projectDirectory) => {
32
- const fullPath = path.join(
33
- process.cwd(),
34
- projectDirectory,
35
- "src",
36
- "assets",
37
- ".cache"
38
- );
39
- const stat = await fs.stat(fullPath).catch((error) => {
3
+ import { getAssetSync } from "./syncAssetsDirectory/SubAssetSync.js";
4
+ import { doAssetSync } from "./syncAssetsDirectory/doAssetSync.js";
5
+ const syncAssetDirectory = async (cacheDir) => {
6
+ const stat = await fs.stat(cacheDir).catch((error) => {
40
7
  if (error.code === "ENOENT") {
41
8
  return;
42
9
  }
43
10
  throw error;
44
11
  });
45
12
  if (!stat?.isDirectory()) {
46
- console.error(`No assets cache directory found at ${fullPath}`);
13
+ console.error(`No assets cache directory found at ${cacheDir}`);
47
14
  return;
48
15
  }
49
- const assets = await fs.readdir(fullPath);
50
- process.stderr.write(`Syncing asset dir: ${fullPath}
16
+ const assets = await fs.readdir(cacheDir);
17
+ process.stderr.write(`Syncing asset dir: ${cacheDir}
51
18
  `);
52
19
  const errors = {};
53
20
  const reportError = (path2, message) => {
@@ -66,7 +33,7 @@ const syncAssetDirectory = async (projectDirectory) => {
66
33
  };
67
34
  for (const assetMd5 of assets) {
68
35
  reportInfo(assetMd5, `Syncing asset: ${assetMd5}`);
69
- const assetDir = path.join(fullPath, assetMd5);
36
+ const assetDir = path.join(cacheDir, assetMd5);
70
37
  const stat2 = await fs.stat(assetDir);
71
38
  if (!stat2.isDirectory()) {
72
39
  reportError(assetMd5, "Invalid asset. Did not find asset directory.");
@@ -78,207 +45,20 @@ const syncAssetDirectory = async (projectDirectory) => {
78
45
  continue;
79
46
  }
80
47
  const subAssetPath = path.join(assetDir, subAsset);
81
- const syncStatus = new SyncStatus(subAssetPath);
82
- if (await syncStatus.isSynced()) {
83
- reportInfo(
84
- subAsset,
85
- `✔ Sub-asset has already been synced: ${subAsset}`
86
- );
87
- continue;
88
- }
89
- switch (true) {
90
- case imageMatch.test(subAsset): {
91
- const probeResult = await Probe.probePath(subAssetPath);
92
- const [videoProbe] = probeResult.videoStreams;
93
- const { format } = probeResult;
94
- if (!videoProbe) {
95
- reportError(subAsset, `No media info found in image: ${subAsset}`);
96
- break;
97
- }
98
- const ext = path.extname(subAsset).slice(1);
99
- if (!(ext === "jpg" || ext === "jpeg" || ext === "png" || ext === "webp")) {
100
- reportError(subAsset, `Invalid image format: ${subAsset}`);
101
- break;
102
- }
103
- reportInfo(subAsset, `🖼️ Syncing image: ${subAsset}`);
104
- const created = await createImageFile(getClient(), {
105
- md5: assetMd5,
106
- filename: subAsset,
107
- width: videoProbe.width,
108
- height: videoProbe.height,
109
- mime_type: `image/${ext}`,
110
- byte_size: (await fs.stat(subAssetPath)).size
111
- });
112
- if (created) {
113
- if (created.complete) {
114
- reportInfo(subAsset, " ✔ Image has already been synced");
115
- await syncStatus.markSynced();
116
- } else {
117
- await uploadImageFile(
118
- getClient(),
119
- created.id,
120
- createReadStream(subAssetPath),
121
- Number.parseInt(format.size || "0")
122
- ).then(() => {
123
- reportSuccess(subAsset, "Image has been synced.");
124
- return syncStatus.markSynced();
125
- }).catch((error) => {
126
- reportError(
127
- subAsset,
128
- `Error uploading image: ${error.message}`
129
- );
130
- });
131
- }
132
- }
133
- break;
134
- }
135
- case trackMatch.test(subAsset): {
136
- reportInfo(subAsset, `📼 Syncing a/v track: ${subAsset}`);
137
- const createdFile = await createISOBMFFFile(getClient(), {
138
- md5: assetMd5,
139
- filename: subAsset.replace(/\.track-[\d]+.mp4$/, "")
140
- });
141
- if (createdFile) {
142
- const probe = await Probe.probePath(subAssetPath);
143
- const trackId = subAsset.match(/track-([\d]+).mp4/)?.[1];
144
- if (!trackId) {
145
- reportError(subAsset, `No track ID found for track: ${subAsset}`);
146
- break;
147
- }
148
- const [track] = probe.streams;
149
- if (!track) {
150
- reportError(
151
- subAsset,
152
- `No track stream found in track: ${subAsset}`
153
- );
154
- break;
155
- }
156
- if (track.duration === void 0) {
157
- reportError(subAsset, `No duration found in track: ${subAsset}`);
158
- break;
159
- }
160
- const stat3 = await fs.stat(subAssetPath);
161
- const createPayload = track.codec_type === "audio" ? {
162
- type: track.codec_type,
163
- file_id: createdFile.id,
164
- track_id: Number(trackId),
165
- probe_info: track,
166
- duration_ms: Math.round(track.duration * 1e3),
167
- codec_name: track.codec_name,
168
- byte_size: stat3.size
169
- } : {
170
- type: track.codec_type,
171
- file_id: createdFile.id,
172
- track_id: Number(trackId),
173
- probe_info: track,
174
- duration_ms: Math.round(track.duration * 1e3),
175
- codec_name: track.codec_name,
176
- byte_size: stat3.size
177
- };
178
- const createdTrack = await createISOBMFFTrack(
179
- getClient(),
180
- createPayload
181
- );
182
- if (createdTrack) {
183
- if (createdTrack.next_byte === createdTrack.byte_size) {
184
- reportInfo(subAsset, "✔ Track has already been synced.");
185
- await syncStatus.markSynced();
186
- } else {
187
- await uploadISOBMFFTrack(
188
- getClient(),
189
- createdFile.id,
190
- Number(trackId),
191
- createReadStream(subAssetPath),
192
- createdTrack.byte_size
193
- ).then(() => {
194
- reportSuccess(subAsset, "Track has been synced.");
195
- return syncStatus.markSynced();
196
- }).catch((error) => {
197
- reportError(
198
- subAsset,
199
- `Error uploading track: ${error.message}`
200
- );
201
- });
202
- }
203
- }
204
- }
205
- break;
206
- }
207
- case fragmentIndexMatch.test(subAsset): {
208
- reportInfo(subAsset, `📋 Syncing fragment index: ${subAsset}`);
209
- const createdFile = await createISOBMFFFile(getClient(), {
210
- md5: assetMd5,
211
- filename: subAsset.replace(/\.tracks.json$/, "")
212
- });
213
- if (createdFile) {
214
- if (createdFile.fragment_index_complete) {
215
- reportInfo(subAsset, "✔ Fragment index has already been synced.");
216
- await syncStatus.markSynced();
217
- } else {
218
- const stats = statSync(subAssetPath);
219
- const readStream = createReadStream(subAssetPath);
220
- await uploadFragmentIndex(
221
- getClient(),
222
- createdFile.id,
223
- readStream,
224
- stats.size
225
- ).then(() => {
226
- reportSuccess(subAsset, "Fragment index has been synced.");
227
- return syncStatus.markSynced();
228
- }).catch((error) => {
229
- reportError(
230
- subAsset,
231
- `Error uploading fragment index: ${error.message}`
232
- );
233
- });
234
- }
235
- } else {
236
- reportError(
237
- subAsset,
238
- `No file found for fragment index: ${subAsset}`
239
- );
240
- break;
241
- }
242
- break;
243
- }
244
- case captionsMatch.test(subAsset): {
245
- reportInfo(subAsset, `📝 Syncing captions: ${subAsset}`);
246
- const createdFile = await createCaptionFile(getClient(), {
247
- md5: assetMd5,
248
- filename: subAsset.replace(/\.captions.json$/, ""),
249
- byte_size: (await fs.stat(subAsset)).size
250
- });
251
- if (createdFile) {
252
- if (createdFile.complete) {
253
- reportInfo(subAsset, "✔ Captions have already been synced.");
254
- await syncStatus.markSynced();
255
- } else {
256
- const readStream = createReadStream(subAssetPath);
257
- const stats = statSync(subAssetPath);
258
- await uploadCaptionFile(
259
- getClient(),
260
- createdFile.id,
261
- readStream,
262
- stats.size
263
- ).then(() => {
264
- reportSuccess(subAsset, "Captions have been synced.");
265
- return syncStatus.markSynced();
266
- }).catch((error) => {
267
- reportError(
268
- subAsset,
269
- `Error uploading captions: ${error.message}`
270
- );
271
- });
272
- }
273
- } else {
274
- reportError(subAsset, `No file found for captions: ${subAsset}`);
275
- break;
48
+ try {
49
+ const assetSync = getAssetSync(subAssetPath, assetMd5);
50
+ for await (const { status, message } of doAssetSync(assetSync)) {
51
+ if (status === "success") {
52
+ reportSuccess(subAsset, message);
53
+ } else if (status === "info") {
54
+ reportInfo(subAsset, message);
276
55
  }
277
- break;
278
56
  }
279
- default: {
280
- reportError(subAsset, `Unknown sub-asset: ${subAsset}`);
281
- break;
57
+ } catch (error) {
58
+ if (error instanceof Error) {
59
+ reportError(subAsset, error.message);
60
+ } else {
61
+ reportError(subAsset, "Unknown error");
282
62
  }
283
63
  }
284
64
  }
@@ -0,0 +1,26 @@
1
+ import { SyncStatusInfo } from '../src/operations/syncAssetsDirectory/SyncStatus.ts';
2
+ export declare const fixture: (fixture: string, name: string) => Promise<Fixture>;
3
+ export interface Fixture {
4
+ name: string;
5
+ fixture: string;
6
+ md5: string;
7
+ content: Buffer;
8
+ originalPath: string;
9
+ write: (dir: string) => Promise<void>;
10
+ }
11
+ export declare const withFixtures: (fixtures: Promise<Fixture>[], fn: (props: {
12
+ files: Fixture[];
13
+ rootDir: string;
14
+ srcDir: string;
15
+ assetsDir: string;
16
+ cacheDir: string;
17
+ expectCacheFiles: (fixture: Fixture, files: string[]) => Promise<void>;
18
+ expectInfoFileContent: (fileName: string, fixture: Fixture, expectedContent: Pick<SyncStatusInfo, "complete" | "id"> & {
19
+ byte_size?: number;
20
+ }) => Promise<void>;
21
+ syncAssetsDirectory: () => Promise<void>;
22
+ cacheImage: (fixture: Fixture) => Promise<string>;
23
+ generateTrack: (fixture: Fixture, trackId: number) => Promise<string>;
24
+ generateTrackFragmentIndex: (fixture: Fixture) => Promise<string>;
25
+ generateCaptions: (fixture: Fixture) => Promise<string>;
26
+ }) => Promise<void>) => Promise<void>;
@@ -5,7 +5,10 @@ let client;
5
5
  const getClient = () => {
6
6
  if (!client) {
7
7
  const programOpts = program.opts();
8
- client = new Client(programOpts.token, programOpts.efHost);
8
+ client = new Client(
9
+ programOpts.token || process.env.EF_TOKEN,
10
+ programOpts.efHost || process.env.EF_HOST
11
+ );
9
12
  }
10
13
  return client;
11
14
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/cli",
3
- "version": "0.10.0-beta.5",
3
+ "version": "0.10.0-beta.7",
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.10.0-beta.5",
27
- "@editframe/assets": "0.10.0-beta.5",
28
- "@editframe/elements": "0.10.0-beta.5",
29
- "@editframe/vite-plugin": "0.10.0-beta.5",
26
+ "@editframe/api": "0.10.0-beta.7",
27
+ "@editframe/assets": "0.10.0-beta.7",
28
+ "@editframe/elements": "0.10.0-beta.7",
29
+ "@editframe/vite-plugin": "0.10.0-beta.7",
30
30
  "@inquirer/prompts": "^5.3.8",
31
31
  "axios": "^1.6.8",
32
32
  "chalk": "^5.3.0",