A modern, network-enabled music player platform built on Rockbox technology. rockboxd.tsiry-sandratraina.com
rust deno navidrome airplay libadwaita zig mpris snapcast mpd rockbox audio subsonic
2

Configure Feed

Select the types of activity you want to include in your feed.

tools: add ./console root launcher for the babashka console

Forwards to tools/console so every build/dev/ops command can be driven
from the repo root without cd'ing in. Prefers bb/clj on PATH, falls back
to `mise exec` using the versions pinned in tools/console/.mise.toml.
Documented in the README under Compiling from Source.

+264 -8
+26
README.md
··· 49 49 - [📦 Downloads](#-downloads) 50 50 - [🧙‍♂️ Systemd Service](#️-systemd-service) 51 51 - [🏗️ Compiling from Source](#️-compiling-from-source) 52 + - [🎛️ Console — one entry point for every build/dev/ops command](#️-console--one-entry-point-for-every-builddevops-command) 52 53 - [🧑‍🔬 Architecture](#-architecture) 53 54 - [📚 APIs](#-apis) 54 55 - [GraphQL](#graphql) ··· 727 728 > **Rebuilding after changes**: after editing C code run `make lib` in 728 729 > `build-lib`; after editing Rust run `cargo build --release`. Then re-run 729 730 > `zig build`. Zig only re-links when the `.a` files are newer than the binary. 731 + 732 + ### 🎛️ Console — one entry point for every build/dev/ops command 733 + 734 + Rather than remembering the exact `make`, `cargo`, `zig`, `bun`, and 735 + `bash scripts/*.sh` incantations, the repo ships a REPL-driven console 736 + ([babashka](https://babashka.org) / Clojure) that wraps them all. The 737 + `./console` launcher at the repo root forwards to it — no need to `cd` in: 738 + 739 + ```sh 740 + ./console # command tour (all available commands) 741 + ./console build:all # firmware → crates → zig → rockboxd 742 + ./console run:debug # RUST_LOG=debug rockboxd 743 + ./console wasm:build # bash scripts/build-wasm.sh 744 + ./console verify:stale # check the stale-binary pitfall 745 + ./console repl # rich terminal REPL (clj -M:rebel) 746 + ./console nrepl # nREPL for your editor (CIDER / Calva) 747 + ``` 748 + 749 + Any other argument is passed straight through to the underlying `bb <task>`. 750 + Tool versions (Java / Clojure / babashka) are pinned in 751 + [`tools/console/.mise.toml`](tools/console/.mise.toml); with 752 + [mise](https://mise.jdx.dev) installed the launcher resolves them 753 + automatically, otherwise install `bb` + `clj` yourself. See 754 + [`tools/console/README.md`](tools/console/README.md) for the full command 755 + reference and how to add your own commands. 730 756 731 757 ### Build the GTK4 desktop app 732 758
+38
bindings/scripts/publish-clojure.sh
··· 1 + #!/usr/bin/env bash 2 + # 3 + # Publish the Clojure jar to Clojars. Unlike the npm/ruby/python scripts (which 4 + # push assets CI already packed), the JVM bindings are built from source here: 5 + # `clojure -T:build deploy` builds the jar and deploys it to Clojars. The jar 6 + # bundles the prebuilt librockbox_ffi for every OS/arch under 7 + # resources/native/<target>/ — staged from the GitHub Release by fetch-libs.sh 8 + # (a JVM jar ships every platform in one artifact), so no local Rust build is 9 + # involved for this binding. 10 + # 11 + # Authenticate first: 12 + # export CLOJARS_USERNAME=<user> CLOJARS_PASSWORD=<clojars-deploy-token> 13 + # The io.github.tsirysndr group must be verified on Clojars (Verified Groups). 14 + # 15 + # Usage: 16 + # bindings/scripts/publish-clojure.sh [--tag <tag>] [--repo <owner/repo>] [--dry-run] 17 + 18 + set -euo pipefail 19 + COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 20 + # shellcheck source=_release_common.sh 21 + . "$COMMON_DIR/_release_common.sh" 22 + ROOT="$(cd "$COMMON_DIR/../.." && pwd)" 23 + 24 + parse_common_args "$@" 25 + [ ${#REST[@]} -eq 0 ] || { echo "unknown argument: ${REST[0]}" >&2; exit 2; } 26 + resolve_repo_and_tag 27 + 28 + command -v clojure >/dev/null 2>&1 || { echo "error: clojure CLI not found — https://clojure.org/guides/install_clojure" >&2; exit 1; } 29 + 30 + echo "== Clojure -> Clojars ==" 31 + # Stage every platform's prebuilt shared lib into resources/native/<t>/ so the 32 + # jar is cross-platform. 33 + run "$COMMON_DIR/fetch-libs.sh" --all --tag "$TAG" --repo "$REPO" 34 + cd "$ROOT/bindings/clojure" 35 + # The version is owned by build.clj (VERSION env, default 0.2.0); bump it there. 36 + echo " deploy io.github.tsirysndr/rockbox-clj-ffi" 37 + run clojure -T:build deploy 38 + echo "Done."
+37
bindings/scripts/publish-elixir.sh
··· 1 + #!/usr/bin/env bash 2 + # 3 + # Publish the Elixir package to Hex. Unlike the JVM bindings, the BEAM bindings 4 + # ship SOURCE (lib + c_src + Makefile) — the consumer compiles the NIF via 5 + # elixir_make. But `mix hex.publish` compiles the project first, and that link 6 + # step needs the local static archive target/release/librockbox_ffi.a, so we 7 + # (re)build rockbox-ffi first (cargo is incremental — a no-op when up to date). 8 + # The published version comes from mix.exs. 9 + # 10 + # Authenticate first: mix hex.user auth (or export HEX_API_KEY=...) 11 + # 12 + # Usage: 13 + # bindings/scripts/publish-elixir.sh [--dry-run] 14 + 15 + set -euo pipefail 16 + COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 17 + # shellcheck source=_release_common.sh 18 + . "$COMMON_DIR/_release_common.sh" 19 + ROOT="$(cd "$COMMON_DIR/../.." && pwd)" 20 + 21 + parse_common_args "$@" 22 + [ ${#REST[@]} -eq 0 ] || { echo "unknown argument: ${REST[0]}" >&2; exit 2; } 23 + 24 + command -v mix >/dev/null 2>&1 || { echo "error: mix (Elixir) not found" >&2; exit 1; } 25 + command -v cargo >/dev/null 2>&1 || { echo "error: cargo not found" >&2; exit 1; } 26 + 27 + echo "== Elixir -> Hex ==" 28 + # Rebuild the native FFI static archive if needed (compile-time dep of the NIF). 29 + run cargo build --release -p rockbox-ffi --manifest-path "$ROOT/Cargo.toml" 30 + cd "$ROOT/bindings/elixir" 31 + run mix deps.get 32 + # hex.publish uploads both the package and the ExDoc docs to HexDocs. The 33 + # explicit `hex.publish docs` afterwards is idempotent and refreshes HexDocs 34 + # even when the package version already exists (re-run docs without a bump). 35 + run mix hex.publish --yes 36 + run mix hex.publish docs --yes 37 + echo "Done."
+35
bindings/scripts/publish-gleam.sh
··· 1 + #!/usr/bin/env bash 2 + # 3 + # Publish the Gleam package to Hex. Like the Elixir binding it ships SOURCE 4 + # (src + c_src + Makefile) — the consumer compiles the NIF. `gleam publish` 5 + # builds the project first, so we (re)build the local static archive 6 + # target/release/librockbox_ffi.a beforehand (cargo is incremental — a no-op 7 + # when up to date). The published version comes from gleam.toml. 8 + # 9 + # Authenticate first: gleam hex authenticate (or export HEXPM_API_KEY=...) 10 + # 11 + # Usage: 12 + # bindings/scripts/publish-gleam.sh [--dry-run] 13 + 14 + set -euo pipefail 15 + COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 16 + # shellcheck source=_release_common.sh 17 + . "$COMMON_DIR/_release_common.sh" 18 + ROOT="$(cd "$COMMON_DIR/../.." && pwd)" 19 + 20 + parse_common_args "$@" 21 + [ ${#REST[@]} -eq 0 ] || { echo "unknown argument: ${REST[0]}" >&2; exit 2; } 22 + 23 + command -v gleam >/dev/null 2>&1 || { echo "error: gleam not found — https://gleam.run" >&2; exit 1; } 24 + command -v cargo >/dev/null 2>&1 || { echo "error: cargo not found" >&2; exit 1; } 25 + 26 + echo "== Gleam -> Hex ==" 27 + # Rebuild the native FFI static archive if needed (compile-time dep of the NIF). 28 + run cargo build --release -p rockbox-ffi --manifest-path "$ROOT/Cargo.toml" 29 + cd "$ROOT/bindings/gleam" 30 + # `gleam publish` uploads the package and its HexDocs. `gleam docs publish` 31 + # afterwards is idempotent and refreshes HexDocs even when the version already 32 + # exists (re-run docs without a version bump). 33 + run gleam publish --yes 34 + run gleam docs publish 35 + echo "Done."
+39
bindings/scripts/publish-kotlin.sh
··· 1 + #!/usr/bin/env bash 2 + # 3 + # Publish the Kotlin artifact to Maven Central (Sonatype Central Portal). Built 4 + # from source here with the vanniktech maven-publish plugin 5 + # (`gradle publishToMavenCentral`). The jar bundles the prebuilt librockbox_ffi 6 + # for every OS/arch under src/main/resources/native/<target>/ — staged from the 7 + # GitHub Release by fetch-libs.sh — so no local Rust build is involved. 8 + # 9 + # Credentials (in ~/.gradle/gradle.properties or ORG_GRADLE_PROJECT_* env): 10 + # mavenCentralUsername / mavenCentralPassword (a Central Portal token) 11 + # signingInMemoryKey / signingInMemoryKeyPassword (ASCII-armored GPG key) 12 + # 13 + # Usage: 14 + # bindings/scripts/publish-kotlin.sh [--tag <tag>] [--repo <owner/repo>] [--dry-run] 15 + 16 + set -euo pipefail 17 + COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 18 + # shellcheck source=_release_common.sh 19 + . "$COMMON_DIR/_release_common.sh" 20 + ROOT="$(cd "$COMMON_DIR/../.." && pwd)" 21 + 22 + parse_common_args "$@" 23 + [ ${#REST[@]} -eq 0 ] || { echo "unknown argument: ${REST[0]}" >&2; exit 2; } 24 + resolve_repo_and_tag 25 + 26 + cd "$ROOT/bindings/kotlin" 27 + # Prefer a project wrapper if one appears later; fall back to system gradle. 28 + if [ -x ./gradlew ]; then GRADLE=./gradlew; else 29 + command -v gradle >/dev/null 2>&1 || { echo "error: gradle not found (and no ./gradlew wrapper)" >&2; exit 1; } 30 + GRADLE=gradle 31 + fi 32 + 33 + echo "== Kotlin -> Maven Central ==" 34 + # Stage every platform's prebuilt shared lib into src/main/resources/native/<t>/. 35 + run "$COMMON_DIR/fetch-libs.sh" --all --tag "$TAG" --repo "$REPO" 36 + # The version is owned by build.gradle.kts (libVersion); bump it there. 37 + echo " publish io.github.tsirysndr:rockbox-ffi" 38 + run "$GRADLE" publishToMavenCentral --no-configuration-cache 39 + echo "Done."
+61
console
··· 1 + #!/usr/bin/env bash 2 + # Root launcher for the Rockbox Console (tools/console). 3 + # 4 + # Forwards to the babashka task surface so you can drive every 5 + # build / dev / ops command from the repo root instead of cd'ing in: 6 + # 7 + # ./console # command tour (bb help) 8 + # ./console build:all # firmware → crates → zig → rockboxd 9 + # ./console run:debug # RUST_LOG=debug rockboxd 10 + # ./console repl # rich terminal REPL (clj -M:rebel) 11 + # ./console nrepl # nREPL for your editor (clj -M:dev) 12 + # 13 + # Any other argument is passed straight through to `bb` in tools/console. 14 + set -euo pipefail 15 + 16 + # Resolve the directory this script lives in (repo root), following symlinks. 17 + SOURCE="${BASH_SOURCE[0]}" 18 + while [ -h "$SOURCE" ]; do 19 + DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" 20 + SOURCE="$(readlink "$SOURCE")" 21 + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" 22 + done 23 + ROOT="$(cd -P "$(dirname "$SOURCE")" && pwd)" 24 + CONSOLE_DIR="$ROOT/tools/console" 25 + 26 + if [ ! -d "$CONSOLE_DIR" ]; then 27 + echo "console: tools/console not found at $CONSOLE_DIR" >&2 28 + exit 1 29 + fi 30 + 31 + cd "$CONSOLE_DIR" 32 + 33 + # Versions (java / clojure / babashka) are pinned in tools/console/.mise.toml. 34 + # Prefer tools already on PATH; otherwise run them through mise. 35 + run() { 36 + local tool="$1"; shift 37 + if command -v "$tool" >/dev/null 2>&1; then 38 + exec "$tool" "$@" 39 + elif command -v mise >/dev/null 2>&1; then 40 + exec mise exec -- "$tool" "$@" 41 + else 42 + echo "console: '$tool' not found and mise is unavailable." >&2 43 + echo " run 'mise install' in tools/console, or install $tool manually." >&2 44 + exit 1 45 + fi 46 + } 47 + 48 + case "${1:-}" in 49 + repl) 50 + run clj -M:rebel 51 + ;; 52 + nrepl) 53 + run clj -M:dev 54 + ;; 55 + "") 56 + run bb help 57 + ;; 58 + *) 59 + run bb "$@" 60 + ;; 61 + esac
+1 -1
tools/console/bb.edn
··· 84 84 bindings:fetch-libs {:doc "Stage prebuilt libs. Usage: bb bindings:fetch-libs [args...]" 85 85 :task (do (require 'console.bindings) 86 86 (apply (resolve 'console.bindings/fetch-libs) *command-line-args*))} 87 - bindings:publish {:doc "Publish packages. Usage: bb bindings:publish <npm|python|ruby> [flags]" 87 + bindings:publish {:doc "Publish packages. Usage: bb bindings:publish <npm|python|ruby|clojure|kotlin|elixir|gleam> [flags]" 88 88 :task (do (require 'console.bindings) 89 89 (apply (resolve 'console.bindings/publish) *command-line-args*))} 90 90
+27 -7
tools/console/src/console/bindings.clj
··· 7 7 (bindings/fetch-libs) ;; stage prebuilt libs from a GH release 8 8 (bindings/publish :npm) ;; publish npm packages from a GH release 9 9 (bindings/publish :python) 10 - (bindings/publish :ruby)" 10 + (bindings/publish :ruby) 11 + (bindings/publish :clojure) ;; -> Clojars (jar bundles prebuilt libs) 12 + (bindings/publish :kotlin) ;; -> Maven Central (jar bundles prebuilt libs) 13 + (bindings/publish :elixir) ;; -> Hex (rebuilds rockbox-ffi first) 14 + (bindings/publish :gleam) ;; -> Hex (rebuilds rockbox-ffi first)" 11 15 (:require [console.shell :as sh])) 12 16 13 17 (defn ffi ··· 27 31 (apply sh/bash "bindings/scripts/fetch-libs.sh" args)) 28 32 29 33 (defn publish 30 - "Publish a language's packages from a GitHub Release. 34 + "Publish a language's packages to its registry. 35 + 36 + npm/python/ruby push the prebuilt assets from a GitHub Release. The JVM 37 + bindings (clojure -> Clojars, kotlin -> Maven Central) build the jar from 38 + source, bundling every platform's prebuilt lib staged from the Release. The 39 + BEAM bindings (elixir/gleam -> Hex, incl. HexDocs) ship source and rebuild 40 + the local rockbox-ffi static archive first (the NIF's compile-time dep). 31 41 32 42 (bindings/publish :npm) 33 43 (bindings/publish :python \"--dry-run\") 34 - (bindings/publish :ruby)" 44 + (bindings/publish :ruby) 45 + (bindings/publish :clojure) 46 + (bindings/publish :kotlin \"--tag\" \"bindings-v0.2.0\") 47 + (bindings/publish :elixir) 48 + (bindings/publish :gleam \"--dry-run\")" 35 49 [lang & args] 36 50 (let [script (case (keyword lang) 37 - :npm "bindings/scripts/publish-npm.sh" 38 - :python "bindings/scripts/publish-python.sh" 39 - :ruby "bindings/scripts/publish-ruby.sh" 51 + :npm "bindings/scripts/publish-npm.sh" 52 + :python "bindings/scripts/publish-python.sh" 53 + :ruby "bindings/scripts/publish-ruby.sh" 54 + :clojure "bindings/scripts/publish-clojure.sh" 55 + :kotlin "bindings/scripts/publish-kotlin.sh" 56 + :elixir "bindings/scripts/publish-elixir.sh" 57 + :gleam "bindings/scripts/publish-gleam.sh" 40 58 (throw (ex-info "Unknown binding target" 41 - {:lang lang :known [:npm :python :ruby]})))] 59 + {:lang lang 60 + :known [:npm :python :ruby 61 + :clojure :kotlin :elixir :gleam]})))] 42 62 (apply sh/bash script args)))