chore(deps): rusqlite 0.40 (u64 casts; a latent two-statement execute fixed)
rusqlite 0.40 removed ToSql/FromSql for u64 (SQLite integers are i64): every u64 column now reads/writes through an explicit i64 cast. It also rejects a multi-statement execute() instead of silently running only the first statement — which is what catalog.rs's editions/logical_albums prune had been doing all along; it is an execute_batch now, so the logical_albums prune finally runs. cc
This commit is contained in:
26 files changed
+97
-93
No files matched your search
Generated
+30
-28
@@ -43,18 +43,6 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.8.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"version_check",
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.5"
|
version = "1.1.5"
|
||||||
@@ -2062,15 +2050,6 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.14.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.5"
|
version = "0.15.5"
|
||||||
@@ -2104,11 +2083,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashlink"
|
name = "hashlink"
|
||||||
version = "0.9.1"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
checksum = "32069d97bb81e38fa67eab65e3393bf804bb85969f2bc06bf13f64aef5aba248"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.14.5",
|
"hashbrown 0.17.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2847,9 +2826,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.28.0"
|
version = "0.38.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
|
checksum = "f1d20bef17f513b9b3004532233187769cd072d790971f4e4da0e346eb6401e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
@@ -5226,6 +5205,16 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rsqlite-vfs"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"thiserror 2.0.20",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtrb"
|
name = "rtrb"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@@ -5254,9 +5243,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
version = "0.31.0"
|
version = "0.40.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
|
checksum = "23f2a97da3e3873c73cb2a2e71b35c40ff95e0b1eefa8d72d8499a6928c3b5b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.1",
|
"bitflags 2.13.1",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
@@ -5264,6 +5253,7 @@ dependencies = [
|
|||||||
"hashlink",
|
"hashlink",
|
||||||
"libsqlite3-sys",
|
"libsqlite3-sys",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
"sqlite-wasm-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5735,6 +5725,18 @@ dependencies = [
|
|||||||
"lock_api",
|
"lock_api",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlite-wasm-rs"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"js-sys",
|
||||||
|
"rsqlite-vfs",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ssdp-client"
|
name = "ssdp-client"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ serde = { workspace = true }
|
|||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
dirs = "6"
|
dirs = "6"
|
||||||
uuid = { version = "1", features = ["v4"] }
|
uuid = { version = "1", features = ["v4"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ impl BlacklistService {
|
|||||||
.map_err(|e| format!("Failed to prepare blacklist query: {}", e))?;
|
.map_err(|e| format!("Failed to prepare blacklist query: {}", e))?;
|
||||||
|
|
||||||
let ids: Vec<u64> = stmt
|
let ids: Vec<u64> = stmt
|
||||||
.query_map([], |row| row.get(0))
|
.query_map([], |row| row.get::<_, i64>(0).map(|v| v as u64))
|
||||||
.map_err(|e| format!("Failed to query blacklist: {}", e))?
|
.map_err(|e| format!("Failed to query blacklist: {}", e))?
|
||||||
.filter_map(|r| r.ok())
|
.filter_map(|r| r.ok())
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@@ -335,11 +335,11 @@ impl RecoStore {
|
|||||||
params![
|
params![
|
||||||
event.event_type.as_str(),
|
event.event_type.as_str(),
|
||||||
event.item_type.as_str(),
|
event.item_type.as_str(),
|
||||||
event.track_id,
|
event.track_id.map(|v| v as i64),
|
||||||
event.album_id.as_deref(),
|
event.album_id.as_deref(),
|
||||||
event.artist_id,
|
event.artist_id.map(|v| v as i64),
|
||||||
event.playlist_id,
|
event.playlist_id.map(|v| v as i64),
|
||||||
event.genre_id,
|
event.genre_id.map(|v| v as i64),
|
||||||
now_ts(),
|
now_ts(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -405,7 +405,7 @@ impl RecoStore {
|
|||||||
.map_err(|e| format!("Failed to prepare recent tracks query: {}", e))?;
|
.map_err(|e| format!("Failed to prepare recent tracks query: {}", e))?;
|
||||||
|
|
||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map(params![limit], |row| row.get::<_, u64>(0))
|
.query_map(params![limit], |row| row.get::<_, i64>(0).map(|v| v as u64))
|
||||||
.map_err(|e| format!("Failed to query recent tracks: {}", e))?;
|
.map_err(|e| format!("Failed to query recent tracks: {}", e))?;
|
||||||
|
|
||||||
let mut tracks = Vec::new();
|
let mut tracks = Vec::new();
|
||||||
@@ -439,7 +439,7 @@ impl RecoStore {
|
|||||||
.map_err(|e| format!("Failed to prepare windowed recent tracks query: {}", e))?;
|
.map_err(|e| format!("Failed to prepare windowed recent tracks query: {}", e))?;
|
||||||
|
|
||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map(params![since_ts, limit], |row| row.get::<_, u64>(0))
|
.query_map(params![since_ts, limit], |row| row.get::<_, i64>(0).map(|v| v as u64))
|
||||||
.map_err(|e| format!("Failed to query windowed recent tracks: {}", e))?;
|
.map_err(|e| format!("Failed to query windowed recent tracks: {}", e))?;
|
||||||
|
|
||||||
let mut tracks = Vec::new();
|
let mut tracks = Vec::new();
|
||||||
@@ -467,7 +467,7 @@ impl RecoStore {
|
|||||||
.map_err(|e| format!("Failed to prepare favorite tracks query: {}", e))?;
|
.map_err(|e| format!("Failed to prepare favorite tracks query: {}", e))?;
|
||||||
|
|
||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map(params![limit], |row| row.get::<_, u64>(0))
|
.query_map(params![limit], |row| row.get::<_, i64>(0).map(|v| v as u64))
|
||||||
.map_err(|e| format!("Failed to query favorite tracks: {}", e))?;
|
.map_err(|e| format!("Failed to query favorite tracks: {}", e))?;
|
||||||
|
|
||||||
let mut tracks = Vec::new();
|
let mut tracks = Vec::new();
|
||||||
@@ -542,7 +542,7 @@ impl RecoStore {
|
|||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map(params![limit], |row| {
|
.query_map(params![limit], |row| {
|
||||||
Ok(TopArtistSeed {
|
Ok(TopArtistSeed {
|
||||||
artist_id: row.get::<_, u64>(0)?,
|
artist_id: row.get::<_, i64>(0).map(|v| v as u64)?,
|
||||||
play_count: row.get::<_, u32>(1)?,
|
play_count: row.get::<_, u32>(1)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -576,7 +576,7 @@ impl RecoStore {
|
|||||||
)
|
)
|
||||||
.map_err(|e| format!("Failed to prepare known artists query: {}", e))?;
|
.map_err(|e| format!("Failed to prepare known artists query: {}", e))?;
|
||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map(params![play_threshold], |row| row.get::<_, u64>(0))
|
.query_map(params![play_threshold], |row| row.get::<_, i64>(0).map(|v| v as u64))
|
||||||
.map_err(|e| format!("Failed to query known artists: {}", e))?;
|
.map_err(|e| format!("Failed to query known artists: {}", e))?;
|
||||||
let mut ids = std::collections::HashSet::new();
|
let mut ids = std::collections::HashSet::new();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
@@ -604,7 +604,7 @@ impl RecoStore {
|
|||||||
.map_err(|e| format!("Failed to prepare top genres query: {}", e))?;
|
.map_err(|e| format!("Failed to prepare top genres query: {}", e))?;
|
||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map(params![limit], |row| {
|
.query_map(params![limit], |row| {
|
||||||
Ok((row.get::<_, u64>(0)?, row.get::<_, String>(1)?))
|
Ok((row.get::<_, i64>(0).map(|v| v as u64)?, row.get::<_, String>(1)?))
|
||||||
})
|
})
|
||||||
.map_err(|e| format!("Failed to query top genres: {}", e))?;
|
.map_err(|e| format!("Failed to query top genres: {}", e))?;
|
||||||
let mut genres = Vec::new();
|
let mut genres = Vec::new();
|
||||||
@@ -703,7 +703,7 @@ impl RecoStore {
|
|||||||
)
|
)
|
||||||
.map_err(|e| format!("Failed to prepare scored tracks query: {}", e))?;
|
.map_err(|e| format!("Failed to prepare scored tracks query: {}", e))?;
|
||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map(params![score_type, limit], |row| row.get::<_, u64>(0))
|
.query_map(params![score_type, limit], |row| row.get::<_, i64>(0).map(|v| v as u64))
|
||||||
.map_err(|e| format!("Failed to query scored tracks: {}", e))?;
|
.map_err(|e| format!("Failed to query scored tracks: {}", e))?;
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
@@ -729,7 +729,7 @@ impl RecoStore {
|
|||||||
.map_err(|e| format!("Failed to prepare scored artists query: {}", e))?;
|
.map_err(|e| format!("Failed to prepare scored artists query: {}", e))?;
|
||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map(params![score_type, limit], |row| {
|
.query_map(params![score_type, limit], |row| {
|
||||||
Ok((row.get::<_, u64>(0)?, row.get::<_, f64>(1)?))
|
Ok((row.get::<_, i64>(0).map(|v| v as u64)?, row.get::<_, f64>(1)?))
|
||||||
})
|
})
|
||||||
.map_err(|e| format!("Failed to query scored artists: {}", e))?;
|
.map_err(|e| format!("Failed to query scored artists: {}", e))?;
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
@@ -761,10 +761,10 @@ impl RecoStore {
|
|||||||
Ok(RecoEventRecord {
|
Ok(RecoEventRecord {
|
||||||
event_type: row.get(0)?,
|
event_type: row.get(0)?,
|
||||||
item_type: row.get(1)?,
|
item_type: row.get(1)?,
|
||||||
track_id: row.get(2)?,
|
track_id: row.get::<_, Option<i64>>(2)?.map(|v| v as u64),
|
||||||
album_id: row.get(3)?,
|
album_id: row.get(3)?,
|
||||||
artist_id: row.get(4)?,
|
artist_id: row.get::<_, Option<i64>>(4)?.map(|v| v as u64),
|
||||||
genre_id: row.get(5)?,
|
genre_id: row.get::<_, Option<i64>>(5)?.map(|v| v as u64),
|
||||||
created_at: row.get(6)?,
|
created_at: row.get(6)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -808,9 +808,9 @@ impl RecoStore {
|
|||||||
stmt.execute(params![
|
stmt.execute(params![
|
||||||
score_type,
|
score_type,
|
||||||
item_type,
|
item_type,
|
||||||
entry.track_id,
|
entry.track_id.map(|v| v as i64),
|
||||||
entry.album_id.as_deref(),
|
entry.album_id.as_deref(),
|
||||||
entry.artist_id,
|
entry.artist_id.map(|v| v as i64),
|
||||||
entry.score,
|
entry.score,
|
||||||
updated_at,
|
updated_at,
|
||||||
])
|
])
|
||||||
@@ -1071,7 +1071,7 @@ impl RecoStore {
|
|||||||
.conn
|
.conn
|
||||||
.execute(
|
.execute(
|
||||||
"UPDATE reco_events SET genre_id = ? WHERE album_id = ? AND genre_id IS NULL",
|
"UPDATE reco_events SET genre_id = ? WHERE album_id = ? AND genre_id IS NULL",
|
||||||
params![genre_id, album_id],
|
params![genre_id as i64, album_id],
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("Failed to update genre for album: {}", e))?;
|
.map_err(|e| format!("Failed to update genre for album: {}", e))?;
|
||||||
Ok(affected as u64)
|
Ok(affected as u64)
|
||||||
@@ -1147,7 +1147,7 @@ mod tests {
|
|||||||
r#"INSERT INTO reco_events
|
r#"INSERT INTO reco_events
|
||||||
(event_type, item_type, track_id, album_id, artist_id, playlist_id, genre_id, created_at)
|
(event_type, item_type, track_id, album_id, artist_id, playlist_id, genre_id, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, NULL, ?, ?)"#,
|
VALUES (?, ?, ?, ?, ?, NULL, ?, ?)"#,
|
||||||
params![event_type, item_type, track_id, album_id, artist_id, genre_id, created_at],
|
params![event_type, item_type, track_id.map(|v| v as i64), album_id, artist_id.map(|v| v as i64), genre_id.map(|v| v as i64), created_at],
|
||||||
)
|
)
|
||||||
.expect("insert event at ts");
|
.expect("insert event at ts");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ rustfft = "6.4"
|
|||||||
spectrum-analyzer = "1.5"
|
spectrum-analyzer = "1.5"
|
||||||
|
|
||||||
# Database for loudness cache
|
# Database for loudness cache
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
|
|
||||||
# System directories
|
# System directories
|
||||||
dirs = "6"
|
dirs = "6"
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ dirs = "6"
|
|||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
|
|
||||||
# Image cache index + URL hashing
|
# Image cache index + URL hashing
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
md-5 = "0.10"
|
md-5 = "0.10"
|
||||||
@@ -11,7 +11,7 @@ base64 = { workspace = true }
|
|||||||
# The disc MEMORY (src/store.rs). SQLite rather than a JSON file because the
|
# The disc MEMORY (src/store.rs). SQLite rather than a JSON file because the
|
||||||
# app already speaks it everywhere and a row-at-a-time upsert is what the
|
# app already speaks it everywhere and a row-at-a-time upsert is what the
|
||||||
# "never overwrite a human" rule needs.
|
# "never overwrite a human" rule needs.
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
dirs = "6"
|
dirs = "6"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ serde = { workspace = true }
|
|||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
# Cache (per-user SQLite, WAL per ADR-002)
|
# Cache (per-user SQLite, WAL per ADR-002)
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
@@ -30,7 +30,7 @@ urlencoding = "2"
|
|||||||
discord-rich-presence = "1.1.0"
|
discord-rich-presence = "1.1.0"
|
||||||
|
|
||||||
# SQLite for caching (optional, enabled by default)
|
# SQLite for caching (optional, enabled by default)
|
||||||
rusqlite = { version = "0.31", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.40", features = ["bundled"], optional = true }
|
||||||
|
|
||||||
# Time handling for cache TTL
|
# Time handling for cache TTL
|
||||||
chrono = { version = "0.4", optional = true }
|
chrono = { version = "0.4", optional = true }
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ serde_json = "1"
|
|||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
|
|
||||||
# Filesystem
|
# Filesystem
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
|||||||
@@ -1369,12 +1369,12 @@ impl LibraryDatabase {
|
|||||||
track.genre,
|
track.genre,
|
||||||
genres_json,
|
genres_json,
|
||||||
track.catalog_number,
|
track.catalog_number,
|
||||||
track.duration_secs,
|
track.duration_secs as i64,
|
||||||
track.format.to_string(),
|
track.format.to_string(),
|
||||||
track.bit_depth,
|
track.bit_depth,
|
||||||
track.sample_rate,
|
track.sample_rate,
|
||||||
track.channels,
|
track.channels,
|
||||||
track.file_size_bytes,
|
track.file_size_bytes as i64,
|
||||||
track.cue_file_path,
|
track.cue_file_path,
|
||||||
track.cue_start_secs,
|
track.cue_start_secs,
|
||||||
track.cue_end_secs,
|
track.cue_end_secs,
|
||||||
@@ -1416,12 +1416,12 @@ impl LibraryDatabase {
|
|||||||
track.genre,
|
track.genre,
|
||||||
genres_json,
|
genres_json,
|
||||||
track.catalog_number,
|
track.catalog_number,
|
||||||
track.duration_secs,
|
track.duration_secs as i64,
|
||||||
track.format.to_string(),
|
track.format.to_string(),
|
||||||
track.bit_depth,
|
track.bit_depth,
|
||||||
track.sample_rate,
|
track.sample_rate,
|
||||||
track.channels,
|
track.channels,
|
||||||
track.file_size_bytes,
|
track.file_size_bytes as i64,
|
||||||
track.cue_file_path,
|
track.cue_file_path,
|
||||||
track.cue_start_secs,
|
track.cue_start_secs,
|
||||||
track.cue_end_secs,
|
track.cue_end_secs,
|
||||||
@@ -1804,7 +1804,7 @@ impl LibraryDatabase {
|
|||||||
artwork_path,
|
artwork_path,
|
||||||
artwork_source: None,
|
artwork_source: None,
|
||||||
track_count: row.get(7)?,
|
track_count: row.get(7)?,
|
||||||
total_duration_secs: row.get(8)?,
|
total_duration_secs: row.get::<_, i64>(8)? as u64,
|
||||||
format: Self::parse_format(
|
format: Self::parse_format(
|
||||||
&row.get::<_, Option<String>>(9)?.unwrap_or_default(),
|
&row.get::<_, Option<String>>(9)?.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
@@ -2302,7 +2302,7 @@ impl LibraryDatabase {
|
|||||||
artwork_path,
|
artwork_path,
|
||||||
artwork_source: None,
|
artwork_source: None,
|
||||||
track_count: row.get(7)?,
|
track_count: row.get(7)?,
|
||||||
total_duration_secs: row.get(8)?,
|
total_duration_secs: row.get::<_, i64>(8)? as u64,
|
||||||
format: Self::parse_format(
|
format: Self::parse_format(
|
||||||
&row.get::<_, Option<String>>(9)?.unwrap_or_default(),
|
&row.get::<_, Option<String>>(9)?.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
@@ -2855,7 +2855,7 @@ impl LibraryDatabase {
|
|||||||
artwork_path,
|
artwork_path,
|
||||||
artwork_source: None,
|
artwork_source: None,
|
||||||
track_count: row.get(7)?,
|
track_count: row.get(7)?,
|
||||||
total_duration_secs: row.get(8)?,
|
total_duration_secs: row.get::<_, i64>(8)? as u64,
|
||||||
format: Self::parse_format(
|
format: Self::parse_format(
|
||||||
&row.get::<_, Option<String>>(9)?.unwrap_or_default(),
|
&row.get::<_, Option<String>>(9)?.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
@@ -3778,8 +3778,8 @@ impl LibraryDatabase {
|
|||||||
track_count: row.get(0)?,
|
track_count: row.get(0)?,
|
||||||
album_count: row.get(1)?,
|
album_count: row.get(1)?,
|
||||||
artist_count: row.get(2)?,
|
artist_count: row.get(2)?,
|
||||||
total_duration_secs: row.get(3)?,
|
total_duration_secs: row.get::<_, i64>(3)? as u64,
|
||||||
total_size_bytes: row.get(4)?,
|
total_size_bytes: row.get::<_, i64>(4)? as u64,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|e| LibraryError::Database(e.to_string()))
|
.map_err(|e| LibraryError::Database(e.to_string()))
|
||||||
@@ -3862,12 +3862,12 @@ impl LibraryDatabase {
|
|||||||
year: row.get(8)?, // year
|
year: row.get(8)?, // year
|
||||||
genre,
|
genre,
|
||||||
genres,
|
genres,
|
||||||
duration_secs: row.get(11)?, // duration_secs
|
duration_secs: row.get::<_, i64>(11)? as u64, // duration_secs
|
||||||
format: Self::parse_format(&row.get::<_, String>(12)?), // format
|
format: Self::parse_format(&row.get::<_, String>(12)?), // format
|
||||||
bit_depth: row.get(13)?, // bit_depth
|
bit_depth: row.get(13)?, // bit_depth
|
||||||
sample_rate: row.get::<_, f64>(14)?, // sample_rate
|
sample_rate: row.get::<_, f64>(14)?, // sample_rate
|
||||||
channels: row.get(15)?, // channels
|
channels: row.get(15)?, // channels
|
||||||
file_size_bytes: row.get(16)?, // file_size_bytes
|
file_size_bytes: row.get::<_, i64>(16)? as u64, // file_size_bytes
|
||||||
cue_file_path: row.get(17)?, // cue_file_path
|
cue_file_path: row.get(17)?, // cue_file_path
|
||||||
cue_start_secs: row.get(18)?, // cue_start_secs
|
cue_start_secs: row.get(18)?, // cue_start_secs
|
||||||
cue_end_secs: row.get(19)?, // cue_end_secs
|
cue_end_secs: row.get(19)?, // cue_end_secs
|
||||||
@@ -5104,12 +5104,12 @@ impl LibraryDatabase {
|
|||||||
row.get::<_, Option<String>>(11)?.as_deref(),
|
row.get::<_, Option<String>>(11)?.as_deref(),
|
||||||
),
|
),
|
||||||
catalog_number: None,
|
catalog_number: None,
|
||||||
duration_secs: row.get(13)?,
|
duration_secs: row.get::<_, i64>(13)? as u64,
|
||||||
format: Self::parse_format(&row.get::<_, String>(14)?),
|
format: Self::parse_format(&row.get::<_, String>(14)?),
|
||||||
bit_depth: row.get(15)?,
|
bit_depth: row.get(15)?,
|
||||||
sample_rate: row.get::<_, f64>(16)?,
|
sample_rate: row.get::<_, f64>(16)?,
|
||||||
channels: row.get(17)?,
|
channels: row.get(17)?,
|
||||||
file_size_bytes: row.get(18)?,
|
file_size_bytes: row.get::<_, i64>(18)? as u64,
|
||||||
cue_file_path: row.get(19)?,
|
cue_file_path: row.get(19)?,
|
||||||
cue_start_secs: row.get(20)?,
|
cue_start_secs: row.get(20)?,
|
||||||
cue_end_secs: row.get(21)?,
|
cue_end_secs: row.get(21)?,
|
||||||
@@ -5179,12 +5179,12 @@ impl LibraryDatabase {
|
|||||||
row.get::<_, Option<String>>(11)?.as_deref(),
|
row.get::<_, Option<String>>(11)?.as_deref(),
|
||||||
),
|
),
|
||||||
catalog_number: None,
|
catalog_number: None,
|
||||||
duration_secs: row.get(13)?,
|
duration_secs: row.get::<_, i64>(13)? as u64,
|
||||||
format: Self::parse_format(&row.get::<_, String>(14)?),
|
format: Self::parse_format(&row.get::<_, String>(14)?),
|
||||||
bit_depth: row.get(15)?,
|
bit_depth: row.get(15)?,
|
||||||
sample_rate: row.get::<_, f64>(16)?,
|
sample_rate: row.get::<_, f64>(16)?,
|
||||||
channels: row.get(17)?,
|
channels: row.get(17)?,
|
||||||
file_size_bytes: row.get(18)?,
|
file_size_bytes: row.get::<_, i64>(18)? as u64,
|
||||||
cue_file_path: row.get(19)?,
|
cue_file_path: row.get(19)?,
|
||||||
cue_start_secs: row.get(20)?,
|
cue_start_secs: row.get(20)?,
|
||||||
cue_end_secs: row.get(21)?,
|
cue_end_secs: row.get(21)?,
|
||||||
|
|||||||
@@ -344,12 +344,12 @@ fn update_track(
|
|||||||
track.year,
|
track.year,
|
||||||
track.genre,
|
track.genre,
|
||||||
track.catalog_number,
|
track.catalog_number,
|
||||||
track.duration_secs,
|
track.duration_secs as i64,
|
||||||
format,
|
format,
|
||||||
track.bit_depth,
|
track.bit_depth,
|
||||||
track.sample_rate,
|
track.sample_rate,
|
||||||
track.channels,
|
track.channels,
|
||||||
track.file_size_bytes,
|
track.file_size_bytes as i64,
|
||||||
track.artwork_path,
|
track.artwork_path,
|
||||||
track.last_modified,
|
track.last_modified,
|
||||||
track.indexed_at,
|
track.indexed_at,
|
||||||
@@ -397,12 +397,12 @@ fn insert_track(
|
|||||||
track.year,
|
track.year,
|
||||||
track.genre,
|
track.genre,
|
||||||
track.catalog_number,
|
track.catalog_number,
|
||||||
track.duration_secs,
|
track.duration_secs as i64,
|
||||||
format,
|
format,
|
||||||
track.bit_depth,
|
track.bit_depth,
|
||||||
track.sample_rate,
|
track.sample_rate,
|
||||||
track.channels,
|
track.channels,
|
||||||
track.file_size_bytes,
|
track.file_size_bytes as i64,
|
||||||
track.artwork_path,
|
track.artwork_path,
|
||||||
track.last_modified,
|
track.last_modified,
|
||||||
track.indexed_at,
|
track.indexed_at,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ license.workspace = true
|
|||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rusqlite = { version = "0.31", features = ["backup", "bundled"] }
|
rusqlite = { version = "0.40", features = ["backup", "bundled"] }
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::time::{Duration, Instant};
|
|||||||
|
|
||||||
use rusqlite::types::Value;
|
use rusqlite::types::Value;
|
||||||
use rusqlite::{
|
use rusqlite::{
|
||||||
params, params_from_iter, Connection, DatabaseName, OpenFlags, OptionalExtension, Row,
|
params, params_from_iter, Connection, OpenFlags, OptionalExtension, Row,
|
||||||
Statement,
|
Statement,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ impl Catalog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn backup_to(&self, path: &Path) -> Result<()> {
|
pub(crate) fn backup_to(&self, path: &Path) -> Result<()> {
|
||||||
self.conn.backup(DatabaseName::Main, path, None)?;
|
self.conn.backup(rusqlite::MAIN_DB, path, None)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,7 +495,10 @@ impl Catalog {
|
|||||||
)",
|
)",
|
||||||
params![batch.source.source.as_str(), batch.source.source_instance],
|
params![batch.source.source.as_str(), batch.source.source_instance],
|
||||||
)?;
|
)?;
|
||||||
tx.execute(
|
// Two statements, two calls: rusqlite < 0.32 silently ran only the
|
||||||
|
// FIRST statement of a multi-statement `execute` (the logical_albums
|
||||||
|
// prune below never happened); 0.40 refuses it outright.
|
||||||
|
tx.execute_batch(
|
||||||
"DELETE FROM editions
|
"DELETE FROM editions
|
||||||
WHERE NOT EXISTS (
|
WHERE NOT EXISTS (
|
||||||
SELECT 1 FROM source_copies sc
|
SELECT 1 FROM source_copies sc
|
||||||
@@ -506,7 +509,6 @@ impl Catalog {
|
|||||||
SELECT 1 FROM editions e
|
SELECT 1 FROM editions e
|
||||||
WHERE e.logical_album_id=logical_albums.logical_album_id
|
WHERE e.logical_album_id=logical_albums.logical_album_id
|
||||||
);",
|
);",
|
||||||
[],
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
tx.execute(
|
tx.execute(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ authors = ["blitzkriegfc"]
|
|||||||
qbz-qobuz = { path = "../qbz-qobuz" }
|
qbz-qobuz = { path = "../qbz-qobuz" }
|
||||||
|
|
||||||
# Database (same pin as every other QBZ crate)
|
# Database (same pin as every other QBZ crate)
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
|
|
||||||
# HTTP (external fallback providers: LRCLIB, lyrics.ovh)
|
# HTTP (external fallback providers: LRCLIB, lyrics.ovh)
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ license.workspace = true
|
|||||||
description = "One SQLite cache for every remote media server QBZ reads (Jellyfin, Subsonic, and Plex when it migrates)"
|
description = "One SQLite cache for every remote media server QBZ reads (Jellyfin, Subsonic, and Plex when it migrates)"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ qbz-models = { path = "../qbz-models" }
|
|||||||
qbz-qobuz = { path = "../qbz-qobuz" }
|
qbz-qobuz = { path = "../qbz-qobuz" }
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
|
|
||||||
# UUID generation
|
# UUID generation
|
||||||
uuid = { version = "1", features = ["v4"] }
|
uuid = { version = "1", features = ["v4"] }
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ qbz-secrets = { path = "../qbz-secrets" }
|
|||||||
qbz-library = { path = "../qbz-library" }
|
qbz-library = { path = "../qbz-library" }
|
||||||
|
|
||||||
# SQLite index (match qbz-library's version/features)
|
# SQLite index (match qbz-library's version/features)
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
|
|
||||||
# Async runtime + HTTP (download pipeline + artwork)
|
# Async runtime + HTTP (download pipeline + artwork)
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ description = "Plex LAN-only integration for QBZ: HTTP client, XML metadata pars
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# SQLite cache (match the workspace's bundled SQLite usage)
|
# SQLite cache (match the workspace's bundled SQLite usage)
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
|
|
||||||
# HTTP client for the Plex Media Server LAN API
|
# HTTP client for the Plex Media Server LAN API
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
|
|||||||
@@ -1568,7 +1568,7 @@ pub fn plex_cache_get_tracks(
|
|||||||
album: row
|
album: row
|
||||||
.get::<_, Option<String>>(3)?
|
.get::<_, Option<String>>(3)?
|
||||||
.map(|v| decode_xml_entities(v.trim())),
|
.map(|v| decode_xml_entities(v.trim())),
|
||||||
duration_ms: row.get(4)?,
|
duration_ms: row.get::<_, Option<i64>>(4)?.map(|v| v as u64),
|
||||||
artwork_path: row.get(5)?,
|
artwork_path: row.get(5)?,
|
||||||
collection_artwork_path: row.get(19)?,
|
collection_artwork_path: row.get(19)?,
|
||||||
part_key: row.get(6)?,
|
part_key: row.get(6)?,
|
||||||
@@ -1616,7 +1616,7 @@ pub fn plex_cache_get_tracks(
|
|||||||
album: row
|
album: row
|
||||||
.get::<_, Option<String>>(3)?
|
.get::<_, Option<String>>(3)?
|
||||||
.map(|v| decode_xml_entities(v.trim())),
|
.map(|v| decode_xml_entities(v.trim())),
|
||||||
duration_ms: row.get(4)?,
|
duration_ms: row.get::<_, Option<i64>>(4)?.map(|v| v as u64),
|
||||||
artwork_path: row.get(5)?,
|
artwork_path: row.get(5)?,
|
||||||
collection_artwork_path: row.get(19)?,
|
collection_artwork_path: row.get(19)?,
|
||||||
part_key: row.get(6)?,
|
part_key: row.get(6)?,
|
||||||
@@ -1685,7 +1685,7 @@ pub fn plex_cache_get_tracks_by_keys(rating_keys: &[String]) -> Result<Vec<PlexT
|
|||||||
album: row
|
album: row
|
||||||
.get::<_, Option<String>>(3)?
|
.get::<_, Option<String>>(3)?
|
||||||
.map(|v| decode_xml_entities(v.trim())),
|
.map(|v| decode_xml_entities(v.trim())),
|
||||||
duration_ms: row.get(4)?,
|
duration_ms: row.get::<_, Option<i64>>(4)?.map(|v| v as u64),
|
||||||
artwork_path: row.get(5)?,
|
artwork_path: row.get(5)?,
|
||||||
collection_artwork_path: row.get(19)?,
|
collection_artwork_path: row.get(19)?,
|
||||||
part_key: row.get(6)?,
|
part_key: row.get(6)?,
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ uuid = { version = "1", features = ["v4"] }
|
|||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
image = { version = "0.25", default-features = false, features = ["jpeg", "png"] }
|
image = { version = "0.25", default-features = false, features = ["jpeg", "png"] }
|
||||||
|
|
||||||
# Native file picker, used by exactly two call sites — the MyQBZ collection
|
# Native file picker, used by exactly two call sites — the MyQBZ collection
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ description = "Agnostic smart-radio pool builder for QBZ (ported from the Tauri
|
|||||||
qbz-models = { path = "../qbz-models" }
|
qbz-models = { path = "../qbz-models" }
|
||||||
qbz-qobuz = { path = "../qbz-qobuz" }
|
qbz-qobuz = { path = "../qbz-qobuz" }
|
||||||
|
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
dirs = "5"
|
dirs = "5"
|
||||||
@@ -7,7 +7,7 @@ description = "Frontend-agnostic artist-vector recommendation engine for QBZ (po
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ impl ArtistVectorStore {
|
|||||||
.execute(
|
.execute(
|
||||||
"INSERT OR REPLACE INTO vector_metadata (artist_idx, updated_at, nnz)
|
"INSERT OR REPLACE INTO vector_metadata (artist_idx, updated_at, nnz)
|
||||||
VALUES (?1, ?2, ?3)",
|
VALUES (?1, ?2, ?3)",
|
||||||
params![artist_idx, now, vector.nnz()],
|
params![artist_idx, now, vector.nnz() as i64],
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("Failed to update metadata: {}", e))?;
|
.map_err(|e| format!("Failed to update metadata: {}", e))?;
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ qbz-subsonic = { path = "../qbz-subsonic" }
|
|||||||
qbz-media-cache = { path = "../qbz-media-cache" }
|
qbz-media-cache = { path = "../qbz-media-cache" }
|
||||||
# Only to hold the cache's `Connection`. This crate never writes SQL of its own
|
# Only to hold the cache's `Connection`. This crate never writes SQL of its own
|
||||||
# — every statement lives in `qbz-media-cache` or `qbz-library`.
|
# — every statement lives in `qbz-media-cache` or `qbz-library`.
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||||
# `ItemResolver` — this crate IMPLEMENTS it (mixtape_adapter.rs). qbz-mixtape is
|
# `ItemResolver` — this crate IMPLEMENTS it (mixtape_adapter.rs). qbz-mixtape is
|
||||||
# provider-agnostic and the dependency remains one-directional, so the Qt
|
# provider-agnostic and the dependency remains one-directional, so the Qt
|
||||||
# application can consume the adapter without coupling qbz-mixtape to sources.
|
# application can consume the adapter without coupling qbz-mixtape to sources.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ log = { workspace = true }
|
|||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
# QConnect glue copies (T9) — mirror the desktop copies' direct imports
|
# QConnect glue copies (T9) — mirror the desktop copies' direct imports
|
||||||
uuid = { version = "1", features = ["v4"] } # renderer-report action uuids
|
uuid = { version = "1", features = ["v4"] } # renderer-report action uuids
|
||||||
rusqlite = { version = "0.31", features = ["bundled"] } # daemon-root qconnect_settings.db KV
|
rusqlite = { version = "0.40", features = ["bundled"] } # daemon-root qconnect_settings.db KV
|
||||||
futures-util = { workspace = true } # remote_stream.rs feeder (byte-exact)
|
futures-util = { workspace = true } # remote_stream.rs feeder (byte-exact)
|
||||||
# new direct deps (versions already in lock where noted)
|
# new direct deps (versions already in lock where noted)
|
||||||
tiny_http = "0.12" # in lock via qbz-cast
|
tiny_http = "0.12" # in lock via qbz-cast
|
||||||
|
|||||||
Reference in new issue
Block a user