@tomorrowos/sdk 0.4.2 → 0.4.4
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/playlist-catalog.d.ts +8 -0
- package/dist/playlist-catalog.d.ts.map +1 -1
- package/dist/playlist-catalog.js +28 -0
- package/dist/tomorrowos.d.ts.map +1 -1
- package/dist/tomorrowos.js +49 -2
- package/package.json +1 -1
- package/templates/cms-starter/package.json +2 -2
- package/templates/cms-starter/public/index.html +8 -0
- package/templates/cms-starter/public/methods.js +130 -17
- package/templates/cms-starter/public/panel.css +36 -0
|
@@ -32,6 +32,14 @@ export declare class PlaylistCatalog {
|
|
|
32
32
|
removePlaylistFromDevice(deviceId: string, playlistId: string): Promise<BuiltDevicePolicy>;
|
|
33
33
|
/** Remove every playlist assignment from a device (CMS catalog + empty policy). */
|
|
34
34
|
clearAllAssignmentsFromDevice(deviceId: string): Promise<BuiltDevicePolicy>;
|
|
35
|
+
/**
|
|
36
|
+
* If the device has this playlist assigned, refresh its snapshot/version to latest
|
|
37
|
+
* and return a rebuilt snapshot policy for immediate push.
|
|
38
|
+
*/
|
|
39
|
+
refreshPlaylistSnapshotOnDevice(deviceId: string, playlistId: string, options?: PolicyBuildOptions): Promise<{
|
|
40
|
+
updated: boolean;
|
|
41
|
+
built?: BuiltDevicePolicy;
|
|
42
|
+
}>;
|
|
35
43
|
buildPolicyForDevice(deviceId: string, options?: PolicyBuildOptions): Promise<BuiltDevicePolicy>;
|
|
36
44
|
private buildPolicyFromAssignments;
|
|
37
45
|
canPublishPlaylistToNewDevice(playlistId: string): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playlist-catalog.d.ts","sourceRoot":"","sources":["../src/playlist-catalog.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,wBAAwB,EACxB,kBAAkB,EAClB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE;QACN,SAAS,EAAE,yBAAyB,EAAE,CAAC;QACvC,QAAQ,EAAE;YAAE,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC;QAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;KAClC,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA+CD,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,eAAe;IAE7C,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK1C,6BAA6B,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAI1D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI5D,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;IAgC/D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAcnD,oBAAoB,CACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAIhC,wBAAwB,CAC5B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,EACrB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC;IA2CvB,wBAAwB,CAC5B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,iBAAiB,CAAC;IAO7B,mFAAmF;IAC7E,6BAA6B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"playlist-catalog.d.ts","sourceRoot":"","sources":["../src/playlist-catalog.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,wBAAwB,EACxB,kBAAkB,EAClB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE;QACN,SAAS,EAAE,yBAAyB,EAAE,CAAC;QACvC,QAAQ,EAAE;YAAE,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC;QAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;KAClC,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA+CD,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,eAAe;IAE7C,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK1C,6BAA6B,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAI1D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI5D,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;IAgC/D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAcnD,oBAAoB,CACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAIhC,wBAAwB,CAC5B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,EACrB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC;IA2CvB,wBAAwB,CAC5B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,iBAAiB,CAAC;IAO7B,mFAAmF;IAC7E,6BAA6B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAKjF;;;OAGG;IACG,+BAA+B,CACnC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,iBAAiB,CAAA;KAAE,CAAC;IA6BrD,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC;YAKf,0BAA0B;IAgCxC,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGpE"}
|
package/dist/playlist-catalog.js
CHANGED
|
@@ -148,6 +148,34 @@ export class PlaylistCatalog {
|
|
|
148
148
|
await this.store.setDeviceAssignments(deviceId, []);
|
|
149
149
|
return this.buildPolicyFromAssignments([], { useLatest: false });
|
|
150
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* If the device has this playlist assigned, refresh its snapshot/version to latest
|
|
153
|
+
* and return a rebuilt snapshot policy for immediate push.
|
|
154
|
+
*/
|
|
155
|
+
async refreshPlaylistSnapshotOnDevice(deviceId, playlistId, options = {}) {
|
|
156
|
+
const assignments = await this.store.getDeviceAssignments(deviceId);
|
|
157
|
+
const idx = assignments.findIndex((a) => a.playlistId === playlistId);
|
|
158
|
+
if (idx < 0)
|
|
159
|
+
return { updated: false };
|
|
160
|
+
const playlist = await this.store.getPlaylist(playlistId);
|
|
161
|
+
if (!playlist || playlist.retired) {
|
|
162
|
+
return { updated: false };
|
|
163
|
+
}
|
|
164
|
+
const next = assignments.map((a, i) => i !== idx
|
|
165
|
+
? a
|
|
166
|
+
: {
|
|
167
|
+
playlistId,
|
|
168
|
+
publishedVersion: playlist.version,
|
|
169
|
+
publishedAt: new Date().toISOString(),
|
|
170
|
+
snapshot: cloneSnapshot(playlist, options.mediaBaseUrl)
|
|
171
|
+
});
|
|
172
|
+
await this.store.setDeviceAssignments(deviceId, next);
|
|
173
|
+
const built = await this.buildPolicyFromAssignments(next, {
|
|
174
|
+
useLatest: false,
|
|
175
|
+
mediaBaseUrl: options.mediaBaseUrl
|
|
176
|
+
});
|
|
177
|
+
return { updated: true, built };
|
|
178
|
+
}
|
|
151
179
|
async buildPolicyForDevice(deviceId, options = {}) {
|
|
152
180
|
const assignments = await this.store.getDeviceAssignments(deviceId);
|
|
153
181
|
return this.buildPolicyFromAssignments(assignments, options);
|
package/dist/tomorrowos.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -604,7 +604,16 @@ export class TomorrowOS extends EventEmitter {
|
|
|
604
604
|
async tryServeStatic(pathname, res) {
|
|
605
605
|
if (!this.staticRoot)
|
|
606
606
|
return false;
|
|
607
|
-
|
|
607
|
+
let relRaw = pathname === "/" || pathname === "" ? this.staticIndexFile : pathname.slice(1);
|
|
608
|
+
let rel = relRaw;
|
|
609
|
+
try {
|
|
610
|
+
// Browser path segments are percent-encoded for spaces/non-ASCII.
|
|
611
|
+
// Decode before mapping to filesystem path.
|
|
612
|
+
rel = decodeURIComponent(relRaw);
|
|
613
|
+
}
|
|
614
|
+
catch {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
608
617
|
if (!rel || rel.includes(".."))
|
|
609
618
|
return false;
|
|
610
619
|
const rootResolved = path.resolve(this.staticRoot);
|
|
@@ -703,7 +712,45 @@ export class TomorrowOS extends EventEmitter {
|
|
|
703
712
|
: undefined,
|
|
704
713
|
items: Array.isArray(body.items) ? body.items : []
|
|
705
714
|
});
|
|
706
|
-
|
|
715
|
+
const paired = await this.store.listPairedDevices();
|
|
716
|
+
const deployResults = [];
|
|
717
|
+
for (const entry of paired) {
|
|
718
|
+
const deviceId = entry.deviceId;
|
|
719
|
+
try {
|
|
720
|
+
const refreshed = await this.playlists.refreshPlaylistSnapshotOnDevice(deviceId, saved.id);
|
|
721
|
+
if (!refreshed.updated || !refreshed.built) {
|
|
722
|
+
deployResults.push({ deviceId, assigned: false, pushed: false });
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
const ws = this.devices.get(deviceId);
|
|
726
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
727
|
+
deployResults.push({ deviceId, assigned: true, pushed: false });
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
await this.sendDeviceCommand(deviceId, "device.content.setPolicy", {
|
|
731
|
+
policy: refreshed.built.policy
|
|
732
|
+
});
|
|
733
|
+
await this.recordPolicyPush(deviceId);
|
|
734
|
+
deployResults.push({ deviceId, assigned: true, pushed: true });
|
|
735
|
+
}
|
|
736
|
+
catch (err) {
|
|
737
|
+
deployResults.push({
|
|
738
|
+
deviceId,
|
|
739
|
+
assigned: true,
|
|
740
|
+
pushed: false,
|
|
741
|
+
error: err instanceof Error ? err.message : "push failed"
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
sendJson(res, 200, {
|
|
746
|
+
status: "success",
|
|
747
|
+
playlist: saved,
|
|
748
|
+
deploy: {
|
|
749
|
+
assignedDevices: deployResults.filter((x) => x.assigned).length,
|
|
750
|
+
pushedDevices: deployResults.filter((x) => x.pushed).length,
|
|
751
|
+
results: deployResults
|
|
752
|
+
}
|
|
753
|
+
});
|
|
707
754
|
}
|
|
708
755
|
catch (e) {
|
|
709
756
|
const msg = e instanceof Error ? e.message : "Save failed";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "TomorrowOS CMS server SDK — WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
|
|
5
5
|
"private": true,
|
|
6
6
|
"type": "module",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"build-player": "tomorrowos build --platform tizen"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@tomorrowos/sdk": "^0.4.
|
|
13
|
+
"@tomorrowos/sdk": "^0.4.4"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^20.0.0",
|
|
@@ -116,7 +116,15 @@
|
|
|
116
116
|
id="fileInput"
|
|
117
117
|
class="hidden"
|
|
118
118
|
accept="image/*,video/*,.wgt,.zip"
|
|
119
|
+
multiple
|
|
119
120
|
/>
|
|
121
|
+
<div id="uploadStatusShell" class="upload-status hidden" aria-live="polite">
|
|
122
|
+
<div id="uploadStatusText" class="upload-status-text">Uploading...</div>
|
|
123
|
+
<div class="upload-progress-track">
|
|
124
|
+
<div id="uploadProgressBar" class="upload-progress-bar"></div>
|
|
125
|
+
</div>
|
|
126
|
+
<div id="uploadQueueText" class="upload-queue-text"></div>
|
|
127
|
+
</div>
|
|
120
128
|
<ul id="playlistList" class="playlist-list">
|
|
121
129
|
<li class="playlist-empty" id="playlistEmpty">Select or create a playlist, then add assets.</li>
|
|
122
130
|
</ul>
|
|
@@ -19,6 +19,11 @@ let editorItems = [];
|
|
|
19
19
|
let publishModalDeviceId = null;
|
|
20
20
|
|
|
21
21
|
let devicePollTimer = null;
|
|
22
|
+
let uploadQueue = [];
|
|
23
|
+
let uploadInProgress = false;
|
|
24
|
+
|
|
25
|
+
const UPLOAD_MAX_RETRIES = 3;
|
|
26
|
+
const UPLOAD_TIMEOUT_MS = 120000;
|
|
22
27
|
|
|
23
28
|
function escapeHtml(value) {
|
|
24
29
|
return String(value ?? "")
|
|
@@ -71,6 +76,34 @@ function showResult(data) {
|
|
|
71
76
|
document.getElementById("result").textContent = JSON.stringify(data, null, 2);
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
function setAssetUploadBusy(isBusy) {
|
|
80
|
+
const addBtn = document.getElementById("addAssetBtn");
|
|
81
|
+
if (addBtn) {
|
|
82
|
+
addBtn.disabled = isBusy;
|
|
83
|
+
addBtn.textContent = isBusy ? "Uploading..." : "+";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function updateUploadStatusUi(status) {
|
|
88
|
+
const shell = document.getElementById("uploadStatusShell");
|
|
89
|
+
const text = document.getElementById("uploadStatusText");
|
|
90
|
+
const bar = document.getElementById("uploadProgressBar");
|
|
91
|
+
const queue = document.getElementById("uploadQueueText");
|
|
92
|
+
if (!shell || !text || !bar || !queue) return;
|
|
93
|
+
|
|
94
|
+
if (!status || status.hidden) {
|
|
95
|
+
shell.classList.add("hidden");
|
|
96
|
+
bar.style.width = "0%";
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
shell.classList.remove("hidden");
|
|
101
|
+
text.textContent = status.text || "Uploading...";
|
|
102
|
+
queue.textContent = status.queueText || "";
|
|
103
|
+
const percent = Math.max(0, Math.min(100, Number(status.percent) || 0));
|
|
104
|
+
bar.style.width = `${percent}%`;
|
|
105
|
+
}
|
|
106
|
+
|
|
74
107
|
function isLocalPanelHost(hostname) {
|
|
75
108
|
const h = String(hostname || "").toLowerCase();
|
|
76
109
|
return h === "localhost" || h === "127.0.0.1" || h === "[::1]";
|
|
@@ -846,26 +879,68 @@ async function removePlaylistFromDevice(deviceId, playlistId) {
|
|
|
846
879
|
await fetchDevices();
|
|
847
880
|
}
|
|
848
881
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
882
|
+
function uploadFileWithProgress(file, { onProgress, attempt }) {
|
|
883
|
+
return new Promise((resolve, reject) => {
|
|
884
|
+
const xhr = new XMLHttpRequest();
|
|
885
|
+
const q = new URLSearchParams({ filename: file.name });
|
|
886
|
+
xhr.open("POST", `/media/upload?${q.toString()}`, true);
|
|
887
|
+
xhr.timeout = UPLOAD_TIMEOUT_MS;
|
|
888
|
+
xhr.setRequestHeader("Content-Type", "application/octet-stream");
|
|
889
|
+
|
|
890
|
+
xhr.upload.onprogress = (event) => {
|
|
891
|
+
if (!event.lengthComputable) return;
|
|
892
|
+
const percent = Math.round((event.loaded / event.total) * 100);
|
|
893
|
+
if (typeof onProgress === "function") onProgress(percent);
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
xhr.onerror = () => reject(new Error(`Network error on attempt ${attempt}`));
|
|
897
|
+
xhr.ontimeout = () => reject(new Error(`Upload timed out after ${UPLOAD_TIMEOUT_MS / 1000}s`));
|
|
898
|
+
|
|
899
|
+
xhr.onload = () => {
|
|
900
|
+
let payload = {};
|
|
901
|
+
try {
|
|
902
|
+
payload = xhr.responseText ? JSON.parse(xhr.responseText) : {};
|
|
903
|
+
} catch {
|
|
904
|
+
payload = {};
|
|
905
|
+
}
|
|
906
|
+
if (xhr.status >= 200 && xhr.status < 300 && payload.status !== "failed") {
|
|
907
|
+
resolve(payload);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
reject(new Error(payload.error || `Upload failed (${xhr.status})`));
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
xhr.send(file);
|
|
855
914
|
});
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
async function uploadFile(file, onProgress) {
|
|
918
|
+
let lastErr = null;
|
|
919
|
+
for (let attempt = 1; attempt <= UPLOAD_MAX_RETRIES; attempt += 1) {
|
|
920
|
+
try {
|
|
921
|
+
const payload = await uploadFileWithProgress(file, { onProgress, attempt });
|
|
922
|
+
return payload;
|
|
923
|
+
} catch (err) {
|
|
924
|
+
lastErr = err;
|
|
925
|
+
if (attempt >= UPLOAD_MAX_RETRIES) break;
|
|
926
|
+
await new Promise((resolve) => setTimeout(resolve, 500 * attempt));
|
|
927
|
+
}
|
|
859
928
|
}
|
|
860
|
-
|
|
929
|
+
throw lastErr || new Error("Upload failed");
|
|
861
930
|
}
|
|
862
931
|
|
|
863
|
-
async function addAssetFromFile(file) {
|
|
932
|
+
async function addAssetFromFile(file, queueIndex, queueTotal) {
|
|
864
933
|
if (!isPlaylistEditorOpen()) {
|
|
865
934
|
newPlaylistDraft();
|
|
866
935
|
}
|
|
867
936
|
const type = inferMediaType(file.name, file.type);
|
|
868
|
-
const data = await uploadFile(file)
|
|
937
|
+
const data = await uploadFile(file, (percent) => {
|
|
938
|
+
updateUploadStatusUi({
|
|
939
|
+
text: `Uploading ${file.name} (${percent}%)`,
|
|
940
|
+
percent,
|
|
941
|
+
queueText: `File ${queueIndex}/${queueTotal}`
|
|
942
|
+
});
|
|
943
|
+
});
|
|
869
944
|
const durationMs = await resolveVideoDurationMs(file, type, data);
|
|
870
945
|
editorItems.push({
|
|
871
946
|
id: crypto.randomUUID(),
|
|
@@ -878,11 +953,51 @@ async function addAssetFromFile(file) {
|
|
|
878
953
|
showResult({
|
|
879
954
|
status: "uploaded",
|
|
880
955
|
...data,
|
|
956
|
+
fileName: file.name,
|
|
881
957
|
detectedDurationMs: durationMs,
|
|
882
958
|
detectedDurationSec: Math.round(durationMs / 1000)
|
|
883
959
|
});
|
|
884
960
|
}
|
|
885
961
|
|
|
962
|
+
async function processUploadQueue() {
|
|
963
|
+
if (uploadInProgress) return;
|
|
964
|
+
if (uploadQueue.length === 0) {
|
|
965
|
+
updateUploadStatusUi({ hidden: true });
|
|
966
|
+
setAssetUploadBusy(false);
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
uploadInProgress = true;
|
|
971
|
+
setAssetUploadBusy(true);
|
|
972
|
+
const total = uploadQueue.length;
|
|
973
|
+
const failures = [];
|
|
974
|
+
|
|
975
|
+
try {
|
|
976
|
+
for (let i = 0; i < total; i += 1) {
|
|
977
|
+
const file = uploadQueue[i];
|
|
978
|
+
try {
|
|
979
|
+
await addAssetFromFile(file, i + 1, total);
|
|
980
|
+
} catch (err) {
|
|
981
|
+
failures.push({ file: file.name, error: err?.message || String(err) });
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
} finally {
|
|
985
|
+
uploadQueue = [];
|
|
986
|
+
uploadInProgress = false;
|
|
987
|
+
setAssetUploadBusy(false);
|
|
988
|
+
updateUploadStatusUi({ hidden: true });
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if (failures.length > 0) {
|
|
992
|
+
showResult({ status: "upload_completed_with_failures", failures });
|
|
993
|
+
alert(
|
|
994
|
+
`Uploaded with ${failures.length} failure(s). Check result panel for details.`
|
|
995
|
+
);
|
|
996
|
+
} else {
|
|
997
|
+
showResult({ status: "upload_completed", total });
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
886
1001
|
async function verify() {
|
|
887
1002
|
const code = String(document.getElementById("code").value || "")
|
|
888
1003
|
.trim()
|
|
@@ -982,6 +1097,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
982
1097
|
});
|
|
983
1098
|
|
|
984
1099
|
document.getElementById("addAssetBtn")?.addEventListener("click", () => {
|
|
1100
|
+
if (uploadInProgress) return;
|
|
985
1101
|
if (!isPlaylistEditorOpen()) {
|
|
986
1102
|
newPlaylistDraft();
|
|
987
1103
|
}
|
|
@@ -991,11 +1107,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
991
1107
|
document.getElementById("fileInput")?.addEventListener("change", async (ev) => {
|
|
992
1108
|
const files = ev.target.files;
|
|
993
1109
|
if (!files?.length) return;
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
} catch (err) {
|
|
997
|
-
alert(err.message);
|
|
998
|
-
}
|
|
1110
|
+
uploadQueue.push(...Array.from(files));
|
|
1111
|
+
void processUploadQueue();
|
|
999
1112
|
ev.target.value = "";
|
|
1000
1113
|
});
|
|
1001
1114
|
});
|
|
@@ -306,6 +306,42 @@ button.danger {
|
|
|
306
306
|
overflow-y: auto;
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
+
.upload-status {
|
|
310
|
+
margin: 0.25rem 0.75rem 0.5rem;
|
|
311
|
+
padding: 0.55rem 0.6rem;
|
|
312
|
+
border: 1px solid #e4e1dc;
|
|
313
|
+
border-radius: 8px;
|
|
314
|
+
background: #fafaf9;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.upload-status-text {
|
|
318
|
+
font-size: 0.78rem;
|
|
319
|
+
font-weight: 600;
|
|
320
|
+
color: #333;
|
|
321
|
+
margin-bottom: 0.35rem;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.upload-progress-track {
|
|
325
|
+
width: 100%;
|
|
326
|
+
height: 8px;
|
|
327
|
+
border-radius: 999px;
|
|
328
|
+
overflow: hidden;
|
|
329
|
+
background: #ece8e2;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.upload-progress-bar {
|
|
333
|
+
width: 0%;
|
|
334
|
+
height: 100%;
|
|
335
|
+
background: #ff8a3d;
|
|
336
|
+
transition: width 120ms linear;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.upload-queue-text {
|
|
340
|
+
margin-top: 0.35rem;
|
|
341
|
+
font-size: 0.72rem;
|
|
342
|
+
color: #666;
|
|
343
|
+
}
|
|
344
|
+
|
|
309
345
|
.playlist-empty {
|
|
310
346
|
padding: 1.5rem 1rem;
|
|
311
347
|
text-align: center;
|