@tomorrowos/sdk 0.1.8 → 0.1.9
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
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.1.
|
|
3
|
+
"version": "0.1.9",
|
|
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.1.
|
|
13
|
+
"@tomorrowos/sdk": "^0.1.9"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^20.0.0",
|
|
@@ -23,6 +23,23 @@
|
|
|
23
23
|
<input type="hidden" id="deviceId" />
|
|
24
24
|
</section>
|
|
25
25
|
|
|
26
|
+
<section class="card">
|
|
27
|
+
<h2>CMS URL for screens</h2>
|
|
28
|
+
<p style="margin: 0 0 0.5rem; font-size: 0.8rem; color: #666">
|
|
29
|
+
HTTP(S) base URL your TV uses to download uploads (same host as <code>ws://</code> on the
|
|
30
|
+
device, not <code>localhost</code>).
|
|
31
|
+
</p>
|
|
32
|
+
<div class="row">
|
|
33
|
+
<input
|
|
34
|
+
id="cmsDeviceBaseUrl"
|
|
35
|
+
type="text"
|
|
36
|
+
style="flex: 1; min-width: 16rem"
|
|
37
|
+
placeholder="http://192.168.1.105:3000"
|
|
38
|
+
/>
|
|
39
|
+
<button type="button" onclick="saveCmsDeviceBaseUrl()">Save</button>
|
|
40
|
+
</div>
|
|
41
|
+
</section>
|
|
42
|
+
|
|
26
43
|
<section class="card">
|
|
27
44
|
<h2>When this playlist plays</h2>
|
|
28
45
|
<p style="margin: 0 0 0.75rem; font-size: 0.8rem; color: #666">
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const PANEL_DEVICE_ID_KEY = "tomorrowos.panel.deviceId";
|
|
2
2
|
const PANEL_PLAYLIST_KEY = "tomorrowos.panel.playlistDraft";
|
|
3
3
|
const PANEL_SCHEDULE_KEY = "tomorrowos.panel.scheduleDraft";
|
|
4
|
+
const PANEL_MEDIA_BASE_KEY = "tomorrowos.panel.mediaBaseUrl";
|
|
4
5
|
|
|
5
6
|
/** @type {{ id: string, url: string, name: string, type: string, durationMs: number }[]} */
|
|
6
7
|
let playlistItems = [];
|
|
@@ -25,11 +26,65 @@ function showResult(data) {
|
|
|
25
26
|
document.getElementById("result").textContent = JSON.stringify(data, null, 2);
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
function isLocalPanelHost(hostname) {
|
|
30
|
+
const h = String(hostname || "").toLowerCase();
|
|
31
|
+
return h === "localhost" || h === "127.0.0.1" || h === "[::1]";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeMediaBaseUrl(raw) {
|
|
35
|
+
let s = String(raw || "").trim();
|
|
36
|
+
if (!s) return "";
|
|
37
|
+
if (!/^https?:\/\//i.test(s)) {
|
|
38
|
+
s = `http://${s}`;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const u = new URL(s);
|
|
42
|
+
return u.origin;
|
|
43
|
+
} catch {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getMediaBaseOrigin() {
|
|
49
|
+
const fromInput = normalizeMediaBaseUrl(
|
|
50
|
+
document.getElementById("cmsDeviceBaseUrl")?.value ||
|
|
51
|
+
localStorage.getItem(PANEL_MEDIA_BASE_KEY) ||
|
|
52
|
+
""
|
|
53
|
+
);
|
|
54
|
+
if (fromInput) return fromInput;
|
|
55
|
+
|
|
56
|
+
if (!isLocalPanelHost(window.location.hostname)) {
|
|
57
|
+
return window.location.origin;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function saveCmsDeviceBaseUrl() {
|
|
64
|
+
const normalized = normalizeMediaBaseUrl(
|
|
65
|
+
document.getElementById("cmsDeviceBaseUrl")?.value
|
|
66
|
+
);
|
|
67
|
+
if (!normalized) {
|
|
68
|
+
alert("Enter a valid URL, e.g. http://192.168.1.105:3000");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
document.getElementById("cmsDeviceBaseUrl").value = normalized;
|
|
72
|
+
localStorage.setItem(PANEL_MEDIA_BASE_KEY, normalized);
|
|
73
|
+
showResult({ status: "saved", mediaBaseUrl: normalized });
|
|
74
|
+
}
|
|
75
|
+
|
|
28
76
|
function absoluteMediaUrl(path) {
|
|
29
77
|
const p = String(path || "").trim();
|
|
30
78
|
if (!p) return "";
|
|
31
79
|
if (/^https?:\/\//i.test(p)) return p;
|
|
32
|
-
|
|
80
|
+
|
|
81
|
+
const base = getMediaBaseOrigin();
|
|
82
|
+
if (!base) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"Set CMS URL for screens (use your PC LAN IP or Replit https URL, not localhost)."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return `${base}${p.startsWith("/") ? p : `/${p}`}`;
|
|
33
88
|
}
|
|
34
89
|
|
|
35
90
|
function inferMediaType(filename, mime) {
|
|
@@ -271,8 +326,28 @@ async function publish() {
|
|
|
271
326
|
return;
|
|
272
327
|
}
|
|
273
328
|
|
|
329
|
+
const mediaBase = getMediaBaseOrigin();
|
|
330
|
+
if (!mediaBase) {
|
|
331
|
+
alert(
|
|
332
|
+
"Set CMS URL for screens first (e.g. http://192.168.1.105:3000 — same machine as the TV ws:// address)."
|
|
333
|
+
);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
if (isLocalPanelHost(new URL(mediaBase).hostname)) {
|
|
337
|
+
const ok = confirm(
|
|
338
|
+
"Media URLs use localhost. TVs cannot download from localhost unless the player runs on this PC. Continue anyway?"
|
|
339
|
+
);
|
|
340
|
+
if (!ok) return;
|
|
341
|
+
}
|
|
342
|
+
|
|
274
343
|
saveScheduleDraft();
|
|
275
|
-
|
|
344
|
+
let payload;
|
|
345
|
+
try {
|
|
346
|
+
payload = buildPolicyPayload();
|
|
347
|
+
} catch (err) {
|
|
348
|
+
alert(err.message);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
276
351
|
|
|
277
352
|
const res = await fetch(`/device/${deviceId}/content/set-policy`, {
|
|
278
353
|
method: "POST",
|
|
@@ -330,6 +405,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
330
405
|
const saved = localStorage.getItem(PANEL_DEVICE_ID_KEY);
|
|
331
406
|
if (saved) setPanelDeviceId(saved);
|
|
332
407
|
|
|
408
|
+
const savedMediaBase = localStorage.getItem(PANEL_MEDIA_BASE_KEY);
|
|
409
|
+
const cmsBaseInput = document.getElementById("cmsDeviceBaseUrl");
|
|
410
|
+
if (savedMediaBase && cmsBaseInput) {
|
|
411
|
+
cmsBaseInput.value = savedMediaBase;
|
|
412
|
+
} else if (cmsBaseInput && !isLocalPanelHost(window.location.hostname)) {
|
|
413
|
+
cmsBaseInput.value = window.location.origin;
|
|
414
|
+
}
|
|
415
|
+
|
|
333
416
|
loadPlaylistDraft();
|
|
334
417
|
loadScheduleDraft();
|
|
335
418
|
|