use std::str::FromStr;
use color_eyre::eyre::Result;
use sqlx::{SqlitePool, sqlite::SqliteConnectOptions};
use tano_providers::local::parse_song::ParsedSong;
use tano_shared::get_data_dir::get_data_dir;
use tokio::{fs, sync::mpsc};
use crate::{
actor::cmd::DatabaseCmd, album::Album, artist::Artist, db, local_song::SyncLocalSong,
song::Song,
};
pub mod cmd;
pub mod handle;
pub struct DatabaseActor {
receiver: mpsc::Receiver<DatabaseCmd>,
pool: Option<SqlitePool>,
}
impl DatabaseActor {
pub fn new(receiver: mpsc::Receiver<DatabaseCmd>) -> Self {
Self {
receiver,
pool: None,
}
}
async fn handle_command(&mut self, cmd: DatabaseCmd) {
match cmd {
DatabaseCmd::LoadDatabase { respond_to } => {
let _ = respond_to.send(self.load_database().await);
}
DatabaseCmd::GetSongs { respond_to } => {
let _ = respond_to.send(self.get_songs().await);
}
DatabaseCmd::GetAlbums { respond_to } => {
let _ = respond_to.send(self.get_albums().await);
}
DatabaseCmd::GetAlbumArtists {
album_id,
respond_to,
} => {
let _ = respond_to.send(self.get_album_artists(album_id).await);
}
DatabaseCmd::GetAlbum { id, respond_to } => {
let _ = respond_to.send(self.get_album(id).await);
}
DatabaseCmd::GetAlbumSongs {
album_id,
respond_to,
} => {
let _ = respond_to.send(self.get_album_songs(album_id).await);
}
DatabaseCmd::GetArtists { respond_to } => {
let _ = respond_to.send(self.get_artists().await);
}
DatabaseCmd::GetSongIds { respond_to } => {
let _ = respond_to.send(self.get_song_ids().await);
}
DatabaseCmd::GetSyncLocalSong {
respond_to,
provider_id,
} => {
let _ = respond_to.send(self.get_sync_local_songs(provider_id).await);
}
DatabaseCmd::SyncLocalSongs {
provider_id,
new_songs,
updated_songs,
to_update_path,
to_delete_ids,
respond_to,
} => {
let _ = respond_to.send(
self.sync_local_songs(
provider_id,
new_songs,
updated_songs,
to_update_path,
to_delete_ids,
)
.await,
);
}
DatabaseCmd::GetLocalSongByPath {
provider_id,
path,
respond_to,
} => {
let _ = respond_to.send(
db::get_local_song_by_path(self.pool.as_ref().unwrap(), provider_id, &path)
.await,
);
}
DatabaseCmd::GetLocalSongByInode {
provider_id,
inode,
respond_to,
} => {
let _ = respond_to.send(
db::get_local_song_by_inode(self.pool.as_ref().unwrap(), provider_id, inode)
.await,
);
}
DatabaseCmd::UpdateLocalSongPath {
id,
path,
respond_to,
} => {
let _ = respond_to
.send(db::update_local_song_path(self.pool.as_ref().unwrap(), id, &path).await);
}
DatabaseCmd::InsertLocalSong {
provider_id,
parsed_song,
respond_to,
} => {
let _ = respond_to.send(
db::insert_parsed_song(self.pool.as_ref().unwrap(), provider_id, &parsed_song)
.await,
);
}
DatabaseCmd::UpdateLocalSong {
provider_id,
id,
parsed_song,
respond_to,
} => {
let _ = respond_to.send(
db::update_parsed_song(
self.pool.as_ref().unwrap(),
provider_id,
id,
&parsed_song,
)
.await,
);
}
DatabaseCmd::DeleteLocalSong { id, respond_to } => {
let _ =
respond_to.send(db::delete_parsed_song(self.pool.as_ref().unwrap(), id).await);
}
}
}
async fn get_songs(&self) -> Result<Vec<Song>> {
db::get_songs(self.pool.as_ref().unwrap()).await
}
async fn get_albums(&self) -> Result<Vec<Album>> {
db::get_albums(self.pool.as_ref().unwrap()).await
}
async fn get_album(&self, id: i64) -> Result<Option<Album>> {
db::get_album(self.pool.as_ref().unwrap(), id).await
}
async fn get_album_artists(&self, album_id: i64) -> Result<Vec<Artist>> {
db::get_album_artists(self.pool.as_ref().unwrap(), album_id).await
}
async fn get_album_songs(&self, album_id: i64) -> Result<Vec<Song>> {
db::get_album_songs(self.pool.as_ref().unwrap(), album_id).await
}
async fn get_artists(&self) -> Result<Vec<Artist>> {
db::get_artists(self.pool.as_ref().unwrap()).await
}
async fn get_song_ids(&self) -> Result<Vec<i64>> {
db::get_song_ids(self.pool.as_ref().unwrap()).await
}
async fn get_sync_local_songs(&self, provider_id: u64) -> Result<Vec<SyncLocalSong>> {
db::get_sync_local_songs(self.pool.as_ref().unwrap(), provider_id).await
}
async fn sync_local_songs(
&self,
provider_id: u64,
new_songs: Vec<ParsedSong>,
updated_songs: Vec<(i64, ParsedSong)>,
to_update_path: Vec<(i64, String)>,
to_delete_ids: Vec<i64>,
) -> Result<()> {
db::sync_local_songs(
self.pool.as_ref().unwrap(),
provider_id,
new_songs,
updated_songs,
to_update_path,
to_delete_ids,
)
.await
}
async fn load_database(&mut self) -> Result<()> {
let data_dir = get_data_dir()?;
fs::create_dir_all(&data_dir).await?;
let database_path = &data_dir.join("database.db").to_string_lossy().to_string();
let connection_options =
SqliteConnectOptions::from_str(database_path)?.create_if_missing(true);
let pool = SqlitePool::connect_with(connection_options).await?;
sqlx::migrate!().run(&pool).await?;
self.pool = Some(pool);
Ok(())
}
}
pub async fn run_database_actor(mut actor: DatabaseActor) {
while let Some(cmd) = actor.receiver.recv().await {
actor.handle_command(cmd).await;
}
}