@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.
- 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
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const syncAssetDirectory: (
|
|
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 {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const
|
|
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 ${
|
|
13
|
+
console.error(`No assets cache directory found at ${cacheDir}`);
|
|
47
14
|
return;
|
|
48
15
|
}
|
|
49
|
-
const assets = await fs.readdir(
|
|
50
|
-
process.stderr.write(`Syncing asset dir: ${
|
|
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(
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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>;
|
package/dist/utils/index.js
CHANGED
|
@@ -5,7 +5,10 @@ let client;
|
|
|
5
5
|
const getClient = () => {
|
|
6
6
|
if (!client) {
|
|
7
7
|
const programOpts = program.opts();
|
|
8
|
-
client = new Client(
|
|
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.
|
|
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.
|
|
27
|
-
"@editframe/assets": "0.10.0-beta.
|
|
28
|
-
"@editframe/elements": "0.10.0-beta.
|
|
29
|
-
"@editframe/vite-plugin": "0.10.0-beta.
|
|
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",
|