use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use sqlite::OpenFlags; use crate::artifacts::{Album, Artifact, ArtifactBuilder, Artist, SourceID, Track, tools::{DataSource, ToolDescription}}; #[derive(Debug)] #[allow(unused)] pub enum MixxxError { Sql(sqlite::Error) } impl From for MixxxError { fn from(value: sqlite::Error) -> Self { Self::Sql(value) } } pub struct MixxxDB; #[derive(Serialize, Deserialize, Debug, Default, JsonSchema)] pub struct MixxxQuery { pub playlist_name: String } impl DataSource for MixxxDB { type Args = MixxxQuery; type Error = MixxxError; async fn synchronize(&self, _artifact: &mut Artifact) -> Result, Self::Error> { Ok(vec![]) } async fn query(&self, args: &Self::Args) -> Result, Self::Error> { let mut ret = vec![]; let playlist_name = args.playlist_name.as_str(); log::info!("Loading Mixxx playlist {}", playlist_name); let connection = sqlite::Connection::open_thread_safe_with_flags("mixxxdb.sqlite", OpenFlags::new().with_read_only())?; let query = "SELECT id FROM Playlists WHERE name = ? ORDER BY id DESC LIMIT 1"; let mut statement = connection.prepare(query)?; statement.bind((1, playlist_name))?; statement.next()?; let latest_id = statement.read::("id")?; let query = "SELECT title, artist, album, comment, url, bpm FROM library LEFT JOIN PlaylistTracks ON PlaylistTracks.track_id = library.id WHERE PlaylistTracks.playlist_id = ? ORDER BY position"; for track in connection.prepare(query)?.into_iter().bind((1, latest_id))? { let track = track?; let title = track.try_read::<&str, _>("title").unwrap_or("Untitled Track"); let artist = track.try_read::<&str, _>("artist").unwrap_or("Unknown Artist"); let album = track.try_read::<&str, _>("album").unwrap_or("Unknown Album"); let bpm = track.try_read::("bpm").unwrap_or(0.); ret.push(ArtifactBuilder::new(SourceID::Mixxx) .contents(Track { artist: Some(artist.into()), album: Some(album.into()), title: title.into(), bpm: Some(bpm), ..Default::default() }).build()); ret.push(ArtifactBuilder::new(SourceID::Mixxx) .contents(Album { artist: artist.into(), title: album.into(), ..Default::default() }).build()); ret.push(ArtifactBuilder::new(SourceID::Mixxx) .contents(Artist { name: artist.into(), ..Default::default() }).build()); } Ok(ret) } } impl ToolDescription for MixxxDB { fn description(&self) -> &str { "Loads artifacts from a given Mixxx playlist name" } fn name(&self) -> &str { "query_mixxx" } }