{"id":47714943,"url":"https://github.com/charlexh/sideb","last_synced_at":"2026-05-24T03:08:12.263Z","repository":{"id":347131353,"uuid":"1192586144","full_name":"CharlexH/SideB","owner":"CharlexH","description":"Retro cassette-style music player for TrimUI Brick with Spotify Connect, offline favorites, and local MP3 playback","archived":false,"fork":false,"pushed_at":"2026-04-01T03:57:43.000Z","size":11754,"stargazers_count":8,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T04:56:37.658Z","etag":null,"topics":["embedded","framebuffer","rust","spotify-connect","trimui"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CharlexH.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-26T11:11:56.000Z","updated_at":"2026-04-01T03:57:46.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/CharlexH/SideB","commit_stats":null,"previous_names":["charlexh/trimui-spotify","charlexh/sideb"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/CharlexH/SideB","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CharlexH%2FSideB","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CharlexH%2FSideB/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CharlexH%2FSideB/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CharlexH%2FSideB/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CharlexH","download_url":"https://codeload.github.com/CharlexH/SideB/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CharlexH%2FSideB/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33419586,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T22:14:44.296Z","status":"online","status_checked_at":"2026-05-24T02:00:06.296Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["embedded","framebuffer","rust","spotify-connect","trimui"],"created_at":"2026-04-02T18:51:16.378Z","updated_at":"2026-05-24T03:08:12.258Z","avatar_url":"https://github.com/CharlexH.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SideB\n\nSideB is a retro cassette-style music player for [TrimUI Brick](https://trimui.com) with Spotify Connect, offline favorites, and local MP3 playback.\n\nLatest release: `v1.0.10`\n\n- Local MP3 playback now uses the bundled `ffmpeg-lite` decoder on NextUI\n- Playback failures show short on-device notices such as `Missing ffmpeg` or `Missing aplay`\n- The waiting screen now mentions MP3 import alongside Spotify Connect\n\n## Screenshots 📸\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003cimg src=\"screenshots/spotify_connecting.png\" alt=\"Spotify connecting screen\" width=\"360\"\u003e\u003cbr\u003e\n      \u003cstrong\u003eSpotify connecting\u003c/strong\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003cimg src=\"screenshots/favlist.png\" alt=\"Favorites list\" width=\"360\"\u003e\u003cbr\u003e\n      \u003cstrong\u003eFAV LIST\u003c/strong\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003cimg src=\"screenshots/offline_playing.png\" alt=\"Offline local playback\" width=\"360\"\u003e\u003cbr\u003e\n      \u003cstrong\u003eOffline playback\u003c/strong\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003cimg src=\"screenshots/waiting.png\" alt=\"Waiting screen\" width=\"360\"\u003e\u003cbr\u003e\n      \u003cstrong\u003eWaiting screen\u003c/strong\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Features ✨\n\n### Spotify streaming 🎧\n\n- Turns the TrimUI Brick into a Spotify Connect receiver on your local network\n- Shows cover art, playback state, and cassette animation directly on `/dev/fb0`\n- Supports hardware controls for play, pause, skip, volume, favorites, and list navigation\n\n### Offline FAV list 💾\n\n- Save the current track with `X`\n- Browse and manage favorites in the fullscreen `FAV LIST`\n- Play downloaded favorites locally with shuffle, previous, and next track controls\n\n### Local uploads 📥\n\n- Drop MP3 files into `data/imports/`\n- SideB imports tags and cover art automatically\n- Imported tracks are added to `FAV LIST` and behave like local favorites\n\n## How It Works 🧠\n\nThe app consists of two components:\n\n- **[go-librespot](https://github.com/devgianlu/go-librespot)**: Spotify Connect backend — handles authentication, audio streaming, and exposes a local HTTP/WebSocket API on port `3678`\n- **spotify-ui-rs** (this repo): Rust framebuffer UI — reads input from `/dev/input`, renders the cassette scene to `/dev/fb0`, and communicates with `go-librespot` via its local API\n\n### Offline playback pipeline\n\nWhen a user marks a track as a favorite, the app searches for multiple matching candidates on YouTube using [yt-dlp](https://github.com/yt-dlp/yt-dlp), scores them by duration match against Spotify metadata, title similarity, and channel quality, then downloads the best match as an MP3 file on the SD card. After download, the actual file duration is validated against the Spotify track length to reject mismatched results. Downloads use a bundled FFmpeg-compatible audio transcoder with MP3 encoder support. Cached audio is played back through the bundled `ffmpeg-lite → aplay` subprocess pipeline. Cover art is fetched from the Spotify CDN or copied from the local cover cache. Incomplete downloads are automatically resumed on the next app launch.\n\nFor manual local playback, users can also drop MP3 files into `data/imports/`. SideB scans that folder automatically, reads MP3 metadata, moves the file into `data/music/`, extracts embedded cover art with the device's system ffmpeg when available, and adds the track to `FAV LIST` as a managed local item.\n\n**Important**: The app does **not** intercept, decrypt, or extract audio from Spotify streams. Spotify playback and offline caching use entirely separate audio paths.\n\n## Offline Playback \u0026 Legal Notice ⚠️\n\nThis project provides an offline caching mechanism strictly for **personal, non-commercial use**. By using this feature, you acknowledge and agree to the following:\n\n- **No content is hosted or distributed by this project.** All audio files are cached locally on the user's own device and are never uploaded, shared, or made available to third parties.\n- **The project does not circumvent any DRM or copy protection.** Spotify audio streams are not intercepted or recorded. Offline caching relies on publicly available content retrieved via [yt-dlp](https://github.com/yt-dlp/yt-dlp) from YouTube.\n- **Users are solely responsible for ensuring their use complies with applicable copyright laws and the terms of service of any third-party platform** (including YouTube and Spotify). The authors and contributors of this project assume no liability for how the software is used.\n- **If you are a rights holder** and believe this project facilitates infringement of your rights, please [open an issue](https://github.com/CharlexH/SideB/issues) and we will address it promptly.\n\nThis software is provided as-is for educational and personal use. It is not intended to promote or facilitate unauthorized copying or distribution of copyrighted material.\n\n## Requirements ✅\n\n- TrimUI Brick\n- NextUI, Stock OS, or [CrossMix OS](https://github.com/cizia64/CrossMix-OS) `1.1.1+`\n- Spotify Premium account\n- Wi-Fi on the same network as your Spotify client (for streaming mode)\n\n## Controls 🎮\n\n| Button | Action |\n|--------|--------|\n| **A** | Play / Pause |\n| **← / →** | Previous / Next track |\n| **↑ / ↓** | Volume up / down |\n| **X** | Favorite, or press twice to remove a favorite |\n| **Y** | Open / close playlist |\n| **B** / **MENU** | Exit app |\n\n## Usage 🚀\n\n### Spotify streaming\n\n1. Launch **SideB** from the TrimUI app menu.\n2. Open Spotify on your phone, desktop, or tablet.\n3. Pick **TrimUI Brick** from Spotify Connect.\n4. Use the hardware buttons on the device for playback, volume, favorites, and list access.\n\n### Offline FAV list\n\n1. Start playback from Spotify Connect.\n2. Press **X** to add the current track to favorites.\n3. Wait for the background download to finish.\n4. Press **A** on the device to start local playback from the downloaded library, or press **Y** to pick a downloaded track from `FAV LIST`.\n\n### Local uploads\n\nCopy `.mp3` files into the `data/imports/` folder inside the app directory, either directly or inside subfolders:\n\n```text\nNextUI   -\u003e /mnt/SDCARD/Tools/tg5040/SideB.pak/data/imports/\nStock    -\u003e /mnt/SDCARD/Apps/SideB/data/imports/\nCrossMix -\u003e /mnt/SDCARD/Apps/SideB/data/imports/\n```\n\nSideB scans that folder recursively at startup and while running. Imported files are moved into `data/music/`, then added to `FAV LIST` and treated like downloaded local tracks.\n\nNotes:\n\n- Current manual import support is `MP3` only.\n- Embedded cover art is preferred. If no embedded cover exists, SideB will try to use a same-name sidecar image next to the MP3 during import.\n- Press **X** once to show the remove confirmation, then press **X** again to actually remove the track from favorites.\n- If you remove the track that is currently playing locally, SideB keeps the managed file until playback moves away from that track. If you favorite it again before switching tracks, the cached file is preserved.\n\n## Troubleshooting: Downloads Failing\n\nIf downloads fail with `Sign in to confirm you're not a bot` in the log (`/tmp/sideb.log`), YouTube needs valid cookies.\n\n**Use Firefox** (recommended — Chrome rotates cookies on export, making them expire immediately):\n\n1. Install [Firefox](https://www.mozilla.org/firefox/) and log into YouTube\n2. Install [yt-dlp](https://github.com/yt-dlp/yt-dlp) (`brew install yt-dlp` / `winget install yt-dlp`)\n3. Export cookies and copy to the device:\n   ```bash\n   yt-dlp --cookies-from-browser firefox --cookies cookies.txt -s \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\"\n   ```\n4. Copy `cookies.txt` to the device SD card as `data/yt-dlp-cookies.txt`\n\nRestart SideB and pending downloads will resume automatically.\n\n\u003e **Why not Chrome?** Since late 2024, Chrome automatically invalidates exported cookies as a security measure. Firefox does not have this limitation.\n\n## Build 🔧\n\n### Rust UI (current)\n\n```bash\ngit clone https://github.com/CharlexH/SideB.git\ncd SideB/spotify-ui-rs\ncargo build --release --target aarch64-unknown-linux-musl\ncp target/aarch64-unknown-linux-musl/release/sideb ../package/SideB.pak/sideb\n```\n\n### Required runtime files (not tracked in git)\n\n- `package/SideB.pak/go-librespot` — Spotify Connect backend binary\n- `package/SideB.pak/ffmpeg-lite` — bundled FFmpeg-compatible audio transcoder with MP3 encoding and local playback PCM output support; keep it minimal and audio-focused\n- `package/SideB.pak/yt-dlp` — YouTube audio downloader (aarch64 binary)\n- `package/SideB.pak/resources/ca-certificates.crt` — TLS root certificates\n\n`package/SideB.pak/resources/font_mono.ttf` is tracked and should already be present after checkout.\n\n## Package Releases 📦\n\nBuild all release archives:\n\n```bash\n./scripts/package.sh\n```\n\nThis produces:\n\n- `dist/SideB-\u003cversion\u003e-nextui.zip`\n- `dist/SideB-\u003cversion\u003e-stock.zip`\n- `dist/SideB-\u003cversion\u003e-crossmix.zip`\n\nArchive layouts:\n\n- `nextui`: pak contents such as `launch.sh`, `sideb`, `resources/...`, and `data/...`; the NextUI Pak Store extracts this archive into `Tools/tg5040/SideB.pak/`\n- `stock`: `Apps/SideB/...`\n- `crossmix`: `Apps/SideB/...`\n\nSupported in this release:\n\n- `NextUI` — validated on device\n- `Stock` — package layout and launcher verified\n- `CrossMix` — package layout and launcher verified\n\n## Deploy 🚀\n\nManual installation:\n\n```text\nNextUI   -\u003e extract the nextui archive into /mnt/SDCARD/Tools/tg5040/SideB.pak/\nStock    -\u003e extract the stock archive at the SD-card root so it creates /mnt/SDCARD/Apps/SideB/\nCrossMix -\u003e extract the crossmix archive at the SD-card root so it creates /mnt/SDCARD/Apps/SideB/\n```\n\nLaunch **SideB** from the TrimUI app menu, then select **TrimUI Brick** from Spotify Connect on another device.\n\n## GitHub Release 🏷️\n\nPublic releases attach three installable archives:\n\n- `SideB-\u003cversion\u003e-nextui.zip`\n- `SideB-\u003cversion\u003e-stock.zip`\n- `SideB-\u003cversion\u003e-crossmix.zip`\n\nThe NextUI Pak Store consumes the `nextui` archive via [`pak.json`](pak.json).\n\nCurrent release tag: `v1.0.10`\n\n## Repo Layout 🗂️\n\n```text\nspotify-ui-rs/                  Rust UI source\npackage/SideB.pak/              Local runtime staging folder and source assets\npackage/SideB.pak/data/         Runtime config copied into release packages\npackage/SideB.pak/resources/    UI images, icons, and fonts\npackaging/                      Platform wrappers and release metadata\nscripts/package.sh              Multi-platform release packager\n```\n\n## Configuration ⚙️\n\nMain config: [`package/SideB.pak/data/config.yml`](package/SideB.pak/data/config.yml)\n\n```yaml\ndevice_name: \"TrimUI Brick\"\ndevice_type: \"speaker\"\naudio_backend: \"alsa\"\naudio_device: \"default\"\nbitrate: 160\nvolume_steps: 100\ninitial_volume: 80\nzeroconf_enabled: true\n```\n\nThe UI communicates with `go-librespot` at `http://127.0.0.1:3678`.\n\n## Credits \u0026 Third-Party Software 🙏\n\nThis project builds upon the following open-source projects:\n\n| Project | License | Usage |\n|---------|---------|-------|\n| [go-librespot](https://github.com/devgianlu/go-librespot) | GPL-3.0 | Spotify Connect backend |\n| [yt-dlp](https://github.com/yt-dlp/yt-dlp) | Unlicense | YouTube audio search and download for offline caching |\n| [FFmpeg](https://ffmpeg.org/) | LGPL-2.1+ / GPL-2.0+ | Audio decoding and playback pipeline |\n| [CrossMix OS](https://github.com/cizia64/CrossMix-OS) | — | Firmware base for TrimUI devices |\n\nHardware: [TrimUI Brick](https://trimui.com)\n\nRelease archives also include:\n\n- `LICENSES/NOTICE.md`\n- `LICENSES/THIRD_PARTY_SOURCES.md`\n- Full upstream license texts for bundled binaries\n\nBefore publishing a GitHub release, record the exact upstream versions and checksums of the bundled third-party binaries in `LICENSES/THIRD_PARTY_SOURCES.md`.\n\n## License 📄\n\nApache-2.0. See [LICENSE](LICENSE).\n\n## Disclaimer ⚖️\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. THE AUTHORS ARE NOT RESPONSIBLE FOR ANY MISUSE OF THIS SOFTWARE OR FOR ANY VIOLATION OF THIRD-PARTY TERMS OF SERVICE OR APPLICABLE LAWS. USE AT YOUR OWN RISK.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharlexh%2Fsideb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcharlexh%2Fsideb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharlexh%2Fsideb/lists"}