From 4381750c6e3c83235014ce1d1993451689bad126 Mon Sep 17 00:00:00 2001 From: Ultradesu Date: Mon, 20 Jul 2026 16:29:21 +0300 Subject: [PATCH] Extend DHT scheme --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/federation/mod.rs | 35 +++++++++++++++++++++++++++-------- src/federation/serve.rs | 39 +++++++++++++++++++++++++++++++-------- src/main.rs | 2 +- 5 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e621270..4989eef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1717,7 +1717,7 @@ dependencies = [ [[package]] name = "federation-net" version = "0.1.0" -source = "git+https://gt.hexor.cy/ab/frid.git#3f15e9aebd574f7dd922e623e724eb7fde741b59" +source = "git+https://gt.hexor.cy/ab/frid.git#a897737978d476c223b66e65b960b70376084f2a" dependencies = [ "blake3", "data-encoding", @@ -3621,7 +3621,7 @@ dependencies = [ [[package]] name = "music-dht" version = "0.1.0" -source = "git+https://gt.hexor.cy/ab/frid.git#3f15e9aebd574f7dd922e623e724eb7fde741b59" +source = "git+https://gt.hexor.cy/ab/frid.git#a897737978d476c223b66e65b960b70376084f2a" dependencies = [ "async-trait", "blake3", diff --git a/Cargo.toml b/Cargo.toml index 2dfbd86..033f73c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "furumusic" -version = "0.6.2-fd" +version = "0.6.3-fd" edition = "2024" description = "Reusable web-app boilerplate: auth, OIDC/SSO, admin panel, user management, i18n, PostgreSQL" diff --git a/src/federation/mod.rs b/src/federation/mod.rs index 490aaa1..79e5041 100644 --- a/src/federation/mod.rs +++ b/src/federation/mod.rs @@ -355,8 +355,12 @@ impl Federation { kind: ItemKind::Artist, name: row.get(1), artist_names: Vec::new(), + featured_artist_names: Vec::new(), year: None, release_type: None, + release_title: None, + track_number: None, + disc_number: None, duration_seconds: None, }); } @@ -389,29 +393,40 @@ impl Federation { kind: ItemKind::Release, name: row.get(1), artist_names: artists_of_release.remove(&id).unwrap_or_default(), + featured_artist_names: Vec::new(), year: row.get(2), release_type: row.get(3), + release_title: None, + track_number: None, + disc_number: None, duration_seconds: None, }); } let track_artists = sqlx::query( - "SELECT ta.track_id, a.name FROM furumusic__track_artist ta + "SELECT ta.track_id, a.name, ta.role FROM furumusic__track_artist ta JOIN furumusic__artist a ON a.id = ta.artist_id WHERE ta.role IN ('main', 'featuring') - ORDER BY ta.track_id, ta.position", + ORDER BY ta.track_id, + CASE ta.role WHEN 'main' THEN 0 ELSE 1 END, + ta.position", ) .fetch_all(&pool) .await?; let mut artists_of_track: std::collections::HashMap> = Default::default(); + let mut featured_of_track: std::collections::HashMap> = Default::default(); for row in &track_artists { - artists_of_track - .entry(row.get(0)) - .or_default() - .push(row.get(1)); + let id: i64 = row.get(0); + let name: String = row.get(1); + if row.get::(2) == "featuring" { + featured_of_track.entry(id).or_default().push(name); + } else { + artists_of_track.entry(id).or_default().push(name); + } } let tracks = sqlx::query( - "SELECT t.id, t.title, COALESCE(t.year, r.year), t.duration_seconds + "SELECT t.id, t.title, COALESCE(t.year, r.year), t.duration_seconds, + r.title, r.release_type, t.track_number, t.disc_number FROM furumusic__track t JOIN furumusic__release r ON r.id = t.release_id WHERE t.is_hidden = false AND r.is_hidden = false", @@ -426,8 +441,12 @@ impl Federation { kind: ItemKind::Track, name: row.get(1), artist_names: artists_of_track.remove(&id).unwrap_or_default(), + featured_artist_names: featured_of_track.remove(&id).unwrap_or_default(), year: row.get(2), - release_type: None, + release_type: row.get(5), + release_title: Some(row.get(4)), + track_number: row.get(6), + disc_number: row.get(7), duration_seconds: (duration > 0.0).then_some(duration), }); } diff --git a/src/federation/serve.rs b/src/federation/serve.rs index 34adc6b..27ad077 100644 --- a/src/federation/serve.rs +++ b/src/federation/serve.rs @@ -150,7 +150,10 @@ async fn read_line(reader: &mut R) -> Result> { } } -async fn write_line(writer: &mut W, value: &impl Serialize) -> Result<()> { +async fn write_line( + writer: &mut W, + value: &impl Serialize, +) -> Result<()> { let mut line = serde_json::to_vec(value)?; line.push(b'\n'); writer.write_all(&line).await?; @@ -186,7 +189,10 @@ fn guess_mime(path: &Path) -> &'static str { } /// Reads an image media file from disk, bounded by [`MAX_IMAGE_BYTES`]. -async fn read_image(storage_dir: &str, media: Option<(String, String)>) -> Option<(Vec, String)> { +async fn read_image( + storage_dir: &str, + media: Option<(String, String)>, +) -> Option<(Vec, String)> { let (file_path, mime) = media?; let path = resolve_media_path(storage_dir, &file_path); let size = tokio::fs::metadata(&path).await.ok()?.len(); @@ -367,7 +373,9 @@ async fn serve_audio_one( Some(item_id) => match resolve_track_id(&pool, &own, item_id).await { Ok(Some(track_id)) => track_id, Ok(None) => return refuse_audio(stream, "track not found in the library").await, - Err(err) => return refuse_audio(stream, &format!("library lookup failed: {err:#}")).await, + Err(err) => { + return refuse_audio(stream, &format!("library lookup failed: {err:#}")).await; + } }, None => return refuse_audio(stream, "malformed item_id").await, }; @@ -378,7 +386,9 @@ async fn serve_audio_one( let path = resolve_media_path(&storage_dir, &file_path); let mut file = match tokio::fs::File::open(&path).await { Ok(file) => file, - Err(err) => return refuse_audio(stream, &format!("audio file is not readable: {err}")).await, + Err(err) => { + return refuse_audio(stream, &format!("audio file is not readable: {err}")).await; + } }; let total_size = file.metadata().await?.len(); let offset = request.offset.min(total_size); @@ -395,10 +405,17 @@ async fn serve_audio_one( }; let (cover, artist_image) = if request.want_cover { ( - read_image(&storage_dir, track_cover_file(&pool, track_id).await.ok().flatten()).await, read_image( &storage_dir, - track_artist_image_file(&pool, track_id).await.ok().flatten(), + track_cover_file(&pool, track_id).await.ok().flatten(), + ) + .await, + read_image( + &storage_dir, + track_artist_image_file(&pool, track_id) + .await + .ok() + .flatten(), ) .await, ) @@ -511,7 +528,10 @@ async fn serve_catalog_one( artist: None, }, }; - stream.send.write_all(&serde_json::to_vec(&response)?).await?; + stream + .send + .write_all(&serde_json::to_vec(&response)?) + .await?; } Some(want @ ("artist_image" | "release_cover")) => { let media = if want == "release_cover" { @@ -549,7 +569,10 @@ async fn serve_catalog_one( error: Some(format!("unknown request kind '{other}'")), artist: None, }; - stream.send.write_all(&serde_json::to_vec(&response)?).await?; + stream + .send + .write_all(&serde_json::to_vec(&response)?) + .await?; } } stream.send.finish()?; diff --git a/src/main.rs b/src/main.rs index ad5da1e..a3ed554 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod agent; mod api; mod auth; mod config; +mod federation; mod i18n; mod jobs; mod lastfm; @@ -12,7 +13,6 @@ mod music; mod oidc; mod player; mod scheduler; -mod federation; mod torrents; mod user;