@tomorrowos/sdk 0.7.0 → 0.7.2
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 +1 -1
- package/dist/playlist-catalog.d.ts.map +1 -1
- package/dist/playlist-catalog.js +1 -6
- package/dist/store/postgres-store.d.ts +1 -0
- package/dist/store/postgres-store.d.ts.map +1 -1
- package/dist/store/postgres-store.js +46 -15
- package/dist/store/snapshot-utils.d.ts +6 -0
- package/dist/store/snapshot-utils.d.ts.map +1 -0
- package/dist/store/snapshot-utils.js +5 -0
- package/dist/store/sqlite-store.d.ts +2 -0
- package/dist/store/sqlite-store.d.ts.map +1 -1
- package/dist/store/sqlite-store.js +97 -15
- package/dist/store/types.d.ts +0 -3
- package/dist/store/types.d.ts.map +1 -1
- package/dist/tomorrowos.d.ts +4 -1
- package/dist/tomorrowos.d.ts.map +1 -1
- package/dist/tomorrowos.js +17 -2
- package/package.json +1 -1
- package/templates/cms-starter/package.json +2 -2
- package/templates/cms-starter/public/methods.js +66 -15
- package/templates/cms-starter/public/panel.css +51 -1
|
@@ -33,7 +33,7 @@ export declare class PlaylistCatalog {
|
|
|
33
33
|
/** Remove every playlist assignment from a device (CMS catalog + empty policy). */
|
|
34
34
|
clearAllAssignmentsFromDevice(deviceId: string): Promise<BuiltDevicePolicy>;
|
|
35
35
|
/**
|
|
36
|
-
* If the device has this playlist assigned, refresh its snapshot
|
|
36
|
+
* If the device has this playlist assigned, refresh its snapshot to latest
|
|
37
37
|
* and return a rebuilt snapshot policy for immediate push.
|
|
38
38
|
*/
|
|
39
39
|
refreshPlaylistSnapshotOnDevice(deviceId: string, playlistId: string, options?: PolicyBuildOptions): Promise<{
|
|
@@ -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;
|
|
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;AA8CD,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;IA8B/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;IA0CvB,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;IA4BrD,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
|
@@ -36,7 +36,6 @@ function cloneSnapshot(playlist, mediaBaseUrl) {
|
|
|
36
36
|
return {
|
|
37
37
|
id: playlist.id,
|
|
38
38
|
name: playlist.name,
|
|
39
|
-
version: playlist.version,
|
|
40
39
|
schedule: playlist.schedule ? { ...playlist.schedule } : undefined,
|
|
41
40
|
items: absolutizePlaylistItems(playlist.items, mediaBaseUrl)
|
|
42
41
|
};
|
|
@@ -70,14 +69,12 @@ export class PlaylistCatalog {
|
|
|
70
69
|
code: "PLAYLIST_NAME_CONFLICT"
|
|
71
70
|
});
|
|
72
71
|
}
|
|
73
|
-
const existing = await this.store.getPlaylist(id);
|
|
74
72
|
const now = new Date().toISOString();
|
|
75
73
|
const record = {
|
|
76
74
|
id,
|
|
77
75
|
name,
|
|
78
76
|
schedule: input.schedule,
|
|
79
77
|
items: Array.isArray(input.items) ? input.items : [],
|
|
80
|
-
version: (existing?.version ?? 0) + 1,
|
|
81
78
|
updatedAt: now,
|
|
82
79
|
retired: false
|
|
83
80
|
};
|
|
@@ -126,7 +123,6 @@ export class PlaylistCatalog {
|
|
|
126
123
|
}
|
|
127
124
|
assignments.push({
|
|
128
125
|
playlistId,
|
|
129
|
-
publishedVersion: playlist.version,
|
|
130
126
|
publishedAt: new Date().toISOString(),
|
|
131
127
|
snapshot: cloneSnapshot(playlist, options.mediaBaseUrl)
|
|
132
128
|
});
|
|
@@ -149,7 +145,7 @@ export class PlaylistCatalog {
|
|
|
149
145
|
return this.buildPolicyFromAssignments([], { useLatest: false });
|
|
150
146
|
}
|
|
151
147
|
/**
|
|
152
|
-
* If the device has this playlist assigned, refresh its snapshot
|
|
148
|
+
* If the device has this playlist assigned, refresh its snapshot to latest
|
|
153
149
|
* and return a rebuilt snapshot policy for immediate push.
|
|
154
150
|
*/
|
|
155
151
|
async refreshPlaylistSnapshotOnDevice(deviceId, playlistId, options = {}) {
|
|
@@ -165,7 +161,6 @@ export class PlaylistCatalog {
|
|
|
165
161
|
? a
|
|
166
162
|
: {
|
|
167
163
|
playlistId,
|
|
168
|
-
publishedVersion: playlist.version,
|
|
169
164
|
publishedAt: new Date().toISOString(),
|
|
170
165
|
snapshot: cloneSnapshot(playlist, options.mediaBaseUrl)
|
|
171
166
|
});
|
|
@@ -13,6 +13,7 @@ export declare class PostgresStore implements TomorrowOSMigratableStore {
|
|
|
13
13
|
private ready?;
|
|
14
14
|
constructor(options: PostgresStoreOptions | string);
|
|
15
15
|
init(): Promise<void>;
|
|
16
|
+
private migrateDropPlaylistVersionColumns;
|
|
16
17
|
close(): Promise<void>;
|
|
17
18
|
private ensureReady;
|
|
18
19
|
setPendingCode(code: string, record: PendingCodeRecord): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/store/postgres-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAE3C,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAGjB,cAAc,EACd,yBAAyB,EACzB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/store/postgres-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Pool } from "pg";
|
|
2
|
+
import { normalizePublishedPlaylistSnapshot } from "./snapshot-utils.js";
|
|
2
3
|
function optionalString(value) {
|
|
3
4
|
return value ?? undefined;
|
|
4
5
|
}
|
|
@@ -71,7 +72,6 @@ export class PostgresStore {
|
|
|
71
72
|
name TEXT NOT NULL,
|
|
72
73
|
schedule_json TEXT,
|
|
73
74
|
items_json TEXT NOT NULL DEFAULT '[]',
|
|
74
|
-
version INTEGER NOT NULL,
|
|
75
75
|
updated_at TEXT NOT NULL,
|
|
76
76
|
retired BOOLEAN NOT NULL DEFAULT false,
|
|
77
77
|
retired_at TEXT
|
|
@@ -85,7 +85,6 @@ export class PostgresStore {
|
|
|
85
85
|
device_id TEXT NOT NULL,
|
|
86
86
|
playlist_id TEXT NOT NULL,
|
|
87
87
|
sort_order INTEGER NOT NULL,
|
|
88
|
-
published_version INTEGER NOT NULL,
|
|
89
88
|
published_at TEXT NOT NULL,
|
|
90
89
|
snapshot_json TEXT NOT NULL,
|
|
91
90
|
PRIMARY KEY (device_id, playlist_id)
|
|
@@ -121,6 +120,46 @@ export class PostgresStore {
|
|
|
121
120
|
|
|
122
121
|
ALTER TABLE paired_devices
|
|
123
122
|
ADD COLUMN IF NOT EXISTS system_version TEXT;
|
|
123
|
+
`);
|
|
124
|
+
await this.migrateDropPlaylistVersionColumns();
|
|
125
|
+
}
|
|
126
|
+
async migrateDropPlaylistVersionColumns() {
|
|
127
|
+
const applied = await this.pool.query(`
|
|
128
|
+
SELECT 1 AS exists
|
|
129
|
+
FROM schema_migrations
|
|
130
|
+
WHERE name = 'drop_playlist_version'
|
|
131
|
+
LIMIT 1
|
|
132
|
+
`);
|
|
133
|
+
if (applied.rows.length > 0)
|
|
134
|
+
return;
|
|
135
|
+
await this.pool.query(`
|
|
136
|
+
ALTER TABLE playlists
|
|
137
|
+
DROP COLUMN IF EXISTS version;
|
|
138
|
+
`);
|
|
139
|
+
await this.pool.query(`
|
|
140
|
+
ALTER TABLE device_assignments
|
|
141
|
+
DROP COLUMN IF EXISTS published_version;
|
|
142
|
+
`);
|
|
143
|
+
const assignmentRows = await this.pool.query(`
|
|
144
|
+
SELECT device_id, playlist_id, snapshot_json
|
|
145
|
+
FROM device_assignments
|
|
146
|
+
`);
|
|
147
|
+
for (const row of assignmentRows.rows) {
|
|
148
|
+
const snapshot = normalizePublishedPlaylistSnapshot(parseJson(row.snapshot_json, {
|
|
149
|
+
id: row.playlist_id,
|
|
150
|
+
name: row.playlist_id,
|
|
151
|
+
items: []
|
|
152
|
+
}));
|
|
153
|
+
await this.pool.query(`
|
|
154
|
+
UPDATE device_assignments
|
|
155
|
+
SET snapshot_json = $1
|
|
156
|
+
WHERE device_id = $2 AND playlist_id = $3
|
|
157
|
+
`, [JSON.stringify(snapshot), row.device_id, row.playlist_id]);
|
|
158
|
+
}
|
|
159
|
+
await this.pool.query(`
|
|
160
|
+
INSERT INTO schema_migrations (id, name)
|
|
161
|
+
VALUES (2, 'drop_playlist_version')
|
|
162
|
+
ON CONFLICT (id) DO NOTHING
|
|
124
163
|
`);
|
|
125
164
|
}
|
|
126
165
|
async close() {
|
|
@@ -332,17 +371,15 @@ export class PostgresStore {
|
|
|
332
371
|
name,
|
|
333
372
|
schedule_json,
|
|
334
373
|
items_json,
|
|
335
|
-
version,
|
|
336
374
|
updated_at,
|
|
337
375
|
retired,
|
|
338
376
|
retired_at
|
|
339
377
|
)
|
|
340
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7
|
|
378
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
341
379
|
ON CONFLICT (id) DO UPDATE SET
|
|
342
380
|
name = EXCLUDED.name,
|
|
343
381
|
schedule_json = EXCLUDED.schedule_json,
|
|
344
382
|
items_json = EXCLUDED.items_json,
|
|
345
|
-
version = EXCLUDED.version,
|
|
346
383
|
updated_at = EXCLUDED.updated_at,
|
|
347
384
|
retired = EXCLUDED.retired,
|
|
348
385
|
retired_at = EXCLUDED.retired_at
|
|
@@ -351,7 +388,6 @@ export class PostgresStore {
|
|
|
351
388
|
record.name,
|
|
352
389
|
record.schedule ? JSON.stringify(record.schedule) : null,
|
|
353
390
|
JSON.stringify(record.items ?? []),
|
|
354
|
-
record.version,
|
|
355
391
|
record.updatedAt,
|
|
356
392
|
record.retired === true,
|
|
357
393
|
record.retiredAt ?? null
|
|
@@ -451,21 +487,19 @@ export class PostgresStore {
|
|
|
451
487
|
async getDeviceAssignments(deviceId) {
|
|
452
488
|
await this.ensureReady();
|
|
453
489
|
const result = await this.pool.query(`
|
|
454
|
-
SELECT playlist_id,
|
|
490
|
+
SELECT playlist_id, published_at, snapshot_json
|
|
455
491
|
FROM device_assignments
|
|
456
492
|
WHERE device_id = $1
|
|
457
493
|
ORDER BY sort_order ASC
|
|
458
494
|
`, [deviceId]);
|
|
459
495
|
return result.rows.map((row) => ({
|
|
460
496
|
playlistId: row.playlist_id,
|
|
461
|
-
publishedVersion: row.published_version,
|
|
462
497
|
publishedAt: row.published_at,
|
|
463
|
-
snapshot: parseJson(row.snapshot_json, {
|
|
498
|
+
snapshot: normalizePublishedPlaylistSnapshot(parseJson(row.snapshot_json, {
|
|
464
499
|
id: row.playlist_id,
|
|
465
500
|
name: row.playlist_id,
|
|
466
|
-
version: row.published_version,
|
|
467
501
|
items: []
|
|
468
|
-
})
|
|
502
|
+
}))
|
|
469
503
|
}));
|
|
470
504
|
}
|
|
471
505
|
async setDeviceAssignments(deviceId, assignments) {
|
|
@@ -482,16 +516,14 @@ export class PostgresStore {
|
|
|
482
516
|
device_id,
|
|
483
517
|
playlist_id,
|
|
484
518
|
sort_order,
|
|
485
|
-
published_version,
|
|
486
519
|
published_at,
|
|
487
520
|
snapshot_json
|
|
488
521
|
)
|
|
489
|
-
VALUES ($1, $2, $3, $4, $5
|
|
522
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
490
523
|
`, [
|
|
491
524
|
deviceId,
|
|
492
525
|
assignment.playlistId,
|
|
493
526
|
index,
|
|
494
|
-
assignment.publishedVersion,
|
|
495
527
|
assignment.publishedAt,
|
|
496
528
|
JSON.stringify(assignment.snapshot)
|
|
497
529
|
]);
|
|
@@ -536,7 +568,6 @@ export class PostgresStore {
|
|
|
536
568
|
name: row.name,
|
|
537
569
|
schedule: parseJson(row.schedule_json, undefined),
|
|
538
570
|
items: parseJson(row.items_json, []),
|
|
539
|
-
version: row.version,
|
|
540
571
|
updatedAt: row.updated_at,
|
|
541
572
|
retired: row.retired,
|
|
542
573
|
retiredAt: optionalString(row.retired_at)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PublishedPlaylistSnapshot } from "./types.js";
|
|
2
|
+
/** Drop legacy playlist `version` fields from stored assignment snapshots. */
|
|
3
|
+
export declare function normalizePublishedPlaylistSnapshot(value: PublishedPlaylistSnapshot & {
|
|
4
|
+
version?: unknown;
|
|
5
|
+
}): PublishedPlaylistSnapshot;
|
|
6
|
+
//# sourceMappingURL=snapshot-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-utils.d.ts","sourceRoot":"","sources":["../../src/store/snapshot-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,8EAA8E;AAC9E,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,yBAAyB,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACvD,yBAAyB,CAG3B"}
|
|
@@ -11,6 +11,8 @@ export declare class SQLiteStore implements TomorrowOSMigratableStore {
|
|
|
11
11
|
private readonly db;
|
|
12
12
|
constructor(options?: SQLiteStoreOptions | string);
|
|
13
13
|
init(): void;
|
|
14
|
+
private tableHasColumn;
|
|
15
|
+
private migrateDropPlaylistVersionColumns;
|
|
14
16
|
private addColumnIfMissing;
|
|
15
17
|
close(): void;
|
|
16
18
|
setPendingCode(code: string, record: PendingCodeRecord): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAGjB,cAAc,EACd,yBAAyB,EACzB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAGjB,cAAc,EACd,yBAAyB,EACzB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,YAAY,CAAC;AAGpB,UAAU,kBAAkB;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA6ED;;;GAGG;AACH,qBAAa,WAAY,YAAW,yBAAyB;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;gBAE3B,OAAO,GAAE,kBAAkB,GAAG,MAAW;IAerD,IAAI,IAAI,IAAI;IA6FZ,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,iCAAiC;IA4GzC,OAAO,CAAC,kBAAkB;IAS1B,KAAK,IAAI,IAAI;IAIP,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAUpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAY/C,iBAAiB,CACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAStC,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IA2BV,uBAAuB,CAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,oBAAoB,CAAA;KAAE,GAAG,SAAS,CAAC;IAapE,kBAAkB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAYpD,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IA6CV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IASpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAYjD,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAS1C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAS5D,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BlD,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAcvE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAStE,wBAAwB,CAC5B,MAAM,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,4BAA4B,GAC7C,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAerC,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyC5D,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,kBAAkB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IASpD,oBAAoB,CACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAuBhC,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,wBAAwB,EAAE,GACtC,OAAO,CAAC,IAAI,CAAC;IA4BhB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,mBAAmB;CAe5B"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
|
+
import { normalizePublishedPlaylistSnapshot } from "./snapshot-utils.js";
|
|
4
5
|
const DEFAULT_SQLITE_PATH = path.join(process.cwd(), "data", "tomorrowos.db");
|
|
5
6
|
function optionalString(value) {
|
|
6
7
|
return value ?? undefined;
|
|
@@ -77,7 +78,6 @@ export class SQLiteStore {
|
|
|
77
78
|
name TEXT NOT NULL,
|
|
78
79
|
schedule_json TEXT,
|
|
79
80
|
items_json TEXT NOT NULL DEFAULT '[]',
|
|
80
|
-
version INTEGER NOT NULL,
|
|
81
81
|
updated_at TEXT NOT NULL,
|
|
82
82
|
retired INTEGER NOT NULL DEFAULT 0,
|
|
83
83
|
retired_at TEXT
|
|
@@ -91,7 +91,6 @@ export class SQLiteStore {
|
|
|
91
91
|
device_id TEXT NOT NULL,
|
|
92
92
|
playlist_id TEXT NOT NULL,
|
|
93
93
|
sort_order INTEGER NOT NULL,
|
|
94
|
-
published_version INTEGER NOT NULL,
|
|
95
94
|
published_at TEXT NOT NULL,
|
|
96
95
|
snapshot_json TEXT NOT NULL,
|
|
97
96
|
PRIMARY KEY (device_id, playlist_id)
|
|
@@ -123,6 +122,95 @@ export class SQLiteStore {
|
|
|
123
122
|
`);
|
|
124
123
|
this.addColumnIfMissing("paired_devices", "player_version TEXT");
|
|
125
124
|
this.addColumnIfMissing("paired_devices", "system_version TEXT");
|
|
125
|
+
this.migrateDropPlaylistVersionColumns();
|
|
126
|
+
}
|
|
127
|
+
tableHasColumn(table, column) {
|
|
128
|
+
const rows = this.db.pragma(`table_info(${table})`);
|
|
129
|
+
return rows.some((row) => row.name === column);
|
|
130
|
+
}
|
|
131
|
+
migrateDropPlaylistVersionColumns() {
|
|
132
|
+
const alreadyApplied = this.db.prepare(`
|
|
133
|
+
SELECT 1
|
|
134
|
+
FROM schema_migrations
|
|
135
|
+
WHERE name = 'drop_playlist_version'
|
|
136
|
+
LIMIT 1
|
|
137
|
+
`).get();
|
|
138
|
+
if (alreadyApplied)
|
|
139
|
+
return;
|
|
140
|
+
if (this.tableHasColumn("playlists", "version")) {
|
|
141
|
+
this.db.exec(`
|
|
142
|
+
CREATE TABLE playlists_new (
|
|
143
|
+
id TEXT PRIMARY KEY,
|
|
144
|
+
name TEXT NOT NULL,
|
|
145
|
+
schedule_json TEXT,
|
|
146
|
+
items_json TEXT NOT NULL DEFAULT '[]',
|
|
147
|
+
updated_at TEXT NOT NULL,
|
|
148
|
+
retired INTEGER NOT NULL DEFAULT 0,
|
|
149
|
+
retired_at TEXT
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
INSERT INTO playlists_new (
|
|
153
|
+
id, name, schedule_json, items_json, updated_at, retired, retired_at
|
|
154
|
+
)
|
|
155
|
+
SELECT id, name, schedule_json, items_json, updated_at, retired, retired_at
|
|
156
|
+
FROM playlists;
|
|
157
|
+
|
|
158
|
+
DROP TABLE playlists;
|
|
159
|
+
ALTER TABLE playlists_new RENAME TO playlists;
|
|
160
|
+
|
|
161
|
+
CREATE UNIQUE INDEX IF NOT EXISTS playlists_active_name_unique
|
|
162
|
+
ON playlists (lower(trim(name)))
|
|
163
|
+
WHERE retired = 0;
|
|
164
|
+
`);
|
|
165
|
+
}
|
|
166
|
+
if (this.tableHasColumn("device_assignments", "published_version")) {
|
|
167
|
+
const rows = this.db.prepare(`
|
|
168
|
+
SELECT device_id, playlist_id, sort_order, published_at, snapshot_json
|
|
169
|
+
FROM device_assignments
|
|
170
|
+
ORDER BY device_id ASC, sort_order ASC
|
|
171
|
+
`).all();
|
|
172
|
+
this.db.exec(`
|
|
173
|
+
CREATE TABLE device_assignments_new (
|
|
174
|
+
device_id TEXT NOT NULL,
|
|
175
|
+
playlist_id TEXT NOT NULL,
|
|
176
|
+
sort_order INTEGER NOT NULL,
|
|
177
|
+
published_at TEXT NOT NULL,
|
|
178
|
+
snapshot_json TEXT NOT NULL,
|
|
179
|
+
PRIMARY KEY (device_id, playlist_id)
|
|
180
|
+
);
|
|
181
|
+
`);
|
|
182
|
+
const insert = this.db.prepare(`
|
|
183
|
+
INSERT INTO device_assignments_new (
|
|
184
|
+
device_id,
|
|
185
|
+
playlist_id,
|
|
186
|
+
sort_order,
|
|
187
|
+
published_at,
|
|
188
|
+
snapshot_json
|
|
189
|
+
)
|
|
190
|
+
VALUES (?, ?, ?, ?, ?)
|
|
191
|
+
`);
|
|
192
|
+
const replaceAssignments = this.db.transaction(() => {
|
|
193
|
+
for (const row of rows) {
|
|
194
|
+
const snapshot = normalizePublishedPlaylistSnapshot(parseJson(row.snapshot_json, {
|
|
195
|
+
id: row.playlist_id,
|
|
196
|
+
name: row.playlist_id,
|
|
197
|
+
items: []
|
|
198
|
+
}));
|
|
199
|
+
insert.run(row.device_id, row.playlist_id, row.sort_order, row.published_at, JSON.stringify(snapshot));
|
|
200
|
+
}
|
|
201
|
+
this.db.exec("DROP TABLE device_assignments");
|
|
202
|
+
this.db.exec("ALTER TABLE device_assignments_new RENAME TO device_assignments");
|
|
203
|
+
this.db.exec(`
|
|
204
|
+
CREATE INDEX IF NOT EXISTS device_assignments_device_order_idx
|
|
205
|
+
ON device_assignments (device_id, sort_order)
|
|
206
|
+
`);
|
|
207
|
+
});
|
|
208
|
+
replaceAssignments();
|
|
209
|
+
}
|
|
210
|
+
this.db.prepare(`
|
|
211
|
+
INSERT OR IGNORE INTO schema_migrations (id, name)
|
|
212
|
+
VALUES (2, 'drop_playlist_version')
|
|
213
|
+
`).run();
|
|
126
214
|
}
|
|
127
215
|
addColumnIfMissing(table, columnDefinition) {
|
|
128
216
|
try {
|
|
@@ -297,21 +385,19 @@ export class SQLiteStore {
|
|
|
297
385
|
name,
|
|
298
386
|
schedule_json,
|
|
299
387
|
items_json,
|
|
300
|
-
version,
|
|
301
388
|
updated_at,
|
|
302
389
|
retired,
|
|
303
390
|
retired_at
|
|
304
391
|
)
|
|
305
|
-
VALUES (?, ?, ?, ?, ?, ?,
|
|
392
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
306
393
|
ON CONFLICT(id) DO UPDATE SET
|
|
307
394
|
name = excluded.name,
|
|
308
395
|
schedule_json = excluded.schedule_json,
|
|
309
396
|
items_json = excluded.items_json,
|
|
310
|
-
version = excluded.version,
|
|
311
397
|
updated_at = excluded.updated_at,
|
|
312
398
|
retired = excluded.retired,
|
|
313
399
|
retired_at = excluded.retired_at
|
|
314
|
-
`).run(record.id, record.name, record.schedule ? JSON.stringify(record.schedule) : null, JSON.stringify(record.items ?? []), record.
|
|
400
|
+
`).run(record.id, record.name, record.schedule ? JSON.stringify(record.schedule) : null, JSON.stringify(record.items ?? []), record.updatedAt, record.retired ? 1 : 0, record.retiredAt ?? null);
|
|
315
401
|
}
|
|
316
402
|
async isPlaylistNameTaken(name, excludeId) {
|
|
317
403
|
const target = name.trim().toLowerCase();
|
|
@@ -386,21 +472,19 @@ export class SQLiteStore {
|
|
|
386
472
|
}
|
|
387
473
|
async getDeviceAssignments(deviceId) {
|
|
388
474
|
const rows = this.db.prepare(`
|
|
389
|
-
SELECT playlist_id,
|
|
475
|
+
SELECT playlist_id, published_at, snapshot_json
|
|
390
476
|
FROM device_assignments
|
|
391
477
|
WHERE device_id = ?
|
|
392
478
|
ORDER BY sort_order ASC
|
|
393
479
|
`).all(deviceId);
|
|
394
480
|
return rows.map((row) => ({
|
|
395
481
|
playlistId: row.playlist_id,
|
|
396
|
-
publishedVersion: row.published_version,
|
|
397
482
|
publishedAt: row.published_at,
|
|
398
|
-
snapshot: parseJson(row.snapshot_json, {
|
|
483
|
+
snapshot: normalizePublishedPlaylistSnapshot(parseJson(row.snapshot_json, {
|
|
399
484
|
id: row.playlist_id,
|
|
400
485
|
name: row.playlist_id,
|
|
401
|
-
version: row.published_version,
|
|
402
486
|
items: []
|
|
403
|
-
})
|
|
487
|
+
}))
|
|
404
488
|
}));
|
|
405
489
|
}
|
|
406
490
|
async setDeviceAssignments(deviceId, assignments) {
|
|
@@ -411,14 +495,13 @@ export class SQLiteStore {
|
|
|
411
495
|
device_id,
|
|
412
496
|
playlist_id,
|
|
413
497
|
sort_order,
|
|
414
|
-
published_version,
|
|
415
498
|
published_at,
|
|
416
499
|
snapshot_json
|
|
417
500
|
)
|
|
418
|
-
VALUES (?, ?, ?, ?,
|
|
501
|
+
VALUES (?, ?, ?, ?, ?)
|
|
419
502
|
`);
|
|
420
503
|
assignments.forEach((assignment, index) => {
|
|
421
|
-
insert.run(deviceId, assignment.playlistId, index, assignment.
|
|
504
|
+
insert.run(deviceId, assignment.playlistId, index, assignment.publishedAt, JSON.stringify(assignment.snapshot));
|
|
422
505
|
});
|
|
423
506
|
});
|
|
424
507
|
replaceAssignments();
|
|
@@ -453,7 +536,6 @@ export class SQLiteStore {
|
|
|
453
536
|
name: row.name,
|
|
454
537
|
schedule: parseJson(row.schedule_json, undefined),
|
|
455
538
|
items: parseJson(row.items_json, []),
|
|
456
|
-
version: row.version,
|
|
457
539
|
updatedAt: row.updated_at,
|
|
458
540
|
retired: row.retired === 1,
|
|
459
541
|
retiredAt: optionalString(row.retired_at)
|
package/dist/store/types.d.ts
CHANGED
|
@@ -75,7 +75,6 @@ export interface StoredPlaylist {
|
|
|
75
75
|
name: string;
|
|
76
76
|
schedule?: PlaylistSchedule;
|
|
77
77
|
items: PlaylistItemRecord[];
|
|
78
|
-
version: number;
|
|
79
78
|
updatedAt: string;
|
|
80
79
|
retired?: boolean;
|
|
81
80
|
retiredAt?: string;
|
|
@@ -83,13 +82,11 @@ export interface StoredPlaylist {
|
|
|
83
82
|
export interface PublishedPlaylistSnapshot {
|
|
84
83
|
id: string;
|
|
85
84
|
name: string;
|
|
86
|
-
version: number;
|
|
87
85
|
schedule?: PlaylistSchedule;
|
|
88
86
|
items: PlaylistItemRecord[];
|
|
89
87
|
}
|
|
90
88
|
export interface DevicePlaylistAssignment {
|
|
91
89
|
playlistId: string;
|
|
92
|
-
publishedVersion: number;
|
|
93
90
|
publishedAt: string;
|
|
94
91
|
snapshot: PublishedPlaylistSnapshot;
|
|
95
92
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wFAAwF;AACxF,MAAM,WAAW,oBAAoB;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,4BAA4B,GAAG,OAAO,GAAG,YAAY,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,4BAA4B,CAAC;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wGAAwG;AACxG,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.d.ts
CHANGED
|
@@ -51,7 +51,6 @@ export interface DeviceListItem {
|
|
|
51
51
|
publishedPlaylists: Array<{
|
|
52
52
|
playlistId: string;
|
|
53
53
|
name: string;
|
|
54
|
-
version: number;
|
|
55
54
|
publishedAt: string;
|
|
56
55
|
schedule?: {
|
|
57
56
|
startDate?: string;
|
|
@@ -62,6 +61,10 @@ export interface DeviceListItem {
|
|
|
62
61
|
}>;
|
|
63
62
|
latestErrorAt: string | null;
|
|
64
63
|
latestErrorMessage: string | null;
|
|
64
|
+
latestScreenshot: {
|
|
65
|
+
url: string;
|
|
66
|
+
capturedAt: string;
|
|
67
|
+
} | null;
|
|
65
68
|
}
|
|
66
69
|
export interface DeviceLogEntry {
|
|
67
70
|
timestamp: string;
|
package/dist/tomorrowos.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAYxB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAKV,eAAe,EAGhB,MAAM,kBAAkB,CAAC;AAc1B,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAmED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kBAAkB,EAAE,KAAK,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -325,13 +325,27 @@ export class TomorrowOS extends EventEmitter {
|
|
|
325
325
|
const screenOnlineSince = connected && record.lastBootAt ? record.lastBootAt : null;
|
|
326
326
|
const logs = this.deviceLogs.get(deviceId) ?? [];
|
|
327
327
|
const latestError = [...logs].reverse().find((entry) => entry.level === "error");
|
|
328
|
+
let latestScreenshot = null;
|
|
329
|
+
if (this.staticRoot) {
|
|
330
|
+
try {
|
|
331
|
+
const screenshot = await this.getLatestDeviceScreenshot(deviceId);
|
|
332
|
+
if (screenshot?.url) {
|
|
333
|
+
latestScreenshot = {
|
|
334
|
+
url: screenshot.url,
|
|
335
|
+
capturedAt: screenshot.capturedAt
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
latestScreenshot = null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
328
343
|
return {
|
|
329
344
|
deviceId,
|
|
330
345
|
pairingCode: reg?.permanentPairingCode ?? null,
|
|
331
346
|
publishedPlaylists: assignments.map((a) => ({
|
|
332
347
|
playlistId: a.playlistId,
|
|
333
348
|
name: a.snapshot.name,
|
|
334
|
-
version: a.publishedVersion,
|
|
335
349
|
publishedAt: a.publishedAt,
|
|
336
350
|
schedule: a.snapshot.schedule
|
|
337
351
|
})),
|
|
@@ -351,7 +365,8 @@ export class TomorrowOS extends EventEmitter {
|
|
|
351
365
|
screenOnlineLabel,
|
|
352
366
|
screenOnlineSince,
|
|
353
367
|
latestErrorAt: latestError?.timestamp ?? null,
|
|
354
|
-
latestErrorMessage: latestError?.message ?? null
|
|
368
|
+
latestErrorMessage: latestError?.message ?? null,
|
|
369
|
+
latestScreenshot
|
|
355
370
|
};
|
|
356
371
|
}));
|
|
357
372
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
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.7.
|
|
3
|
+
"version": "0.7.2",
|
|
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.7.
|
|
13
|
+
"@tomorrowos/sdk": "^0.7.2",
|
|
14
14
|
"dotenv": "^17.2.3"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
@@ -872,6 +872,44 @@ async function fetchDevices() {
|
|
|
872
872
|
}
|
|
873
873
|
}
|
|
874
874
|
|
|
875
|
+
function screenshotThumbUrl(screenshot) {
|
|
876
|
+
if (!screenshot?.url) return "";
|
|
877
|
+
const cacheBust = encodeURIComponent(screenshot.capturedAt || Date.now());
|
|
878
|
+
const joiner = screenshot.url.includes("?") ? "&" : "?";
|
|
879
|
+
return `${screenshot.url}${joiner}v=${cacheBust}`;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function appendDeviceMetaRow(meta, label, value) {
|
|
883
|
+
const row = document.createElement("div");
|
|
884
|
+
row.className = "device-meta-row";
|
|
885
|
+
const dt = document.createElement("dt");
|
|
886
|
+
dt.textContent = label;
|
|
887
|
+
const dd = document.createElement("dd");
|
|
888
|
+
dd.textContent = value;
|
|
889
|
+
row.appendChild(dt);
|
|
890
|
+
row.appendChild(dd);
|
|
891
|
+
meta.appendChild(row);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
function createDeviceScreenshotThumb(device) {
|
|
895
|
+
const slot = document.createElement("div");
|
|
896
|
+
slot.className = "device-screenshot-slot";
|
|
897
|
+
const screenshot = device.latestScreenshot;
|
|
898
|
+
if (screenshot?.url) {
|
|
899
|
+
const btn = document.createElement("button");
|
|
900
|
+
btn.type = "button";
|
|
901
|
+
btn.className = "device-screenshot-thumb";
|
|
902
|
+
btn.title = `Last screenshot — ${formatDateTimeSeconds(screenshot.capturedAt)}`;
|
|
903
|
+
const img = document.createElement("img");
|
|
904
|
+
img.alt = `Last screenshot for ${device.deviceId}`;
|
|
905
|
+
img.src = screenshotThumbUrl(screenshot);
|
|
906
|
+
btn.appendChild(img);
|
|
907
|
+
btn.addEventListener("click", () => openScreenshotModal(device.deviceId, screenshot));
|
|
908
|
+
slot.appendChild(btn);
|
|
909
|
+
}
|
|
910
|
+
return slot;
|
|
911
|
+
}
|
|
912
|
+
|
|
875
913
|
function renderDeviceCards() {
|
|
876
914
|
const grid = document.getElementById("devicesGrid");
|
|
877
915
|
if (!grid) return;
|
|
@@ -983,31 +1021,43 @@ function renderDeviceCards() {
|
|
|
983
1021
|
}
|
|
984
1022
|
}
|
|
985
1023
|
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
1024
|
+
const metaBlock = document.createElement("div");
|
|
1025
|
+
metaBlock.className = "device-meta-block";
|
|
1026
|
+
|
|
1027
|
+
const metaTop = document.createElement("div");
|
|
1028
|
+
metaTop.className = "device-meta-top";
|
|
1029
|
+
|
|
1030
|
+
const metaPrimary = document.createElement("dl");
|
|
1031
|
+
metaPrimary.className = "device-meta device-meta--primary";
|
|
1032
|
+
const primaryRows = [
|
|
989
1033
|
["Device ID", device.deviceId],
|
|
990
1034
|
["System", device.system || device.platform || "—"],
|
|
991
1035
|
["System version", device.systemVersion || "—"],
|
|
992
|
-
["Player version", device.playerVersion || "—"]
|
|
1036
|
+
["Player version", device.playerVersion || "—"]
|
|
1037
|
+
];
|
|
1038
|
+
for (const [label, value] of primaryRows) {
|
|
1039
|
+
appendDeviceMetaRow(metaPrimary, label, value);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
metaTop.appendChild(metaPrimary);
|
|
1043
|
+
metaTop.appendChild(createDeviceScreenshotThumb(device));
|
|
1044
|
+
|
|
1045
|
+
const metaSecondary = document.createElement("dl");
|
|
1046
|
+
metaSecondary.className = "device-meta device-meta--secondary";
|
|
1047
|
+
const secondaryRows = [
|
|
993
1048
|
["Device online", formatDeviceOnlineLabel(device)],
|
|
994
1049
|
["Last boot", formatDateTimeSeconds(device.lastBootAt)],
|
|
995
1050
|
["Latest push", formatDateTimeSeconds(device.lastPolicyPushAt)],
|
|
996
1051
|
["Latest error", device.latestErrorMessage || "—"],
|
|
997
1052
|
["Error at", formatDateTimeSeconds(device.latestErrorAt)]
|
|
998
1053
|
];
|
|
999
|
-
for (const [label, value] of
|
|
1000
|
-
|
|
1001
|
-
row.className = "device-meta-row";
|
|
1002
|
-
const dt = document.createElement("dt");
|
|
1003
|
-
dt.textContent = label;
|
|
1004
|
-
const dd = document.createElement("dd");
|
|
1005
|
-
dd.textContent = value;
|
|
1006
|
-
row.appendChild(dt);
|
|
1007
|
-
row.appendChild(dd);
|
|
1008
|
-
meta.appendChild(row);
|
|
1054
|
+
for (const [label, value] of secondaryRows) {
|
|
1055
|
+
appendDeviceMetaRow(metaSecondary, label, value);
|
|
1009
1056
|
}
|
|
1010
1057
|
|
|
1058
|
+
metaBlock.appendChild(metaTop);
|
|
1059
|
+
metaBlock.appendChild(metaSecondary);
|
|
1060
|
+
|
|
1011
1061
|
const actions = document.createElement("div");
|
|
1012
1062
|
actions.className = "device-card-actions";
|
|
1013
1063
|
|
|
@@ -1072,7 +1122,7 @@ function renderDeviceCards() {
|
|
|
1072
1122
|
|
|
1073
1123
|
card.appendChild(header);
|
|
1074
1124
|
card.appendChild(published);
|
|
1075
|
-
card.appendChild(
|
|
1125
|
+
card.appendChild(metaBlock);
|
|
1076
1126
|
card.appendChild(actions);
|
|
1077
1127
|
grid.appendChild(card);
|
|
1078
1128
|
}
|
|
@@ -1519,6 +1569,7 @@ async function captureDeviceScreenshot(deviceId) {
|
|
|
1519
1569
|
}
|
|
1520
1570
|
const capturedAt = formatDateTimeSeconds(data.screenshot?.capturedAt);
|
|
1521
1571
|
alert(`Screenshot captured successfully${capturedAt ? ` at ${capturedAt}` : ""}.`);
|
|
1572
|
+
void fetchDevices();
|
|
1522
1573
|
}
|
|
1523
1574
|
|
|
1524
1575
|
async function viewLatestScreenshot(deviceId) {
|
|
@@ -120,7 +120,7 @@ body {
|
|
|
120
120
|
|
|
121
121
|
.devices-grid {
|
|
122
122
|
display: grid;
|
|
123
|
-
grid-template-columns: repeat(auto-fill, minmax(
|
|
123
|
+
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
|
|
124
124
|
gap: 0.85rem;
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -205,9 +205,59 @@ body {
|
|
|
205
205
|
gap: 0.35rem;
|
|
206
206
|
font-size: 0.78rem;
|
|
207
207
|
color: #444;
|
|
208
|
+
margin: 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.device-meta-block {
|
|
208
212
|
margin-bottom: 0.75rem;
|
|
209
213
|
}
|
|
210
214
|
|
|
215
|
+
.device-meta-top {
|
|
216
|
+
display: grid;
|
|
217
|
+
grid-template-columns: minmax(0, 1fr) 92px;
|
|
218
|
+
gap: 0.65rem;
|
|
219
|
+
align-items: stretch;
|
|
220
|
+
margin-bottom: 0.35rem;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.device-meta--primary {
|
|
224
|
+
min-width: 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.device-meta--secondary {
|
|
228
|
+
margin-bottom: 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.device-screenshot-slot {
|
|
232
|
+
width: 92px;
|
|
233
|
+
min-height: 4.85rem;
|
|
234
|
+
flex-shrink: 0;
|
|
235
|
+
border: 1px solid transparent;
|
|
236
|
+
border-radius: 6px;
|
|
237
|
+
background: #fafaf9;
|
|
238
|
+
overflow: hidden;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.device-screenshot-thumb {
|
|
242
|
+
display: block;
|
|
243
|
+
width: 100%;
|
|
244
|
+
height: 100%;
|
|
245
|
+
min-height: 4.85rem;
|
|
246
|
+
padding: 0;
|
|
247
|
+
border: 0;
|
|
248
|
+
background: transparent;
|
|
249
|
+
cursor: pointer;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.device-screenshot-thumb img {
|
|
253
|
+
display: block;
|
|
254
|
+
width: 100%;
|
|
255
|
+
height: 100%;
|
|
256
|
+
min-height: 4.85rem;
|
|
257
|
+
object-fit: cover;
|
|
258
|
+
object-position: center;
|
|
259
|
+
}
|
|
260
|
+
|
|
211
261
|
.device-meta dt {
|
|
212
262
|
font-weight: 600;
|
|
213
263
|
color: #666;
|