- Add fluxa-streaming-engine companion server (torrent start/stop, ffmpeg transcode/probe, OAuth token exchange) for the web build - Add SSRF guard restricting transcode/probe url param to http(s) loopback hosts, blocking file:// and remote-host abuse via ffmpeg - Add src/ffi.rs string-routed RPC dispatcher and wasm bindings - Add headless engine profile module and several FFI surface additions (scrobble plans, episode navigation, collections import/export) - Remove dead runtime::msg/update scaffolding and addon_transport trait - Remove fluxa_play.rs dev scratch binary (hardcoded scraper, not part of the product) - Reorder dv_rewrite.rs so implementation precedes its test module; strip decorative section dividers - Add docs/, fuzz/ targets, and a rewritten README - Fix .gitignore to cover fluxa-streaming-engine/target
3.3 KiB
Platform integration guide
fluxa-core ships as a compiled native library. Each platform links against it differently.
Android
How it links: Two paths, both real.
Primary path — JNI (bindings/jni.rs): ~157 hand-written extern "system" functions called directly from FluxaCoreNative.kt. This covers most of the crate's surface area, including the headless engine, stream selection, DV policy, calendar, watchlist, and more.
Secondary path — UniFFI: A small subset of functions in bindings/uniffi.rs marked #[uniffi::export] generate Kotlin bindings (FluxaCoreUniFfi.kt). This covers headless engine lifecycle, coreCapabilities, and version.
Setup
- Build
.sofiles for each ABI (see building.md). - Place them in
src/main/jniLibs/<abi>/libfluxa_core.soin the Android project. System.loadLibrary("fluxa_core")in yourApplicationclass or via the generated UniFFI loader.- Call
FluxaCoreNative.createHeadlessEngine(initialJson)to get a handle, then dispatch actions and complete effects throughFluxaCoreNative.*methods.
Adding a new capability for Android
Add a new string_fn! (or equivalent) entry to src/bindings/jni.rs. If the same capability also needs to reach desktop or Swift, add a route to src/ffi.rs::route_* too. There is no shared registration — you wire each platform separately.
Desktop (Linux / macOS / Windows)
How it links: Plain Rust path dependency in fluxa-desktop (a Tauri app):
fluxa_core = { path = "../../fluxa-core" }
No FFI marshaling — it calls Rust functions directly.
Two call sites:
-
FluxaCore::*methods (insrc/core_api.rs) for the 8 things desktop calls without going through the dispatcher: headless engine lifecycle,stream_playback_info_json,torrent_runtime_info_json,player_buffer_targets_json,offline_download_plan_json. -
fluxa_core::ffi::core_invoke(method, args_json)for everything else — the full ~115-method dispatcher.
Adding a new capability for desktop
If desktop needs it via core_invoke: add a route arm to the appropriate route_* function in src/ffi.rs.
If desktop needs a direct FluxaCore method (unusual — only do this if core_invoke is genuinely not suitable): add it to src/core_api.rs and confirm there's a real call site in fluxa-desktop/src-tauri/src/ before adding.
iOS / tvOS
How it links: Via UniFFI-generated Swift bindings. src/bindings/uniffi.rs::core_invoke is the intended entry point — Swift calls it instead of binding each helper individually.
Build the crate with --features uniffi-bindings, generate Swift source with uniffi-bindgen, and link the resulting .xcframework into the Xcode project.
webOS
How it links: Via wasm-bindgen exports in src/bindings/wasm.rs.
Build with --no-default-features --features wasm using wasm-pack or cargo build --target wasm32-unknown-unknown. The WASM module exposes the same core_invoke entry point.
Wire contract
The JSON field names and nesting in anything that crosses dispatch / completeEffect / core_invoke are read by platform code in the consuming repos. Do not rename wire fields without coordinating with the consumer. Internal Rust refactors are safe as long as #[serde(rename_all = "camelCase")] output is unchanged.