Android Studio couldn't build the iOS app: resolving the videolan/vlckit
Swift package fails inside it (Xcode's source control service reports
"Communication with the service was interrupted"), ending in
"Missing package product 'VLCKit'". VLCKit's README only documents
CocoaPods and Carthage anyway.
- Podfile with MobileVLCKit 3.7.5 (iOS-only build, 233 MB instead of the
922 MB cross-platform VLCKit 4 binary); build iosApp.xcworkspace.
- VlcPlayerEngine ported to the MobileVLCKit 3 API: it has a real
"ended" state, so the stop bookkeeping and the post-resume seek
workaround needed for VLCKit 4 are gone. Buffering/ES-added
notifications keep arriving while playing, so "playing" is reported
whenever VLC is playing.
- NativePlayerListener.onBuffered removed: MobileVLCKit has no buffering
progress callback.
- Pods/ is ignored; AGENTS.md notes the pod install + workspace setup.
Compose Resources' getString/getPluralString/getStringArray call
getSystemResourceEnvironment() on every lookup, which on desktop asks
Skiko for the system theme. On Linux that is a blocking D-Bus call to the
desktop portal on the calling thread, and from a background coroutine it
segfaulted the GraalVM AppImage on Fedora GNOME when installing a plugin
from the Welcome screen.
Strings never depend on the theme, so app code now uses wrappers in
core/i18n that reuse the resource environment the UI already computed
(published by AppLocaleProvider), falling back to the library lookup
before anything is composed. A test guards against importing the library
getters again.
Fixes#3183
The jam room server now lives in https://github.com/team-spotube/jam-server.
Remove jam-server/ and its deploy workflow, and point code comments, test
instructions, AGENTS.md and the self-hosting note (all locales) at the new
repository.
Security:
- Participants connect with a per-room key derived from a random per-install
secret; others only see an id derived from it, so nobody can reconnect as
someone else (including the host).
- "create" only works on a fresh code or for the room's own host, so empty
rooms can't be taken over.
- Per-socket message and queue budgets disconnect clients that flood; the
host throttles itself well under them.
- Bans also cover the device: a hashed digest of device traits combined with
the network, stored only as an opaque per-room value. Earlier members are
never caught by someone else's ban.
- Peer queue items and suggestions are sanitized (streaming tracks only, no
URLs, https covers); suggestions are credited to the server-attested sender;
local file paths go out as opaque ids. Names are cleaned server-side.
- Room codes and keys use a secure random source; the deploy workflow is
read-only.
Names:
- Participants get a stable generated name ("Mellow Otter") per install, and
can pick their own on the Jam screen or in Jam settings. The participant
list marks "(you)".
Also translate the Jam strings into all locales and add tests for the above.
- Add jam-server/: a Cloudflare Worker with one Durable Object per room code.
It tracks membership, host handoff and kicks/bans, keeps the queue for late
joiners, routes guest commands to the host and serves the room clock.
- Sync playback: devices estimate the server clock, the host sends position
anchors in server time, guests follow by nudging playback rate (seeking only
when far off) and stream the host's audio source when they share a plugin.
- Seek, play/pause, skip and jump apply to the whole room.
- Recover from network loss instead of crashing: socket failures reconnect
with backoff, silent sockets are detected, and an expired room is recreated
by its host.
- Keep cost and battery low: anchors only on change (plus host drift and a
60 s heartbeat), kept in memory rather than storage, free runtime keep-alives
once a minute, rare clock re-syncs, and no polling while paused or buffering.
- Remove the MQTT broker settings and libraries.
- Add sync unit tests, client/session integration tests (JAM_TEST_URL) and a
network-loss test, plus a deploy workflow for the server.
On iOS the CIO client engine competed with Darwin as the default engine and
has no TLS on Kotlin/Native, so HTTPS requests (e.g. plugin discovery) failed.
Remove ktor-client-cio and build every client through a Koin-provided
HttpClientFactory that owns one client on the platform default engine
(Darwin on iOS, OkHttp on Android/desktop). Consumers use the shared
default/json clients or derive their own via create { }, sharing the engine.
- Flatpak manifest (GNOME 51, libvlc built from source) installing from a new
portable spotube-linux-<version>-<arch>.tar.xz, shared with the AUR package
- Release workflow builds and attaches the Linux tarballs for x86_64 and aarch64
- Package files are JMustache templates rendered by :composeApp:renderPackaging<Target>
with the version, release asset checksums and git-cliff release notes; the
metainfo release history is generated
- Publish workflow (workflow_dispatch, release tag input) renders once and pushes to
Flathub, the AUR, WinGet (PR from team-spotube/winget-pkgs) and Chocolatey
- Chocolatey and WinGet updated for the NSIS installer