diff --git a/desktop/README.md b/desktop/README.md index 042e6dad..49613f60 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -105,6 +105,7 @@ Environment variables override the defaults: | `QUINTODROME_HOST` | `127.0.0.1` | Address the webview connects to | | `QUINTODROME_PORT` | `4533` | Port for the Navidrome server | | `QUINTODROME_MUSIC_FOLDER` | OS music dir (e.g. `~/Music`) | Where your music lives | +| `QUINTODROME_PUBLIC_URL` | `http://quintodrome` | Friendly origin shown in the address bar | The data folder (DB, cache) is always the OS app-data directory (e.g. `~/Library/Application Support/com.quintodrome.desktop` on macOS). diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 46c7fd0a..165c3528 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -7,6 +7,38 @@ use tauri::{ }; const TOOLBAR_HEIGHT: f64 = 60.0; +const DEFAULT_PUBLIC_URL: &str = "http://quintodrome"; + +/// Maps the internal Navidrome origin (e.g. `http://127.0.0.1:4533`) to a +/// friendlier, user-facing origin shown in the address bar. +#[derive(Clone)] +struct UrlMask { + real: String, + public: String, +} + +impl UrlMask { + fn new(host: &str, port: u16) -> Self { + let real = format!("http://{host}:{port}"); + let public = std::env::var("QUINTODROME_PUBLIC_URL") + .unwrap_or_else(|_| DEFAULT_PUBLIC_URL.to_string()); + Self { real, public } + } + + /// `http://127.0.0.1:4533/...` -> `http://quintodrome/...` + fn mask(&self, url: &str) -> String { + url.strip_prefix(&self.real) + .map(|rest| format!("{}{}", self.public, rest)) + .unwrap_or_else(|| url.to_string()) + } + + /// `http://quintodrome/...` -> `http://127.0.0.1:4533/...` + fn unmask(&self, url: &str) -> String { + url.strip_prefix(&self.public) + .map(|rest| format!("{}{}", self.real, rest)) + .unwrap_or_else(|| url.to_string()) + } +} #[tauri::command] fn back(app: tauri::AppHandle) { @@ -31,6 +63,7 @@ fn reload(app: tauri::AppHandle) { #[tauri::command] fn navigate(app: tauri::AppHandle, url: String) { + let url = app.state::().unmask(&url); if let Some(content) = app.get_webview("content") { if let Ok(url) = Url::parse(&url) { let _ = content.navigate(url); @@ -91,18 +124,22 @@ pub fn run() { .and_then(|p| p.parse::().ok()) .unwrap_or(server::DEFAULT_PORT); - let navidrome = Navidrome::new(host, port); + let navidrome = Navidrome::new(host.clone(), port); let navidrome_url = navidrome.url.clone(); + let mask = UrlMask::new(&host, port); + app.manage(mask.clone()); + // The primary webview ("main") is the toolbar; the Navidrome content // lives in a child webview below it. let content = WebviewBuilder::new("content", WebviewUrl::App("index.html".into())) .on_navigation({ let handle = handle.clone(); + let mask = mask.clone(); move |url| { if url.scheme() == "http" || url.scheme() == "https" { if let Some(toolbar) = handle.get_webview("main") { - let _ = toolbar.emit("url-changed", url.to_string()); + let _ = toolbar.emit("url-changed", mask.mask(&url.to_string())); } } true @@ -129,7 +166,7 @@ pub fn run() { enable_swipe_navigation(app.handle()); if let Some(toolbar) = app.get_webview("main") { - let _ = toolbar.emit("url-changed", &navidrome_url); + let _ = toolbar.emit("url-changed", mask.mask(&navidrome_url)); } // Detect/start Navidrome off the main thread, then load it. diff --git a/desktop/src/toolbar.html b/desktop/src/toolbar.html index df9cf582..6d28b300 100644 --- a/desktop/src/toolbar.html +++ b/desktop/src/toolbar.html @@ -65,7 +65,7 @@ - +