fix: resolve all Rust warnings in qbz-nix crate

- Remove unused import TagExt in library/commands.rs
- Remove unnecessary unsafe block in alsa_direct.rs
- Fix value-never-read warnings in search.rs blacklist filtering
- Handle unused Result from chromecast.disconnect() in lib.rs
- Add #[allow(dead_code)] to unused but intentional API:
  - VECTOR_TTL_SECS constant (future use)
  - PipeWireBackend host field (stored for future use)
  - AlsaDirectStream is_playing field (future use)
  - CacheEntry track_id field (debug purposes)
  - CastSession app_id field (deserialized but unused)
  - ItunesAlbumResult struct (deserialized response)
  - ReleaseDetails struct (Discogs API response)
  - simple_hash function (utility for future use)
  - LrclibItem struct (lyrics API response)
  - AudioSpecs/AudioMetadata structs (future use)
  - Player visualizer_tap field (future feature)
  - Retry constants (future retry logic)
  - PlaybackEngine position/duration/is_alsa_direct methods

Remaining warnings are in vendor/rodio (external code).
This commit is contained in:
vicrodh committed 2026-02-02 22:09:41 -06:00
1 parent 1540146df6
commit 678693d0c4
25 files changed
+58 -48

No files matched your search

+1
View File
@@ -12,6 +12,7 @@ use tokio::sync::Mutex;
use super::sparse_vector::SparseVector;
/// TTL for vector entries (7 days)
#[allow(dead_code)]
const VECTOR_TTL_SECS: i64 = 7 * 24 * 60 * 60;
/// Shared state wrapper for Tauri
+2 -2
View File
@@ -228,7 +228,7 @@ impl AlsaBackend {
device
.supported_output_configs()
.ok()
.and_then(|mut configs| {
.and_then(|configs| {
configs
.max_by_key(|c| c.max_sample_rate().0)
.map(|c| c.max_sample_rate().0)
@@ -371,7 +371,7 @@ impl AlsaBackend {
let max_sample_rate = device
.supported_output_configs()
.ok()
.and_then(|mut configs| {
.and_then(|configs| {
configs
.max_by_key(|c| c.max_sample_rate().0)
.map(|c| c.max_sample_rate().0)
+4 -4
View File
@@ -7,13 +7,13 @@
use alsa::pcm::{Access, Format, HwParams, PCM};
#[cfg(target_os = "linux")]
use alsa::{Direction, ValueOr};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use std::thread;
/// Direct ALSA PCM stream for hw: devices
pub struct AlsaDirectStream {
pcm: Arc<Mutex<PCM>>,
#[allow(dead_code)]
is_playing: Arc<AtomicBool>,
sample_rate: u32,
channels: u16,
@@ -122,7 +122,7 @@ impl AlsaDirectStream {
/// Write audio samples to ALSA (auto-converts from i16 based on detected format)
#[cfg(target_os = "linux")]
pub fn write(&self, samples_i16: &[i16]) -> Result<(), String> {
let mut pcm = self.pcm.lock().unwrap();
let pcm = self.pcm.lock().unwrap();
let frames = samples_i16.len() / self.channels as usize;
match self.format {
@@ -218,7 +218,7 @@ impl AlsaDirectStream {
}
// Use raw byte I/O for 3-byte packed format
let io = unsafe { pcm.io_bytes() };
let io = pcm.io_bytes();
match io.writei(&bytes) {
Ok(written) => {
+2 -1
View File
@@ -6,7 +6,7 @@
//! - Creates stream using CPAL "pulse" or "pipewire" device
//! - Does NOT change system default (only affects QBZ)
use super::backend::{AlsaPlugin, AudioBackend, AudioBackendType, AudioDevice, BackendConfig, BackendResult};
use super::backend::{AudioBackend, AudioBackendType, AudioDevice, BackendConfig, BackendResult};
use rodio::{
cpal::{
traits::{DeviceTrait, HostTrait},
@@ -17,6 +17,7 @@ use rodio::{
use std::process::Command;
pub struct PipeWireBackend {
#[allow(dead_code)]
host: rodio::cpal::Host,
}
+1 -1
View File
@@ -3,7 +3,7 @@
//! Similar to PipeWire backend but for systems running PulseAudio only.
//! Uses same approach: pactl + PULSE_SINK + CPAL "pulse" device.
use super::backend::{AlsaPlugin, AudioBackend, AudioBackendType, AudioDevice, BackendConfig, BackendResult};
use super::backend::{AudioBackend, AudioBackendType, AudioDevice, BackendConfig, BackendResult};
use super::pipewire_backend::PipeWireBackend;
use rodio::{OutputStream, OutputStreamHandle};
+1
View File
@@ -13,6 +13,7 @@ use std::time::SystemTime;
/// Entry metadata for tracking cache usage
#[derive(Debug, Clone)]
struct CacheEntry {
#[allow(dead_code)]
track_id: u64,
size_bytes: u64,
last_accessed: SystemTime,
+1
View File
@@ -47,6 +47,7 @@ pub struct CastApplication {
}
struct CastSession {
#[allow(dead_code)]
app_id: String,
session_id: String,
transport_id: String,
+1 -1
View File
@@ -5,7 +5,7 @@
use tauri::State;
use crate::listenbrainz::{
AdditionalInfo, ListenBrainzSharedState, ListenBrainzStatus, QueueStats, QueuedListen, UserInfo,
AdditionalInfo, ListenBrainzSharedState, ListenBrainzStatus, QueuedListen, UserInfo,
};
/// Get ListenBrainz connection status
+1 -1
View File
@@ -384,7 +384,7 @@ fn release_to_resolved(release: &ReleaseResult) -> ResolvedRelease {
fn extract_relationships(artist: &ArtistFullResponse) -> ArtistRelationships {
let mut members = Vec::new();
let mut past_members = Vec::new();
let past_members = Vec::new();
let mut groups = Vec::new();
let mut collaborators = Vec::new();
+1 -1
View File
@@ -568,7 +568,7 @@ fn extract_audio_format_from_header(data: &[u8]) -> Result<(u32, u16, u32), Stri
use symphonia::core::probe::Hint;
use symphonia::default::get_probe;
let cursor = Box::new(Cursor::new(data.to_vec())) as Box<dyn std::io::Read + Send + Sync>;
let _cursor = Box::new(Cursor::new(data.to_vec())) as Box<dyn std::io::Read + Send + Sync>;
// For probing, we need to create a MediaSource
// Use a simple wrapper
struct ProbeSource {
+3 -7
View File
@@ -233,15 +233,11 @@ pub async fn search_all(
});
// Apply blacklist filtering
let mut filtered_tracks = 0;
let mut filtered_artists = 0;
let mut filtered_albums = 0;
// Filter albums from blacklisted artists
let original_album_count = albums.items.len();
let mut albums = albums;
albums.items.retain(|album| !blacklist_state.is_blacklisted(album.artist.id));
filtered_albums = original_album_count - albums.items.len();
let filtered_albums = original_album_count - albums.items.len();
if filtered_albums > 0 {
albums.total = albums.total.saturating_sub(filtered_albums as u32);
}
@@ -255,7 +251,7 @@ pub async fn search_all(
true
}
});
filtered_tracks = original_track_count - tracks.items.len();
let filtered_tracks = original_track_count - tracks.items.len();
if filtered_tracks > 0 {
tracks.total = tracks.total.saturating_sub(filtered_tracks as u32);
}
@@ -263,7 +259,7 @@ pub async fn search_all(
// Filter blacklisted artists
let original_artist_count = artists.items.len();
artists.items.retain(|artist| !blacklist_state.is_blacklisted(artist.id));
filtered_artists = original_artist_count - artists.items.len();
let filtered_artists = original_artist_count - artists.items.len();
if filtered_artists > 0 {
artists.total = artists.total.saturating_sub(filtered_artists as u32);
}
+2 -1
View File
@@ -183,6 +183,7 @@ async fn search_itunes_by_term(title: &str, artist: &str) -> Option<String> {
/// iTunes album lookup response
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
struct ItunesAlbumResult {
collection_id: Option<u64>,
collection_view_url: Option<String>,
@@ -200,7 +201,7 @@ struct ItunesAlbumLookupResponse {
#[tauri::command]
pub async fn share_album_songlink(
upc: Option<String>,
album_id: Option<String>,
_album_id: Option<String>,
title: Option<String>,
artist: Option<String>,
state: State<'_, AppState>,
+1
View File
@@ -46,6 +46,7 @@ pub struct DiscogsImageOption {
/// Release details from Discogs API (internal, for artwork)
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct ReleaseDetails {
id: u64,
title: String,
+1 -1
View File
@@ -443,7 +443,7 @@ pub fn run() {
// Disconnect Chromecast if connected (sends message through channel)
if let Some(cast_state) = window.app_handle().try_state::<cast::CastState>() {
log::info!("Disconnecting Chromecast on app exit");
cast_state.chromecast.disconnect();
let _ = cast_state.chromecast.disconnect();
}
// Note: DLNA connection will be dropped when the app exits,
+17 -17
View File
@@ -71,7 +71,7 @@ pub async fn library_add_folder(
log::info!("Folder network info: is_network={}, fs_type={:?}", is_network, fs_type);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
let id = db.add_folder_with_network_info(&path, is_network, fs_type.as_deref())
.map_err(|e| e.to_string())?;
@@ -88,7 +88,7 @@ pub async fn library_remove_folder(
) -> Result<(), String> {
log::info!("Command: library_remove_folder {}", path);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.remove_folder(&path).map_err(|e| e.to_string())?;
db.delete_tracks_in_folder(&path).map_err(|e| e.to_string())?;
Ok(())
@@ -109,7 +109,7 @@ pub async fn library_cleanup_missing_files(
) -> Result<CleanupResult, String> {
log::info!("Command: library_cleanup_missing_files");
let mut db = state.db.lock().await;
let db = state.db.lock().await;
// Get all track paths
let tracks = db.get_all_track_paths().map_err(|e| e.to_string())?;
@@ -249,7 +249,7 @@ pub async fn library_clear_thumbnails_cache() -> Result<u64, String> {
pub async fn library_get_folders(state: State<'_, LibraryState>) -> Result<Vec<String>, String> {
log::info!("Command: library_get_folders");
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.get_folders().map_err(|e| e.to_string())
}
@@ -261,7 +261,7 @@ pub async fn library_get_folders_with_metadata(
) -> Result<Vec<LibraryFolder>, String> {
log::info!("Command: library_get_folders_with_metadata");
let mut db = state.db.lock().await;
let db = state.db.lock().await;
let mut folders = db.get_folders_with_metadata().map_err(|e| e.to_string())?;
// Refresh network detection for folders without user override
@@ -316,7 +316,7 @@ pub async fn library_get_folder(
) -> Result<Option<LibraryFolder>, String> {
log::info!("Command: library_get_folder {}", id);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.get_folder_by_id(id).map_err(|e| e.to_string())
}
@@ -333,7 +333,7 @@ pub async fn library_update_folder_settings(
) -> Result<LibraryFolder, String> {
log::info!("Command: library_update_folder_settings {} alias={:?} enabled={}", id, alias, enabled);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.update_folder_settings(
id,
alias.as_deref(),
@@ -357,7 +357,7 @@ pub async fn library_set_folder_enabled(
) -> Result<(), String> {
log::info!("Command: library_set_folder_enabled {} enabled={}", id, enabled);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.set_folder_enabled(id, enabled).map_err(|e| e.to_string())
}
@@ -379,7 +379,7 @@ pub async fn library_update_folder_path(
return Err("The selected path is not a folder".to_string());
}
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.update_folder_path(id, &new_path).map_err(|e| e.to_string())?;
// Check if it's a network folder and update network info
@@ -691,7 +691,7 @@ async fn process_cue_file(cue_path: &Path, state: &State<'_, LibraryState>) -> R
}
// Insert tracks
let mut db = state.db.lock().await;
let db = state.db.lock().await;
let group_key = tracks
.first()
.map(|track| track.album_group_key.clone())
@@ -1022,7 +1022,7 @@ pub async fn library_get_albums(
.map(|s| s.show_in_library)
.unwrap_or(false);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
// Use optimized SQL-based filtering instead of N+1 query pattern
let albums = db.get_albums_with_full_filter(
@@ -1042,7 +1042,7 @@ pub async fn library_get_album_tracks(
) -> Result<Vec<LocalTrack>, String> {
log::info!("Command: library_get_album_tracks {}", album_group_key);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.get_album_tracks(&album_group_key)
.map_err(|e| e.to_string())
}
@@ -1063,7 +1063,7 @@ pub async fn library_get_artists(
.map(|s| s.show_in_library)
.unwrap_or(false);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
// Use optimized SQL-based filtering instead of N+1 query pattern
let artists = db.get_artists_with_filter(
@@ -1093,7 +1093,7 @@ pub async fn library_search(
.map(|s| s.show_in_library)
.unwrap_or(false);
let mut db = state.db.lock().await;
let db = state.db.lock().await;
// Use optimized SQL-based filtering
// limit = 0 means no limit (fetch all tracks)
@@ -1112,7 +1112,7 @@ pub async fn library_search(
pub async fn library_get_stats(state: State<'_, LibraryState>) -> Result<LibraryStats, String> {
log::info!("Command: library_get_stats");
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.get_stats().map_err(|e| e.to_string())
}
@@ -1120,7 +1120,7 @@ pub async fn library_get_stats(state: State<'_, LibraryState>) -> Result<Library
pub async fn library_clear(state: State<'_, LibraryState>) -> Result<(), String> {
log::info!("Command: library_clear");
let mut db = state.db.lock().await;
let db = state.db.lock().await;
db.clear_all_tracks().map_err(|e| e.to_string())
}
@@ -1893,7 +1893,7 @@ pub async fn library_write_album_metadata_to_files(
let write_result = tokio::task::spawn_blocking({
let request = request.clone();
move || -> Result<(), String> {
use lofty::{Accessor, AudioFile, ItemKey, Tag, TagExt, TaggedFileExt, TagType};
use lofty::{Accessor, AudioFile, ItemKey, Tag, TaggedFileExt};
// Ensure we only write each file once.
let mut by_file: HashMap<String, &LibraryAlbumTrackMetadataUpdate> = HashMap::new();
+2 -1
View File
@@ -1,6 +1,6 @@
//! Metadata extraction for audio files
use lofty::{Accessor, AudioFile, ItemKey, MimeType, Probe, TaggedFileExt};
use lofty::{Accessor, AudioFile, ItemKey, Probe, TaggedFileExt};
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
@@ -467,6 +467,7 @@ impl MetadataExtractor {
}
/// Simple hash function for generating filenames
#[allow(dead_code)]
fn simple_hash(s: &str) -> u64 {
let mut hash: u64 = 5381;
for byte in s.bytes() {
+1 -1
View File
@@ -2,7 +2,7 @@
//!
//! SQLite-based queue for offline listens and token persistence
use rusqlite::{params, Connection, OptionalExtension};
use rusqlite::{params, Connection};
use std::path::Path;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
+1
View File
@@ -16,6 +16,7 @@ pub struct LyricsData {
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
struct LrclibItem {
#[serde(default)]
pub track_name: String,
+1 -1
View File
@@ -11,7 +11,7 @@ use std::sync::{
Arc, Mutex,
};
use std::thread;
use tauri::{AppHandle, Emitter, Manager};
use tauri::{AppHandle, Emitter};
use serde::Serialize;
/// Track metadata for media controls
+1 -2
View File
@@ -6,8 +6,7 @@ use tauri::{AppHandle, Emitter, State};
use crate::api::models::Quality;
use crate::AppState;
use crate::offline_cache::path_validator::{self, PathValidationResult};
use crate::offline_cache::{OfflineCacheDb, OfflineCacheState};
use crate::offline_cache::OfflineCacheState;
use crate::offline_cache::metadata::{fetch_complete_metadata, write_flac_tags, embed_artwork, organize_cached_file, save_album_artwork};
use super::{
CachedTrackInfo, OfflineCacheStats, OfflineCacheStatus,
+8 -2
View File
@@ -22,7 +22,7 @@ use std::sync::mpsc::{self, Sender, RecvTimeoutError};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
use rodio::{Decoder, OutputStream, Sink, Source};
use rodio::{Decoder, OutputStream, Source};
use rodio::buffer::SamplesBuffer;
use rodio::decoder::Mp4Type;
use rodio::cpal::traits::{DeviceTrait, HostTrait};
@@ -110,6 +110,7 @@ impl Seek for CursorMediaSource {
}
/// Audio specifications extracted from decoded audio
#[allow(dead_code)]
struct AudioSpecs {
samples: SamplesBuffer<i16>,
sample_rate: u32,
@@ -201,12 +202,14 @@ fn is_isomp4(data: &[u8]) -> bool {
/// Extract audio metadata (sample rate, channels) without full decode.
/// This is much faster than decode_with_symphonia as it only reads headers.
/// Audio metadata extracted from file headers
#[allow(dead_code)]
struct AudioMetadata {
sample_rate: u32,
channels: u16,
bit_depth: Option<u32>,
}
#[allow(dead_code)]
fn extract_audio_metadata(data: &[u8]) -> Result<(u32, u16), String> {
let meta = extract_audio_metadata_full(data)?;
Ok((meta.sample_rate, meta.channels))
@@ -642,6 +645,7 @@ pub struct Player {
/// Audio settings (exclusive mode, DAC passthrough, etc.)
audio_settings: Arc<Mutex<AudioSettings>>,
/// Visualizer tap for audio sample capture (optional)
#[allow(dead_code)]
visualizer_tap: Option<VisualizerTap>,
}
@@ -790,7 +794,9 @@ impl Player {
let mut current_sample_rate: Option<u32> = None;
let mut current_channels: Option<u16> = None;
#[allow(dead_code)]
const MAX_INIT_RETRIES: u32 = 5;
#[allow(dead_code)]
const RETRY_DELAY_MS: u64 = 500;
let mut current_engine: Option<PlaybackEngine> = None;
@@ -808,7 +814,7 @@ impl Player {
log::info!("Audio thread ready and waiting for commands");
let mut handle_command = |command: AudioCommand,
let handle_command = |command: AudioCommand,
current_engine: &mut Option<PlaybackEngine>,
current_audio_data: &mut Option<Vec<u8>>,
current_streaming_source: &mut Option<Arc<BufferedMediaSource>>,
+4 -1
View File
@@ -7,7 +7,7 @@
//! This abstraction allows the player to work with both approaches transparently.
use crate::audio::AlsaDirectStream;
use rodio::{OutputStream, OutputStreamHandle, Sink, Source};
use rodio::{OutputStreamHandle, Sink, Source};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;
@@ -270,6 +270,7 @@ impl PlaybackEngine {
}
/// Get current position in seconds (for ALSA Direct only)
#[allow(dead_code)]
pub fn position_secs(&self) -> Option<u64> {
match self {
Self::Rodio { .. } => None, // Rodio doesn't expose position directly
@@ -286,6 +287,7 @@ impl PlaybackEngine {
}
/// Get duration in seconds (for ALSA Direct only)
#[allow(dead_code)]
pub fn duration_secs(&self) -> Option<u64> {
match self {
Self::Rodio { .. } => None,
@@ -302,6 +304,7 @@ impl PlaybackEngine {
}
/// Check if using ALSA Direct engine
#[allow(dead_code)]
pub fn is_alsa_direct(&self) -> bool {
matches!(self, Self::AlsaDirect { .. })
}
@@ -4,7 +4,6 @@ use serde_json::Value;
use crate::playlist_import::errors::PlaylistImportError;
use crate::playlist_import::models::{ImportPlaylist, ImportProvider, ImportTrack};
use crate::playlist_import::providers::ProviderCredentials;
// Cloudflare Workers proxy URL - handles credentials
const SPOTIFY_PROXY_URL: &str = "https://qbz-api-proxy.blitzkriegfc.workers.dev/spotify";
@@ -8,7 +8,6 @@ use tokio::time::sleep;
use crate::playlist_import::errors::PlaylistImportError;
use crate::playlist_import::models::{ImportPlaylist, ImportProvider, ImportTrack};
use crate::playlist_import::providers::ProviderCredentials;
const RATE_LIMIT_DELAY_MS: u64 = 200; // Delay between API calls to avoid 429
+1 -1
View File
@@ -444,7 +444,7 @@ pub async fn get_playlist_suggestions(
// Find tracks by this artist in our candidates and boost them
// Note: We don't have track->artist mapping here, so this is implicit
// through the scoring system (tracks played when artist was played)
for (track_id, score) in candidates.iter_mut() {
for (_track_id, score) in candidates.iter_mut() {
// Boost slightly based on artist affinity (implicit)
*score += artist_score * 0.1;
}