artifacts: move mixxx api bits into artifacts module

This commit is contained in:
2026-06-09 20:29:52 +02:00
parent 96ec57b2d9
commit 44afe5a713
2 changed files with 54 additions and 43 deletions
+51
View File
@@ -1,6 +1,7 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlite::OpenFlags;
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -48,4 +49,54 @@ impl Into<Artifact> for bandcamp::Artist {
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)] #[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
pub struct BandcampQueryArgs { pub struct BandcampQueryArgs {
pub query: String pub query: String
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct MixxxTrack {
pub artist: String,
pub album: String,
pub title: String,
pub bpm: f64
}
#[derive(Debug)]
pub enum MixxxError {
Sql(sqlite::Error)
}
impl From<sqlite::Error> for MixxxError {
fn from(value: sqlite::Error) -> Self {
Self::Sql(value)
}
}
pub struct MixxxDB(());
impl MixxxDB {
pub fn load(episode_number: u32) -> Result<Vec<MixxxTrack>, MixxxError> {
let mut ret = vec![];
let playlist_name = format!("BFF.fm - Episode {}", episode_number);
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.as_str()))?;
statement.next()?;
let latest_id = statement.read::<i64, _>("id").unwrap();
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).unwrap().into_iter().bind((1, latest_id)).unwrap().map(|row| row.unwrap()) {
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::<f64, _>("bpm").unwrap_or(0.);
ret.push(MixxxTrack {
artist: artist.into(),
album: album.into(),
title: title.into(),
bpm
});
}
Ok(ret)
}
} }
+3 -43
View File
@@ -2,7 +2,7 @@ use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlite::OpenFlags; use sqlite::OpenFlags;
use crate::{artifacts::Artifact, prediction::{GeneratedResponses, PossibleResponse}, scene::conversation::ConversationEntry}; use crate::{artifacts::{Artifact, MixxxDB, MixxxError, MixxxTrack}, prediction::{GeneratedResponses, PossibleResponse}, scene::conversation::ConversationEntry};
pub mod conversation; pub mod conversation;
@@ -12,7 +12,7 @@ pub struct StageDirection {
#[serde(skip)] #[serde(skip)]
pub end_time: DateTime<Utc>, pub end_time: DateTime<Utc>,
pub narrative: String, pub narrative: String,
pub current_playlist: Vec<PlaylistEntry> pub current_playlist: Vec<MixxxTrack>
} }
impl StageDirection { impl StageDirection {
@@ -37,41 +37,9 @@ pub struct Scenery {
pub artifacts: Vec<Artifact> pub artifacts: Vec<Artifact>
} }
#[derive(Debug)]
pub enum MixxxError {
Sql(sqlite::Error)
}
impl From<sqlite::Error> for MixxxError {
fn from(value: sqlite::Error) -> Self {
Self::Sql(value)
}
}
impl StageDirection { impl StageDirection {
pub fn reload_mixxx_playlist(&mut self) -> Result<(), MixxxError> { pub fn reload_mixxx_playlist(&mut self) -> Result<(), MixxxError> {
self.current_playlist.clear(); self.current_playlist = MixxxDB::load(self.episode_number)?;
let playlist_name = format!("BFF.fm - Episode {}", self.episode_number);
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.as_str()))?;
statement.next()?;
let latest_id = statement.read::<i64, _>("id").unwrap();
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).unwrap().into_iter().bind((1, latest_id)).unwrap().map(|row| row.unwrap()) {
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::<f64, _>("bpm").unwrap_or(0.);
self.current_playlist.push(PlaylistEntry {
artist: artist.into(),
album: album.into(),
title: title.into(),
bpm
});
}
Ok(()) Ok(())
} }
@@ -86,14 +54,6 @@ pub enum PredictionAction {
SetShowEndTime(DateTime<Utc>) SetShowEndTime(DateTime<Utc>)
} }
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct PlaylistEntry {
pub artist: String,
pub album: String,
pub title: String,
pub bpm: f64
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)] #[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Scene { pub struct Scene {
reply_options: GeneratedResponses, reply_options: GeneratedResponses,