|
6 | 6 | [](https://github.com/stronk-dev/react-librespot-controller/blob/master/LICENSE) |
7 | 7 | [](https://github.com/stronk-dev/react-librespot-controller/actions/workflows/dependabot/dependabot-updates) |
8 | 8 |
|
9 | | -> [`go-librespot`](https://github.com/devgianlu/go-librespot) squeezebox-alike web frontend for small touchscreens |
10 | | -
|
11 | | -Can be deployed standalone or imported as a NPM module. |
| 9 | +A squeezebox-alike React frontend for controlling [`go-librespot`](https://github.com/devgianlu/go-librespot). |
| 10 | + |
| 11 | +Use it as: |
| 12 | +- a standalone page for touchscreens |
| 13 | +- an embedded component in internal dashboards |
| 14 | + |
| 15 | +## UI Scope |
| 16 | +- Layout modes: `auto`, `default`, `widescreen`, `portrait` |
| 17 | +- Views: `Info`, `Playlists`, `Queue`, `Settings` |
| 18 | +- Interactive album art card: tap to browse the current album, playlist, or show |
| 19 | +- In-app navigation from now playing metadata to album, artist, and show details |
| 20 | +- Browse playlists: view tracks with lazy-loaded metadata, play individual tracks |
| 21 | +- Browse artists: portrait image, biography, top tracks, albums, singles, related artists |
| 22 | +- Browse albums: tracklist with durations, explicit badges, clickable artist links |
| 23 | +- Browse shows: episodes with durations and publish dates |
| 24 | +- Playlist cards with cover art, name, description, owner, track count |
| 25 | +- Paginated playlist loading with infinite scroll |
| 26 | +- Client-side image URL normalization for Spotify CDN compatibility |
| 27 | +- Comes with a set of preset themes and sleep timer |
| 28 | +- Podcast controls include skip back 15s and skip forward 30s |
| 29 | + |
| 30 | +## Backend Requirements |
| 31 | +This UI needs a [`go-librespot`](https://github.com/devgianlu/go-librespot) instance with: |
| 32 | +- HTTP API: player controls, metadata endpoints (`/metadata/rootlist`, `/metadata/playlist/{id}`, `/metadata/track/{id}`, `/metadata/album/{id}`, `/metadata/artist/{id}`, `/metadata/show/{id}`, `/metadata/episode/{id}`) |
| 33 | +- WebSocket event stream at `/events` |
| 34 | +- All metadata is fetched via Spotify's native Mercury/protobuf protocols (no Spotify Web API keys needed) |
| 35 | + |
| 36 | +## Standalone Setup |
| 37 | +Create a `.env` file: |
| 38 | + |
| 39 | +```env |
| 40 | +REACT_APP_API_BASE_URL=http://apollo:3678 |
| 41 | +REACT_APP_WS_URL=ws://apollo:3678/events |
| 42 | +REACT_APP_KIOSK_MODE=false |
| 43 | +REACT_APP_HIDE_ON_DISCONNECT=false |
| 44 | +REACT_APP_LAYOUT=auto |
| 45 | +``` |
12 | 46 |
|
13 | | -The player is styled in the [Tokyo Night](https://github.com/tokyo-night/tokyo-night-vscode-theme) colour scheme except for the album image, which emits an ambilight effect based on the colours in the image. |
| 47 | +Install and run: |
14 | 48 |
|
15 | | -## Install `go-librespot` |
16 | | -TODO: refer to OG source instructions + explain `systemd` script |
17 | | -``` |
18 | | -[Unit] |
19 | | -Description=Spotify daemon |
20 | | -Documentation=https://github.com/devgianlu/go-librespot |
21 | | -Wants=sound.target |
22 | | -After=sound.target |
23 | | -Wants=network-online.target |
24 | | -After=network-online.target |
25 | | -
|
26 | | -[Service] |
27 | | -WorkingDirectory=/home/pulseaudio/go-librespot |
28 | | -ExecStart=/usr/local/go/bin/go run /home/pulseaudio/go-librespot/cmd/daemon |
29 | | -Restart=always |
30 | | -RestartSec=12 |
31 | | -
|
32 | | -[Install] |
33 | | -WantedBy=default.target |
| 49 | +```bash |
| 50 | +npm install |
| 51 | +npm run test |
| 52 | +npm run static |
| 53 | +npm run build |
34 | 54 | ``` |
35 | 55 |
|
36 | | -## Config `go-librespot` |
37 | | -TODO: instructions to enable the API |
| 56 | +Commands: |
| 57 | +- `npm run test`: starts the local demo app |
| 58 | +- `npm run static`: creates the static app build |
| 59 | +- `npm run build`: builds the npm package output in `dist/` |
38 | 60 |
|
39 | | -Make sure to mention the bind address. |
| 61 | +## Module Setup |
| 62 | +Install: |
40 | 63 |
|
41 | | -## Standalone |
42 | | - |
43 | | -Create a `.env` file and fill in you API endpoints |
44 | | -``` |
45 | | -REACT_APP_API_BASE_URL=http://apollo:3678 |
46 | | -REACT_APP_WS_URL=ws://apollo:3678/events |
| 64 | +```bash |
| 65 | +npm install --save @stronk-tech/react-librespot-controller |
47 | 66 | ``` |
48 | 67 |
|
49 | | -Run `npm run test` for local debugging. |
50 | | -Run `npm run static` to generate the build folder. |
| 68 | +Use: |
51 | 69 |
|
52 | | -TODO: build instructions |
53 | | - |
54 | | -TODO: nginx instructions. Include HTTPS instructions with local IP whitelist (+hairpin nat) |
| 70 | +```jsx |
| 71 | +import MediaPlayer from "@stronk-tech/react-librespot-controller"; |
55 | 72 |
|
56 | | -TODO: OS instructions (auto-login, open browser, etc) |
| 73 | +<MediaPlayer |
| 74 | + websocketUrl="ws://apollo:3678/events" |
| 75 | + apiBaseUrl="http://apollo:3678" |
| 76 | + hideOnDisconnect={false} |
| 77 | + kioskMode={false} |
| 78 | + layout="auto" |
| 79 | + maxHeight="70vh" |
| 80 | + mobileBreakpoint={768} |
| 81 | + theme="tokyo-night" |
| 82 | +/>; |
| 83 | +``` |
57 | 84 |
|
| 85 | +### Embedding |
58 | 86 |
|
59 | | -## Module |
| 87 | +The player sizes itself automatically via CSS `aspect-ratio`. Height is derived from available width per layout mode — tab content scrolls internally and never resizes the outer card. |
60 | 88 |
|
61 | | -First install the dependency: |
62 | | -``` |
63 | | -npm install --save `@stronk-tech/react-librespot-controller` |
| 89 | +```jsx |
| 90 | +<div style={{ width: "100%", overflow: "hidden" }}> |
| 91 | + <MediaPlayer websocketUrl="ws://apollo:3678/events" apiBaseUrl="http://apollo:3678" /> |
| 92 | +</div> |
64 | 93 | ``` |
65 | 94 |
|
66 | | -Then import the component and fill in your API endpoints: |
67 | | -``` |
68 | | -import MediaPlayer from "@stronk-tech/react-librespot-controller"; |
69 | | -<MediaPlayer websocketUrl={"ws://apollo:3678/events"} apiBaseUrl={"http://apollo:3678"} hideOnDisconnect={false} /> |
| 95 | +Override the max-height cap or panel scroll height with CSS vars: |
| 96 | + |
| 97 | +```css |
| 98 | +.my-wrapper { |
| 99 | + --spotify-player-max-height: 70vh; |
| 100 | + --spotify-player-panel-max-height: 48vh; |
| 101 | +} |
70 | 102 | ``` |
71 | 103 |
|
72 | 104 | ### Props |
| 105 | +- `websocketUrl`: WebSocket URL for `go-librespot` events |
| 106 | +- `apiBaseUrl`: HTTP API base URL for `go-librespot` |
| 107 | +- `hideOnDisconnect`: hides the component when API connection is down |
| 108 | +- `kioskMode`: uses full-screen behavior |
| 109 | +- `autoDetectKiosk`: auto-enables kiosk mode when card fills most of viewport (default `false`) |
| 110 | +- `layout`: `auto`, `default`, `widescreen`, `portrait` |
| 111 | +- `maxHeight`: maximum component height cap (CSS value, default `100vh`) |
| 112 | +- `panelMaxHeight`: maximum height for scrollable content panels like playlists/details (default `60vh`) |
| 113 | +- `mobileBreakpoint`: when `layout="auto"`, force portrait at or below this viewport width (default `768`) |
| 114 | +- `theme`: optional preset name; if omitted, saved theme is used, default is `tokyo-night` |
| 115 | + |
| 116 | +### Theme Presets |
| 117 | +`tokyo-night`, `tokyo-night-light`, `dracula`, `nord`, `catppuccin`, `catppuccin-light`, `gruvbox`, `gruvbox-light`, `one-dark`, `github-dark`, `rose-pine`, `solarized`, `solarized-light`, `ayu-mirage` |
| 118 | + |
| 119 | +### Preview |
| 120 | + |
| 121 | + |
| 122 | +### Default view / Kiosk mode |
| 123 | + |
73 | 124 |
|
74 | | -- `hideOnDisconnect`: When `true`, the entire component will hide itself when there is no connection to the API endpoint. Otherwise it will display an error state. |
75 | | -- `websocketUrl`: Full URL to the WebSocket endpoint of the `go-librespot` client. |
76 | | -- `apiBaseUrl`: Full URL to the HTTP API endpoint of the `go-librespot` client. |
77 | | -- `kioskMode`: When `true`, the component will fill the entire screen. Otherwise it will fill the available width and base the layout on the width only. It is recommended to leave this option `false` when importing the module into an existing webpage and to `true` when you are running the player standalone on a touch screen. |
78 | | -- `layout`: Can be `auto`, `default`, `widescreen`, `portrait`. See the screenshots for how the layouts look like. |
| 125 | +### Widescreen |
| 126 | + |
79 | 127 |
|
80 | | -# Screen shots |
81 | | -The player arranges itself based on the screen dimensions, with three possible layouts: |
| 128 | +### Portrait |
| 129 | + |
82 | 130 |
|
83 | | -### Default layout |
84 | | - |
| 131 | +### Queue |
| 132 | + |
85 | 133 |
|
86 | | -### Widescreen |
87 | | - |
88 | | - |
89 | | - |
| 134 | +### Settings |
| 135 | + |
90 | 136 |
|
91 | | -### Portrait |
92 | | - |
| 137 | +### Browse |
| 138 | + |
0 commit comments