From 256f8185f5104acbf09779058acd7c4758456119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 15 Aug 2026 17:36:59 +0000 Subject: [PATCH] music leaves the platform repository entirely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 41 files moved to ~/officer-dev/plugins/music, now its own git repository pushed to gitea.officer.dev/plugins/music. The platform tracks none of it. Done in the order the design intends, and each step verified rather than assumed: uninstall via /plugins sidecar stopped and deleted from PM2, ecosystem entry removed, routes unmounted, icon unpublished, TABLES DELIBERATELY KEPT copy out, verify `diff -r` identical before removing anything git rm from the platform regenerate the barrel 1 plugin schema now: offscale bun db:push --force music_favorites, music_playlists, music_playlist_items and music_now_playing DROPPED That last step is the one destructive property this system has, and until today nobody had watched it happen. `plugins/schema.ts` has said since it was written that the barrel follows plugin DIRECTORIES rather than the install table, so that uninstalling keeps every row and only deleting the source can lose data. Both halves are now observed: uninstall kept the tables, deleting the directory dropped them. Nothing else moved — headscale_servers intact, plugin_installs correct, all 9 permission grants untouched. The platform boots clean, mounts /example and /offscale, reports no broken plugins, and answers 404 on /api/music. ── What this cost, and it is the interesting part ── The test suite went from 797 tests across 58 files to 776 across 55. Music took its three test files with it, and one of the seven known failures — the cliamp socket test — left with them. So the platform no longer tests music at all, and music's repository has no CI. That is correct and it is also a gap. And the repository does not build. Measured from outside the platform checkout, every one of `hooks/useClient`, `officerdev` and `officerdb/db` fails to resolve: 7 imports in the backend, ~29 in the frontend. A symlink back into platform/plugins/ was tested and does resolve, but the owner declined it — the point of moving was to stop pretending. So music is now a source of truth that cannot run, and the host API has to become something a plugin depends on rather than reaches into. That was already the known problem; it is now the blocking one. --- plugins/music/MUSIC_API.md | 363 ------- plugins/music/PLUGIN.md | 284 ------ plugins/music/api/router.ts | 46 - plugins/music/assets/icon.png | Bin 126389 -> 0 bytes plugins/music/cliamp/AudioStreamPlayer.tsx | 185 ---- plugins/music/cliamp/CliampPanel.tsx | 36 - plugins/music/cliamp/asoundrc | 9 - plugins/music/cliamp/cliamp-ws.test.ts | 66 -- plugins/music/cliamp/cliamp-ws.ts | 232 ----- plugins/music/cliamp/pulse-audio.ts | 48 - plugins/music/cliamp/relay.ts | 110 -- plugins/music/db/queries.ts | 233 ----- plugins/music/db/schema.ts | 80 -- plugins/music/manifest.ts | 103 -- plugins/music/scripts/reindex-music.ts | 120 --- plugins/music/sidecar/index.ts | 503 --------- plugins/music/sidecar/indexer.ts | 1079 -------------------- plugins/music/sidecar/nightly-reindex.ts | 45 - plugins/music/sidecar/stream-audio.ts | 110 -- plugins/music/web/FavoritesView.tsx | 215 ---- plugins/music/web/LyricsPane.tsx | 84 -- plugins/music/web/LyricsPanel.tsx | 49 - plugins/music/web/MusicBrowser.tsx | 226 ---- plugins/music/web/MusicDetail.tsx | 438 -------- plugins/music/web/MusicHeart.tsx | 43 - plugins/music/web/MusicMiniBar.tsx | 103 -- plugins/music/web/MusicPlayerHost.tsx | 393 ------- plugins/music/web/gapless-engine.test.ts | 191 ---- plugins/music/web/gapless-engine.ts | 375 ------- plugins/music/web/layout.ts | 11 - plugins/music/web/lyrics.test.ts | 73 -- plugins/music/web/lyrics.ts | 64 -- plugins/music/web/panels.ts | 18 - plugins/music/web/player-time.ts | 42 - plugins/music/web/shared.ts | 152 --- plugins/music/web/useLyrics.ts | 78 -- plugins/music/web/useLyricsOpen.ts | 25 - plugins/music/web/useMusicFavorites.ts | 52 - plugins/music/web/useMusicPlayer.ts | 47 - plugins/music/web/usePlayerClock.ts | 16 - plugins/music/widgets/index.tsx | 224 ---- 41 files changed, 6571 deletions(-) delete mode 100644 plugins/music/MUSIC_API.md delete mode 100644 plugins/music/PLUGIN.md delete mode 100644 plugins/music/api/router.ts delete mode 100644 plugins/music/assets/icon.png delete mode 100644 plugins/music/cliamp/AudioStreamPlayer.tsx delete mode 100644 plugins/music/cliamp/CliampPanel.tsx delete mode 100644 plugins/music/cliamp/asoundrc delete mode 100644 plugins/music/cliamp/cliamp-ws.test.ts delete mode 100644 plugins/music/cliamp/cliamp-ws.ts delete mode 100644 plugins/music/cliamp/pulse-audio.ts delete mode 100644 plugins/music/cliamp/relay.ts delete mode 100644 plugins/music/db/queries.ts delete mode 100644 plugins/music/db/schema.ts delete mode 100644 plugins/music/manifest.ts delete mode 100644 plugins/music/scripts/reindex-music.ts delete mode 100644 plugins/music/sidecar/index.ts delete mode 100644 plugins/music/sidecar/indexer.ts delete mode 100644 plugins/music/sidecar/nightly-reindex.ts delete mode 100644 plugins/music/sidecar/stream-audio.ts delete mode 100644 plugins/music/web/FavoritesView.tsx delete mode 100644 plugins/music/web/LyricsPane.tsx delete mode 100644 plugins/music/web/LyricsPanel.tsx delete mode 100644 plugins/music/web/MusicBrowser.tsx delete mode 100644 plugins/music/web/MusicDetail.tsx delete mode 100644 plugins/music/web/MusicHeart.tsx delete mode 100644 plugins/music/web/MusicMiniBar.tsx delete mode 100644 plugins/music/web/MusicPlayerHost.tsx delete mode 100644 plugins/music/web/gapless-engine.test.ts delete mode 100644 plugins/music/web/gapless-engine.ts delete mode 100644 plugins/music/web/layout.ts delete mode 100644 plugins/music/web/lyrics.test.ts delete mode 100644 plugins/music/web/lyrics.ts delete mode 100644 plugins/music/web/panels.ts delete mode 100644 plugins/music/web/player-time.ts delete mode 100644 plugins/music/web/shared.ts delete mode 100644 plugins/music/web/useLyrics.ts delete mode 100644 plugins/music/web/useLyricsOpen.ts delete mode 100644 plugins/music/web/useMusicFavorites.ts delete mode 100644 plugins/music/web/useMusicPlayer.ts delete mode 100644 plugins/music/web/usePlayerClock.ts delete mode 100644 plugins/music/widgets/index.tsx diff --git a/plugins/music/MUSIC_API.md b/plugins/music/MUSIC_API.md deleted file mode 100644 index 3f07484c..00000000 --- a/plugins/music/MUSIC_API.md +++ /dev/null @@ -1,363 +0,0 @@ -# Music API (`/api/music/*`) - -Platform API the mobile app uses to **stream music** and **sync a server-built library index**, so the -app no longer pre-downloads whole tracks or walks/ID3-parses the library on-device. - -- **Source of truth for the code:** `plugins/music/sidecar/index.ts` (the `officer-music` sidecar owns - all of this; the platform `/api/music/*` route is a transparent auth-ing proxy). -- **Music root:** `~/Music` on the server. All `path` values are **home-relative** (e.g. - `Music/Albums/AC-DC/[1980] Back in Black/01 Hells Bells.mp3`), identical to `/api/file-browser/raw`. -- **``:** an album folder path **relative to the `Music` root** (e.g. `Albums/AC-DC/[1980] Back in Black`). - -## Auth - -Every endpoint is behind the standard user auth. Two ways to pass the JWT: - -- **Header:** `Authorization: Bearer ` (normal fetches). -- **Query:** `?token=` — for media elements / native players that can't set headers (audio, images). - -`401` = no token · `403` = invalid/expired token. - ---- - -## 1. Playback — stream a track - -``` -GET /api/music/stream?path=&token= -``` - -Byte-range streaming so the player can **seek without downloading the whole file**. - -| Case | Status | Headers | -| --------------------- | ------ | --------------------------------------------------------------------------------------------- | -| No `Range` | `200` | `Content-Type`, `Content-Length`, `Accept-Ranges: bytes`, `X-Audio-Duration` | -| With `Range: bytes=…` | `206` | `Content-Range`, `Content-Length`, `Accept-Ranges: bytes`, `Content-Type`, `X-Audio-Duration` | - -- **`X-Audio-Duration`**: track duration in **seconds** (ffprobe-derived). Read this to set the player's - duration up front — it's the fix for AVPlayer reporting an _indefinite_ duration on progressively-streamed - VBR MP3s. No need to scan the file. -- Errors: `400` invalid/missing path · `404` not found · `416` bad range. - -``` -curl -H "Authorization: Bearer $JWT" -H "Range: bytes=0-1023" \ - "$BASE/api/music/stream?path=Music/Albums/AC-DC/[1980]%20Back%20in%20Black/01%20Hells%20Bells.mp3" -D - -``` - ---- - -## 2. Library index — the synced cache - -The server maintains a cache tree that **mirrors the library**, one entry per album folder. The app syncs -this instead of walking + ID3-parsing the library itself. - -Each album has a **version stamp `v`** (hash of the album's source files' names/sizes/mtimes + its cover). -`v` changes **iff the album's content changed** → it's the whole basis of the diff: _unchanged `v` ⇒ skip_. - -### 2.1 Manifest — one call, whole library - -``` -GET /api/music/manifest -``` - -```jsonc -{ - "version": 1, - "generatedAt": 1785034701973, // ms; when the index was last built - "albums": { - "Albums/AC-DC/[1980] Back in Black": { "v": "50856380f1ca8f9", "cover": true, "tracks": 10 }, - "DJ Sets/Dave Clarke": { "v": "a1b2c3d4e5f6a7b", "cover": false, "tracks": 3 }, - "Albums/Metallica/[1989] Live Shit": { "v": "beefbeefbeefbee", "cover": true, "tracks": 0, "videos": 2 }, - "Albums/AC-DC": { "v": "c0ffee1234567890", "cover": true, "tracks": 0, "disco": true }, - // … - }, -} -``` - -`404` if the index has never been built (see §3). Entries with **`tracks: 0`** are container folders (e.g. an -**artist** folder). An entry with **`disco: true`** is an artist folder that has a discography — fetch its -grouping via `/discography` (§2.4). **`videos: N`** (optional) counts video files (concerts, clips) that live -directly in that artist/album folder — their per-file metadata is in that folder's `meta.json` (§2.2). A folder -may have any mix of `tracks`, `videos`, and `disco`. - -### 2.2 Album metadata - -``` -GET /api/music/meta?path= -``` - -Returns the album's `meta.json`. Sends `ETag: `; a request with `If-None-Match: ` returns `304`. - -```jsonc -{ - "path": "Albums/AC-DC/[1980] Back in Black", - "cover": "cover.jpg", // present only if a cover exists - "tracks": [ - { - "file": "01 Hells Bells.mp3", // filename within the album folder - "title": "Hells Bells", - "artist": "AC/DC", - "albumArtist": "AC/DC", - "album": "Back in Black", - "track": "1", - "year": "1980", - "durationSec": 312, - "lyrics": "lrc", // present if lyrics exist: "lrc" = synced, "txt" = plain (see §2.3.2) - }, - // … - ], - "videos": [ - // present only for folders that contain video files - { - "file": "1989 - Seattle.mp4", // filename within the folder - "title": "Live Shit: Seattle", // from the container title tag, if any - "durationSec": 8130, - "width": 1280, - "height": 720, - "poster": "posters/1989 - Seattle.mp4.jpg", // present when a poster was generated (see §2.3.1) - }, - // … - ], -} -``` - -All track/video fields except `file` are optional (absent when the tag/stream info is missing). `videos` is -omitted entirely when the folder has none. -To stream a track or video: `GET /api/music/stream?path=Music//` (byte-range; works for `.mp4`). - -### 2.3 Cover - -``` -GET /api/music/cover?path= -``` - -Compressed JPEG (≤600px on the long edge, ~30–80 KB). Sends `ETag: `; `If-None-Match: ` → `304`. -Only meaningful when the manifest entry has `"cover": true`. - -#### 2.3.1 Video poster - -``` -GET /api/music/poster?path=&file=