This commit is contained in:
2026-01-11 19:25:02 +01:00
parent d45fe0fbde
commit abb7cafaed
8 changed files with 362 additions and 59 deletions

1
src-tauri/Cargo.lock generated
View File

@@ -3284,6 +3284,7 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
name = "radio-tauri"
version = "0.1.0"
dependencies = [
"base64 0.22.1",
"cpal",
"mdns-sd",
"reqwest 0.11.27",

View File

@@ -27,6 +27,7 @@ mdns-sd = "0.17.1"
tokio = { version = "1.48.0", features = ["full"] }
tauri-plugin-shell = "2.3.3"
reqwest = { version = "0.11", features = ["json", "rustls-tls"] }
base64 = "0.22"
cpal = "0.15"
ringbuf = "0.3"

View File

@@ -8,6 +8,7 @@ use tauri::{AppHandle, Manager, State};
use tauri_plugin_shell::process::{CommandChild, CommandEvent};
use tauri_plugin_shell::ShellExt;
use reqwest;
use base64::{engine::general_purpose, Engine as _};
mod player;
use player::{PlayerCommand, PlayerController, PlayerShared, PlayerState};
@@ -223,6 +224,53 @@ async fn fetch_url(_app: AppHandle, url: String) -> Result<String, String> {
}
}
#[tauri::command]
async fn fetch_image_data_url(url: String) -> Result<String, String> {
// Fetch remote images via backend and return a data: URL.
// This helps when WebView blocks http images (mixed-content) or some hosts block hotlinking.
let parsed = reqwest::Url::parse(&url).map_err(|e| e.to_string())?;
match parsed.scheme() {
"http" | "https" => {}
_ => return Err("Only http/https URLs are allowed".to_string()),
}
let resp = reqwest::Client::new()
.get(parsed)
.header(reqwest::header::USER_AGENT, "RadioPlayer/1.0")
.send()
.await
.map_err(|e| e.to_string())?;
let status = resp.status();
if !status.is_success() {
return Err(format!("HTTP {} while fetching image", status));
}
let content_type = resp
.headers()
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.map(|s| s.split(';').next().unwrap_or(s).trim().to_string())
.unwrap_or_else(|| "application/octet-stream".to_string());
let bytes = resp.bytes().await.map_err(|e| e.to_string())?;
const MAX_BYTES: usize = 2 * 1024 * 1024;
if bytes.len() > MAX_BYTES {
return Err("Image too large".to_string());
}
// Be conservative: prefer image/* content types, but allow svg even if mislabelled.
let looks_like_image = content_type.starts_with("image/")
|| content_type == "application/svg+xml"
|| url.to_lowercase().ends_with(".svg");
if !looks_like_image {
return Err(format!("Not an image content-type: {}", content_type));
}
let b64 = general_purpose::STANDARD.encode(bytes);
Ok(format!("data:{};base64,{}", content_type, b64))
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
@@ -296,6 +344,8 @@ pub fn run() {
cast_set_volume,
// allow frontend to request arbitrary URLs via backend (bypass CORS)
fetch_url,
// fetch remote images via backend (data: URL), helps with mixed-content
fetch_image_data_url,
// native player commands (step 1 scaffold)
player_play,
player_stop,