diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/transport.js | 29 |
1 files changed, 29 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js index 7528a92..065ccc0 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js @@ -74,6 +74,7 @@ function _aborted() { // one. const ADMIN_OP_TYPES = new Set([ 'tmdb_override', 'tmdb_config', 'tmdb_enabled', 'video_root', 'audio_root', + 'photo_roots', 'musicbrainz_config', 'musicbrainz_enabled', 'file_delete', 'dir_delete', 'member_upload', 'apps_enabled', 'set_scan_settings', 'member_revoke', 'root_add', 'root_remove', 'member_unpin', 'gek_rotate', 'group_attach', @@ -133,6 +134,7 @@ class MeshBayTransport { set onTmdbEnabled(fn) { this._onTmdbEnabled = fn; } set onVideoRoot(fn) { this._onVideoRoot = fn; } set onAudioRoot(fn) { this._onAudioRoot = fn; } + set onPhotoRoots(fn) { this._onPhotoRoots = fn; } set onMusicbrainzConfig(fn) { this._onMusicbrainzConfig = fn; } set onMusicbrainzEnabled(fn) { this._onMusicbrainzEnabled = fn; } set onIndexProgress(fn) { this._onIndexProgress = fn; } @@ -645,6 +647,27 @@ class MeshBayTransport { } /** + * Which folder(s) the Photos app treats as its entry points for this + * group (docs/photos.md §2.1). Unlike setVideoRoot/setAudioRoot, `roots` + * is a whole set, replaced in one signed op — same shape as + * setAppsEnabled. The client normalizes the same way the node does + * (webrtc_server.py's `_do_photo_roots`: trim slashes, drop empties, + * dedupe, sort) so the subject built here matches byte-for-byte what the + * node signs the challenge against. + */ + async setPhotoRoots(roots, signFn) { + const clean = [...new Set( + (roots || []).map((r) => (r || '').replace(/^\/+|\/+$/g, '')).filter(Boolean), + )].sort(); + const msg = await this._sendAndWait({ type: 'photo_roots', v: '0.11', roots: clean }); + if (msg.type === 'error') throw new Error(msg.detail); + if (msg.type === 'admin_challenge') { + return this._authorizeAdminOp(msg, 'photo_roots', clean.join(','), signFn); + } + return msg; + } + + /** * MusicBrainz metadata for one track's path (Music app, docs/musicbay.md * §4.3) — same shape as fetchMediaMeta, minus a season/episode concept: * album-level (release), resolved from the track's own artist/album @@ -1645,6 +1668,12 @@ class MeshBayTransport { this._onAudioRoot(msg.path || ''); } + // Same shape: the operator replaced the Photos app's whole root set + // for this group (docs/photos.md §2.1). + if (msg.type === 'photo_roots_ack' && this._onPhotoRoots) { + this._onPhotoRoots(msg.roots || []); + } + // Node-wide, like tmdb_config_ack above — no token equivalent to hide, // only whether a contact string is configured (docs/musicbay.md §3.2). if (msg.type === 'musicbrainz_config_ack' && this._onMusicbrainzConfig) { |