Compare commits
5 Commits
ci/add-ffm
...
bbb767cd20
| Author | SHA1 | Date | |
|---|---|---|---|
| bbb767cd20 | |||
| a69b4c0bcb | |||
| 4bd22a2009 | |||
| fd08aaffdf | |||
| c954bf25d4 |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "radio-tauri",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "radio-tauri",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2",
|
||||
"cross-env": "^7.0.3",
|
||||
|
||||
@@ -4,6 +4,7 @@ version = "0.1.1"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
default-run = "radio-tauri"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::io::{BufRead, BufReader};
|
||||
use std::net::{IpAddr, SocketAddr, TcpListener, TcpStream, UdpSocket};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::sync::Mutex;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -14,7 +13,7 @@ const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
||||
use serde_json::json;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use tauri::{AppHandle, Manager, State, Emitter};
|
||||
use tauri_plugin_shell::process::{CommandChild, CommandEvent};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
use reqwest;
|
||||
@@ -560,40 +559,55 @@ pub fn run() {
|
||||
let controller = player::spawn_player_thread(shared);
|
||||
app.manage(PlayerRuntime { shared, controller });
|
||||
|
||||
// Start mDNS discovery in background without blocking setup.
|
||||
// This allows the main window to show immediately.
|
||||
let handle = app.handle().clone();
|
||||
thread::spawn(move || {
|
||||
let mdns = ServiceDaemon::new().expect("Failed to create daemon");
|
||||
let receiver = mdns
|
||||
.browse("_googlecast._tcp.local.")
|
||||
.expect("Failed to browse");
|
||||
while let Ok(event) = receiver.recv() {
|
||||
match event {
|
||||
ServiceEvent::ServiceResolved(info) => {
|
||||
let name = info
|
||||
.get_property_val_str("fn")
|
||||
.or_else(|| Some(info.get_fullname()))
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let addresses = info.get_addresses();
|
||||
let ip = addresses
|
||||
.iter()
|
||||
.find(|ip| ip.is_ipv4())
|
||||
.or_else(|| addresses.iter().next());
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// Small delay to ensure window is fully initialized first.
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
|
||||
if let Some(ip) = ip {
|
||||
let state = handle.state::<AppState>();
|
||||
let mut devices = state.known_devices.lock().unwrap();
|
||||
let ip_str = ip.to_string();
|
||||
if !devices.contains_key(&name) {
|
||||
println!("Discovered Cast Device: {} at {}", name, ip_str);
|
||||
devices.insert(name, ip_str);
|
||||
std::thread::spawn(move || {
|
||||
let mdns = ServiceDaemon::new().expect("Failed to create daemon");
|
||||
let receiver = mdns
|
||||
.browse("_googlecast._tcp.local.")
|
||||
.expect("Failed to browse");
|
||||
|
||||
while let Ok(event) = receiver.recv() {
|
||||
match event {
|
||||
ServiceEvent::ServiceResolved(info) => {
|
||||
let name = info
|
||||
.get_property_val_str("fn")
|
||||
.or_else(|| Some(info.get_fullname()))
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let addresses = info.get_addresses();
|
||||
let ip = addresses
|
||||
.iter()
|
||||
.find(|ip| ip.is_ipv4())
|
||||
.or_else(|| addresses.iter().next());
|
||||
|
||||
if let Some(ip) = ip {
|
||||
let state = handle.state::<AppState>();
|
||||
let mut devices = state.known_devices.lock().unwrap();
|
||||
let ip_str = ip.to_string();
|
||||
if !devices.contains_key(&name) {
|
||||
println!("Discovered Cast Device: {} at {}", name, ip_str);
|
||||
devices.insert(name.clone(), ip_str.clone());
|
||||
|
||||
// Emit event to frontend when new device is discovered.
|
||||
let _ = handle.emit("cast-device-discovered", json!({
|
||||
"name": name,
|
||||
"ip": ip_str
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
|
||||
15
src/main.js
15
src/main.js
@@ -1050,6 +1050,17 @@ function setupEventListeners() {
|
||||
await appWindow.close();
|
||||
});
|
||||
|
||||
// Listen for cast device discovery events from backend
|
||||
if (runningInTauri && window.__TAURI__ && window.__TAURI__.event) {
|
||||
window.__TAURI__.event.listen('cast-device-discovered', (event) => {
|
||||
console.log('Cast device discovered:', event.payload);
|
||||
// If cast overlay is currently open, refresh the device list
|
||||
if (!castOverlay.classList.contains('hidden')) {
|
||||
refreshCastDeviceList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Menu button - explicit functionality or placeholder?
|
||||
// Menu removed — header click opens stations via artwork placeholder
|
||||
|
||||
@@ -1353,6 +1364,10 @@ async function openCastOverlay() {
|
||||
castOverlay.setAttribute('aria-hidden', 'false');
|
||||
// ensure cast overlay shows linear list style
|
||||
deviceListEl.classList.remove('stations-grid');
|
||||
await refreshCastDeviceList();
|
||||
}
|
||||
|
||||
async function refreshCastDeviceList() {
|
||||
deviceListEl.innerHTML = '<li class="device"><div class="device-main">Scanning...</div><div class="device-sub">Searching for speakers</div></li>';
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user