From e7d4c10ad667e1e6a8e80fda673892ad4dca9b0d Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Tue, 21 Apr 2026 10:27:14 -0500 Subject: [PATCH] [feat]: Song importing works now This means we have a decent flow for creating and importing songs. By default this will use Genius as the lyric backend. In the future we will support more options, but for now this means you can get the lyrics you need and start building songs rather fast. --- src/core/song_search.rs | 18 ++++++++++++++++ src/core/songs.rs | 48 +++++++++++++++++++++++++++++++++++++++++ src/main.rs | 11 ++++++++++ src/ui/library.rs | 22 ++++++++++++++++++- src/ui/song_editor.rs | 8 +++++-- 5 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/core/song_search.rs b/src/core/song_search.rs index d3a9b5f..a2a1b4f 100644 --- a/src/core/song_search.rs +++ b/src/core/song_search.rs @@ -81,11 +81,29 @@ impl From for Song { .or_insert(online_song.lyrics); Some(map) }; + let lyrics = ron::ser::to_string(&map).ok(); + + let verse_order: Option> = + if let Some(map) = map.as_ref() { + Some(map.keys().map(|v| v.get_name()).collect()) + } else { + None + }; + + let verses: Option> = + if let Some(map) = map.as_ref() { + Some(map.keys().map(|v| v.to_owned()).collect()) + } else { + None + }; Self { title: online_song.title, author: Some(online_song.author), verse_map: map, + lyrics, + verse_order, + verses, ..Default::default() } } diff --git a/src/core/songs.rs b/src/core/songs.rs index 647c52d..76e6d28 100644 --- a/src/core/songs.rs +++ b/src/core/songs.rs @@ -788,6 +788,54 @@ pub async fn remove_song( } } +pub async fn insert_song( + mut song: Song, + mut songs: Vec, + db: Arc, +) -> Result> { + let verse_order = { + song.verse_order.clone().map_or_else(String::new, |vo| { + vo.into_iter() + .map(|mut s| { + s.push(' '); + s + }) + .collect::() + }) + }; + + let audio = song + .audio + .clone() + .map(|a| a.to_str().unwrap_or_default().to_string()); + + let background = song + .background + .clone() + .map(|b| b.path.to_str().unwrap_or_default().to_string()); + + let res = query!( + r#"INSERT INTO songs (title, lyrics, author, ccli, verse_order, audio, font, font_size, background) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)"#, + song.title, + song.lyrics, + song.author, + song.ccli, + verse_order, + audio, + song.font, + song.font_size, + background + ) + .execute(&*db) + .await + .into_diagnostic()?; + song.id = i32::try_from(res.last_insert_rowid()).expect( + "Fairly confident that this number won't get that high", + ); + songs.push(song); + Ok(songs) +} + pub async fn add_song( mut songs: Vec, db: Arc, diff --git a/src/main.rs b/src/main.rs index babf0d3..ce32a53 100755 --- a/src/main.rs +++ b/src/main.rs @@ -958,6 +958,17 @@ impl cosmic::Application for App { Task::none() } } + song_editor::Action::AddSong(song) => { + if self.library.is_some() { + self.update(Message::Library( + library::Message::AddSongFromEditor( + song, + ), + )) + } else { + Task::none() + } + } song_editor::Action::None => Task::none(), } } diff --git a/src/ui/library.rs b/src/ui/library.rs index 21674f6..6dc9da0 100644 --- a/src/ui/library.rs +++ b/src/ui/library.rs @@ -29,7 +29,7 @@ use crate::core::kinds::ServiceItemKind; use crate::core::model::{KindWrapper, LibraryKind, Model}; use crate::core::presentations::{self, Presentation}; use crate::core::service_items::ServiceItem; -use crate::core::songs::{self, Song}; +use crate::core::songs::{self, Song, insert_song}; use crate::core::videos::{self, Video}; #[allow(clippy::struct_field_names)] @@ -110,6 +110,7 @@ pub enum Message { ReaddVideos(Vec