From 5fb1c08afd946635d66e61ff0ea500cc632899bf Mon Sep 17 00:00:00 2001 From: KhooLy <73142442+KhooLy@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:13:54 +0300 Subject: [PATCH] Add Apple streaming engine bridge --- fluxa-streaming-engine/Cargo.toml | 7 +- fluxa-streaming-engine/src/bindings/apple.rs | 104 +++++++++++++++++++ fluxa-streaming-engine/src/bindings/mod.rs | 2 + uniffi.toml | 2 +- 4 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 fluxa-streaming-engine/src/bindings/apple.rs diff --git a/fluxa-streaming-engine/Cargo.toml b/fluxa-streaming-engine/Cargo.toml index d5f329d..8307d4d 100644 --- a/fluxa-streaming-engine/Cargo.toml +++ b/fluxa-streaming-engine/Cargo.toml @@ -4,14 +4,13 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["cdylib", "rlib"] +crate-type = ["cdylib", "staticlib", "rlib"] [features] default = ["native"] -native = [ +engine = [ "dep:axum", "dep:fluxa_core", - "dep:jni", "dep:librqbit", "dep:reqwest", "dep:tokio", @@ -19,6 +18,8 @@ native = [ "dep:tower-http", "dep:url", ] +native = ["engine", "dep:jni"] +apple = ["engine"] [dependencies] axum = { version = "0.8", optional = true } diff --git a/fluxa-streaming-engine/src/bindings/apple.rs b/fluxa-streaming-engine/src/bindings/apple.rs new file mode 100644 index 0000000..8349363 --- /dev/null +++ b/fluxa-streaming-engine/src/bindings/apple.rs @@ -0,0 +1,104 @@ +use crate::local_stream::{start_local_stream_server, stop_local_stream_server}; +use crate::torrent_engine::{start_torrent_server, stop_torrent_server}; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; +use std::ptr; + +fn read_string(value: *const c_char) -> Option { + if value.is_null() { + return None; + } + unsafe { CStr::from_ptr(value) }.to_str().ok().map(str::to_owned) +} + +fn write_string(value: Option) -> *mut c_char { + value + .and_then(|value| CString::new(value).ok()) + .map(CString::into_raw) + .unwrap_or(ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn fluxa_streaming_start_local_stream_server( + target_url: *const c_char, + headers_json: *const c_char, + preferred_port: i32, +) -> *mut c_char { + std::panic::catch_unwind(|| { + let target_url = read_string(target_url)?; + let headers_json = read_string(headers_json)?; + start_local_stream_server(&target_url, &headers_json, preferred_port) + }) + .ok() + .flatten() + .map(Some) + .map(write_string) + .unwrap_or(ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn fluxa_streaming_stop_local_stream_server(server_id: *const c_char) -> bool { + std::panic::catch_unwind(|| { + read_string(server_id) + .map(|server_id| stop_local_stream_server(&server_id)) + .unwrap_or(false) + }) + .unwrap_or(false) +} + +#[no_mangle] +pub extern "C" fn fluxa_streaming_start_torrent_server( + cache_dir: *const c_char, + preferred_port: i32, + access_token: *const c_char, +) -> *mut c_char { + std::panic::catch_unwind(|| { + let cache_dir = read_string(cache_dir)?; + let access_token = read_string(access_token).unwrap_or_default(); + start_torrent_server(&cache_dir, preferred_port, &access_token) + }) + .ok() + .flatten() + .map(Some) + .map(write_string) + .unwrap_or(ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn fluxa_streaming_stop_torrent_server() -> bool { + std::panic::catch_unwind(|| stop_torrent_server(None)).unwrap_or(false) +} + +#[no_mangle] +pub unsafe extern "C" fn fluxa_streaming_string_free(value: *mut c_char) { + if !value.is_null() { + let _ = CString::from_raw(value); + } +} + +#[cfg(test)] +mod tests { + use super::{ + fluxa_streaming_start_local_stream_server, fluxa_streaming_stop_local_stream_server, + fluxa_streaming_string_free, + }; + use std::ffi::{CStr, CString}; + + #[test] + fn local_proxy_lifecycle_is_available_through_the_apple_bridge() { + let target_url = CString::new("https://example.invalid/video.mp4").unwrap(); + let headers = CString::new("{}").unwrap(); + let result = fluxa_streaming_start_local_stream_server(target_url.as_ptr(), headers.as_ptr(), 0); + assert!(!result.is_null()); + let response = unsafe { CStr::from_ptr(result) }.to_str().unwrap().to_string(); + unsafe { fluxa_streaming_string_free(result) }; + let id = serde_json::from_str::(&response) + .unwrap() + .get("id") + .and_then(serde_json::Value::as_str) + .unwrap() + .to_string(); + let id = CString::new(id).unwrap(); + assert!(fluxa_streaming_stop_local_stream_server(id.as_ptr())); + } +} diff --git a/fluxa-streaming-engine/src/bindings/mod.rs b/fluxa-streaming-engine/src/bindings/mod.rs index a94f955..a8dd9cb 100644 --- a/fluxa-streaming-engine/src/bindings/mod.rs +++ b/fluxa-streaming-engine/src/bindings/mod.rs @@ -1,2 +1,4 @@ #[cfg(feature = "native")] pub mod jni; +#[cfg(feature = "apple")] +pub mod apple; diff --git a/uniffi.toml b/uniffi.toml index 0c1845b..d0c51f8 100644 --- a/uniffi.toml +++ b/uniffi.toml @@ -5,5 +5,5 @@ cdylib_name = "fluxa_core" kotlin_target_version = "2.3.0" [bindings.swift] -module_name = "FluxaCore" +module_name = "FluxaRustCore" cdylib_name = "fluxa_core"