chore(cast): delete dead cast and dlna commands modules
The Tauri commands in cast/commands.rs and cast/dlna/commands.rs are unregistered (handler list is V2-only) and uncalled. The CastState / DlnaState path-compat re-exports are also unused (canonical paths are crate::cast::CastState and crate::cast::dlna::DlnaState). Remove the files entirely and drop pub mod declarations. cc
This commit is contained in:
4 files changed
-533
No files matched your search
@@ -1,273 +0,0 @@
|
||||
//! Tauri commands for Chromecast casting
|
||||
|
||||
use tauri::State;
|
||||
|
||||
use crate::api::models::Quality;
|
||||
use crate::cast::{
|
||||
CastError, CastPositionInfo, CastStatus, DiscoveredDevice, MediaMetadata,
|
||||
};
|
||||
use crate::library::{AudioFormat, LibraryState};
|
||||
use crate::AppState;
|
||||
|
||||
// CastState lives in `super::state`; re-export from commands so any
|
||||
// historical `crate::cast::commands::CastState` paths continue to compile.
|
||||
pub use super::state::CastState;
|
||||
|
||||
// === Discovery ===
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_start_discovery(state: State<'_, CastState>) -> Result<(), String> {
|
||||
let mut discovery = state.discovery.lock().await;
|
||||
discovery.start_discovery().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_stop_discovery(state: State<'_, CastState>) -> Result<(), String> {
|
||||
let mut discovery = state.discovery.lock().await;
|
||||
discovery.stop_discovery().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_get_devices(
|
||||
state: State<'_, CastState>,
|
||||
) -> Result<Vec<DiscoveredDevice>, String> {
|
||||
let discovery = state.discovery.lock().await;
|
||||
Ok(discovery.get_discovered_devices())
|
||||
}
|
||||
|
||||
// === Connection ===
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_connect(device_id: String, state: State<'_, CastState>) -> Result<(), String> {
|
||||
let device = {
|
||||
let discovery = state.discovery.lock().await;
|
||||
discovery
|
||||
.get_device(&device_id)
|
||||
.ok_or_else(|| CastError::DeviceNotFound(device_id.clone()))
|
||||
.map_err(|e| e.to_string())?
|
||||
};
|
||||
|
||||
state
|
||||
.chromecast
|
||||
.connect(device.ip.clone(), device.port)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
{
|
||||
let mut connected = state.connected_device_ip.lock().await;
|
||||
*connected = Some(device.ip.clone());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_disconnect(state: State<'_, CastState>) -> Result<(), String> {
|
||||
state.chromecast.disconnect().map_err(|e| e.to_string())?;
|
||||
|
||||
{
|
||||
let mut connected = state.connected_device_ip.lock().await;
|
||||
*connected = None;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_get_status(state: State<'_, CastState>) -> Result<CastStatus, String> {
|
||||
state.chromecast.get_status().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_get_position(state: State<'_, CastState>) -> Result<CastPositionInfo, String> {
|
||||
state
|
||||
.chromecast
|
||||
.get_media_position()
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
// === Playback ===
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_play_track(
|
||||
track_id: u64,
|
||||
metadata: MediaMetadata,
|
||||
state: State<'_, CastState>,
|
||||
app_state: State<'_, AppState>,
|
||||
) -> Result<(), String> {
|
||||
let stream_url = {
|
||||
let client = app_state.client.read().await;
|
||||
client
|
||||
.get_stream_url_with_fallback(track_id, Quality::HiRes)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to get stream URL: {}", e))?
|
||||
};
|
||||
|
||||
let content_type = stream_url.mime_type.clone();
|
||||
let cache = app_state.audio_cache.clone();
|
||||
|
||||
let audio_data = if let Some(cached) = cache.get(track_id) {
|
||||
cached.data
|
||||
} else {
|
||||
let data = download_audio(&stream_url.url).await?;
|
||||
cache.insert(track_id, data.clone());
|
||||
data
|
||||
};
|
||||
|
||||
let target_ip = {
|
||||
let connected = state.connected_device_ip.lock().await;
|
||||
connected.clone()
|
||||
};
|
||||
|
||||
// Ensure media server is started (lazy init)
|
||||
state
|
||||
.get_or_create_media_server()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let url = {
|
||||
let mut server_guard = state.media_server.lock().await;
|
||||
let server = server_guard
|
||||
.as_mut()
|
||||
.ok_or("Media server not initialized")?;
|
||||
server.register_audio(track_id, audio_data, &content_type);
|
||||
let url = match target_ip.as_deref() {
|
||||
Some(ip) => server.get_audio_url_for_target(track_id, ip),
|
||||
None => server.get_audio_url(track_id),
|
||||
};
|
||||
url.ok_or_else(|| "Failed to build media URL".to_string())?
|
||||
};
|
||||
|
||||
state
|
||||
.chromecast
|
||||
.load_media(url, content_type, metadata)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_play_local_track(
|
||||
track_id: i64,
|
||||
state: State<'_, CastState>,
|
||||
library_state: State<'_, LibraryState>,
|
||||
) -> Result<(), String> {
|
||||
let track = {
|
||||
let db_opt__ = library_state.db.lock().await;
|
||||
let db = db_opt__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
db.get_track(track_id)
|
||||
.map_err(|e| e.to_string())?
|
||||
.ok_or_else(|| "Track not found".to_string())?
|
||||
};
|
||||
|
||||
let target_ip = {
|
||||
let connected = state.connected_device_ip.lock().await;
|
||||
connected.clone()
|
||||
};
|
||||
|
||||
// Ensure media server is started (lazy init)
|
||||
state
|
||||
.get_or_create_media_server()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let url = {
|
||||
let mut server_guard = state.media_server.lock().await;
|
||||
let server = server_guard
|
||||
.as_mut()
|
||||
.ok_or("Media server not initialized")?;
|
||||
server
|
||||
.register_file(track_id as u64, &track.file_path)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let url = match target_ip.as_deref() {
|
||||
Some(ip) => server.get_audio_url_for_target(track_id as u64, ip),
|
||||
None => server.get_audio_url(track_id as u64),
|
||||
};
|
||||
url.ok_or_else(|| "Failed to build media URL".to_string())?
|
||||
};
|
||||
|
||||
let metadata = MediaMetadata {
|
||||
title: track.title,
|
||||
artist: track.artist,
|
||||
album: track.album,
|
||||
artwork_url: None,
|
||||
duration_secs: Some(track.duration_secs),
|
||||
};
|
||||
|
||||
let content_type = content_type_from_format(&track.format).to_string();
|
||||
|
||||
state
|
||||
.chromecast
|
||||
.load_media(url, content_type, metadata)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_play(state: State<'_, CastState>) -> Result<(), String> {
|
||||
state.chromecast.play().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_pause(state: State<'_, CastState>) -> Result<(), String> {
|
||||
state.chromecast.pause().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_stop(state: State<'_, CastState>) -> Result<(), String> {
|
||||
state.chromecast.stop().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_seek(position_secs: f64, state: State<'_, CastState>) -> Result<(), String> {
|
||||
state
|
||||
.chromecast
|
||||
.seek(position_secs)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cast_set_volume(volume: f32, state: State<'_, CastState>) -> Result<(), String> {
|
||||
state
|
||||
.chromecast
|
||||
.set_volume(volume)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
async fn download_audio(url: &str) -> Result<Vec<u8>, String> {
|
||||
use std::time::Duration;
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(120))
|
||||
.connect_timeout(Duration::from_secs(10))
|
||||
.build()
|
||||
.map_err(|e| format!("Failed to create HTTP client: {}", e))?;
|
||||
|
||||
let response = client
|
||||
.get(url)
|
||||
.header("User-Agent", "Mozilla/5.0")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to fetch audio: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!("HTTP error: {}", response.status()));
|
||||
}
|
||||
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read audio bytes: {}", e))?;
|
||||
|
||||
Ok(bytes.to_vec())
|
||||
}
|
||||
|
||||
fn content_type_from_format(format: &AudioFormat) -> &'static str {
|
||||
match format {
|
||||
AudioFormat::Flac => "audio/flac",
|
||||
AudioFormat::Alac => "audio/mp4",
|
||||
AudioFormat::Wav => "audio/wav",
|
||||
AudioFormat::Aiff => "audio/aiff",
|
||||
AudioFormat::Ape => "audio/ape",
|
||||
AudioFormat::Mp3 => "audio/mpeg",
|
||||
AudioFormat::Unknown => "application/octet-stream",
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
//! Tauri commands for DLNA/UPnP casting
|
||||
|
||||
use tauri::State;
|
||||
|
||||
use crate::api::models::Quality;
|
||||
use crate::cast::dlna::{
|
||||
DiscoveredDlnaDevice, DlnaConnection, DlnaError, DlnaMetadata, DlnaPositionInfo, DlnaStatus,
|
||||
};
|
||||
use crate::AppState;
|
||||
|
||||
// DlnaState lives in `super::state`; re-export from commands so any
|
||||
// historical `crate::cast::dlna::commands::DlnaState` paths continue
|
||||
// to compile.
|
||||
pub use super::state::DlnaState;
|
||||
|
||||
// === Discovery ===
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_start_discovery(state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let mut discovery = state.discovery.lock().await;
|
||||
discovery.start_discovery().await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_stop_discovery(state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let mut discovery = state.discovery.lock().await;
|
||||
discovery.stop_discovery().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_get_devices(
|
||||
state: State<'_, DlnaState>,
|
||||
) -> Result<Vec<DiscoveredDlnaDevice>, String> {
|
||||
let discovery = state.discovery.lock().await;
|
||||
Ok(discovery.get_discovered_devices())
|
||||
}
|
||||
|
||||
// === Connection ===
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_connect(device_id: String, state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let device = {
|
||||
let discovery = state.discovery.lock().await;
|
||||
discovery
|
||||
.get_device(&device_id)
|
||||
.ok_or_else(|| DlnaError::DeviceNotFound(device_id.clone()))
|
||||
.map_err(|e| e.to_string())?
|
||||
};
|
||||
|
||||
let connection = DlnaConnection::connect(device)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut state_connection = state.connection.lock().await;
|
||||
*state_connection = Some(connection);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_disconnect(state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let mut connection = state.connection.lock().await;
|
||||
if let Some(conn) = connection.as_mut() {
|
||||
conn.disconnect().map_err(|e| e.to_string())?;
|
||||
}
|
||||
*connection = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_get_status(state: State<'_, DlnaState>) -> Result<DlnaStatus, String> {
|
||||
let connection = state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
Ok(conn.get_status())
|
||||
}
|
||||
|
||||
/// Get current playback position from DLNA device
|
||||
#[tauri::command]
|
||||
pub async fn dlna_get_position(state: State<'_, DlnaState>) -> Result<DlnaPositionInfo, String> {
|
||||
let connection = state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
conn.get_position_info().await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
// === Playback ===
|
||||
|
||||
/// Play a Qobuz track on the DLNA device
|
||||
#[tauri::command]
|
||||
pub async fn dlna_play_track(
|
||||
track_id: u64,
|
||||
metadata: DlnaMetadata,
|
||||
dlna_state: State<'_, DlnaState>,
|
||||
app_state: State<'_, AppState>,
|
||||
) -> Result<(), String> {
|
||||
log::info!("DLNA: dlna_play_track called for track_id={}", track_id);
|
||||
|
||||
// Get stream URL from Qobuz
|
||||
let stream_url = {
|
||||
let client = app_state.client.read().await;
|
||||
client
|
||||
.get_stream_url_with_fallback(track_id, Quality::HiRes)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to get stream URL: {}", e))?
|
||||
};
|
||||
|
||||
let content_type = stream_url.mime_type.clone();
|
||||
let cache = app_state.audio_cache.clone();
|
||||
|
||||
// Download audio data
|
||||
let audio_data = if let Some(cached) = cache.get(track_id) {
|
||||
cached.data
|
||||
} else {
|
||||
let data = download_audio(&stream_url.url).await?;
|
||||
cache.insert(track_id, data.clone());
|
||||
data
|
||||
};
|
||||
|
||||
// Register with media server to get a URL the DLNA device can access
|
||||
let target_ip = {
|
||||
let connection = dlna_state.connection.lock().await;
|
||||
connection.as_ref().map(|conn| conn.device_ip().to_string())
|
||||
};
|
||||
|
||||
// Ensure media server is started (lazy init)
|
||||
dlna_state
|
||||
.ensure_media_server()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let url = {
|
||||
let mut server_guard = dlna_state.media_server.lock().await;
|
||||
let server = server_guard
|
||||
.as_mut()
|
||||
.ok_or("Media server not initialized")?;
|
||||
server.register_audio(track_id, audio_data, &content_type);
|
||||
let url = match target_ip.as_deref() {
|
||||
Some(ip) => server.get_audio_url_for_target(track_id, ip),
|
||||
None => server.get_audio_url(track_id),
|
||||
};
|
||||
url.ok_or_else(|| "Failed to build media URL".to_string())?
|
||||
};
|
||||
|
||||
log::info!(
|
||||
"DLNA: Playing track {} via MediaServer URL: {}",
|
||||
track_id,
|
||||
url
|
||||
);
|
||||
log::info!("DLNA: Content-Type from Qobuz: {}", content_type);
|
||||
|
||||
// Load media on DLNA device
|
||||
{
|
||||
let mut connection = dlna_state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
conn.load_media(&url, &metadata, &content_type)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
// Start playback
|
||||
{
|
||||
let mut connection = dlna_state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
conn.play().await.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load media without starting playback (for compatibility with existing castStore)
|
||||
#[tauri::command]
|
||||
pub async fn dlna_load_media(
|
||||
_metadata: DlnaMetadata,
|
||||
_state: State<'_, DlnaState>,
|
||||
) -> Result<(), String> {
|
||||
// This is a stub - actual loading happens in dlna_play_track
|
||||
// The castStore calls this but we need the track_id for actual loading
|
||||
log::warn!("dlna_load_media called without track_id - use dlna_play_track instead");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_play(state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let mut connection = state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
conn.play().await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_pause(state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let mut connection = state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
conn.pause().await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_stop(state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let mut connection = state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
conn.stop().await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_seek(position_secs: u64, state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let mut connection = state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
conn.seek(position_secs).await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dlna_set_volume(volume: f32, state: State<'_, DlnaState>) -> Result<(), String> {
|
||||
let mut connection = state.connection.lock().await;
|
||||
let conn = connection
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Not connected".to_string())?;
|
||||
conn.set_volume(volume).await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
async fn download_audio(url: &str) -> Result<Vec<u8>, String> {
|
||||
use std::time::Duration;
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(120))
|
||||
.connect_timeout(Duration::from_secs(10))
|
||||
.build()
|
||||
.map_err(|e| format!("Failed to create HTTP client: {}", e))?;
|
||||
|
||||
let response = client
|
||||
.get(url)
|
||||
.header("User-Agent", "Mozilla/5.0")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to fetch audio: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!("HTTP error: {}", response.status()));
|
||||
}
|
||||
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read audio bytes: {}", e))?;
|
||||
|
||||
Ok(bytes.to_vec())
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
//!
|
||||
//! Core functionality is in qbz-cast crate, this module provides Tauri commands.
|
||||
|
||||
pub mod commands;
|
||||
pub mod state;
|
||||
|
||||
// Re-export from qbz-cast
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
//! The core casting logic is in the qbz-cast crate.
|
||||
|
||||
pub mod chromecast_thread;
|
||||
pub mod commands;
|
||||
pub mod device;
|
||||
pub mod discovery;
|
||||
pub mod dlna;
|
||||
|
||||
Reference in new issue
Block a user