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:
vicrodh committed 2026-04-26 22:35:14 -06:00
1 parent c57acbda4c
commit 3f819d71df
4 files changed
-533

No files matched your search

-273
View File
@@ -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",
}
}
-258
View File
@@ -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())
}
-1
View File
@@ -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
-1
View File
@@ -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;