Added Controller State and Playlist loading on startup

This commit is contained in:
MrDulfin 2024-12-25 18:26:29 -05:00
parent 10223b23e2
commit 4f2d5ab64a
8 changed files with 220 additions and 55 deletions

View file

@ -1,9 +1,7 @@
pub mod other_settings;
use std::{ use std::{
fs::{self, File, OpenOptions}, fs::{self, File, OpenOptions},
io::{Error, Read, Write}, io::{Error, Read, Write},
path::PathBuf, path::{Path, PathBuf},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -100,8 +98,8 @@ pub struct Config {
pub path: PathBuf, pub path: PathBuf,
pub backup_folder: Option<PathBuf>, pub backup_folder: Option<PathBuf>,
pub libraries: ConfigLibraries, pub libraries: ConfigLibraries,
pub volume: f32,
pub connections: ConfigConnections, pub connections: ConfigConnections,
pub state_path: PathBuf,
} }
impl Config { impl Config {
@ -160,9 +158,9 @@ impl Config {
pub fn read_file(path: PathBuf) -> Result<Self, Error> { pub fn read_file(path: PathBuf) -> Result<Self, Error> {
let mut file: File = File::open(path)?; let mut file: File = File::open(path)?;
let mut bun: String = String::new(); let mut buf: String = String::new();
_ = file.read_to_string(&mut bun); _ = file.read_to_string(&mut buf);
let config: Config = serde_json::from_str::<Config>(&bun)?; let config: Config = serde_json::from_str::<Config>(&buf)?;
Ok(config) Ok(config)
} }

View file

@ -1,7 +0,0 @@
pub enum Setting {
String { name: String, value: String },
Int { name: String, value: i32 },
Bool { name: String, value: bool },
}
pub struct Form {}

View file

@ -6,15 +6,19 @@
use itertools::Itertools; use itertools::Itertools;
use kushi::{Queue, QueueItemType}; use kushi::{Queue, QueueItemType};
use kushi::{QueueError, QueueItem}; use kushi::{QueueError, QueueItem};
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::to_string_pretty;
use std::error::Error; use std::error::Error;
use std::fs::OpenOptions;
use std::io::Write;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use thiserror::Error; use thiserror::Error;
use uuid::Uuid; use uuid::Uuid;
use crate::config::ConfigError; use crate::config::{self, ConfigError};
use crate::music_player::player::{Player, PlayerError}; use crate::music_player::player::{Player, PlayerError};
use crate::music_storage::library::Song; use crate::music_storage::library::Song;
use crate::music_storage::playlist::{ExternalPlaylist, Playlist, PlaylistFolderItem}; use crate::music_storage::playlist::{ExternalPlaylist, Playlist, PlaylistFolderItem};
@ -75,7 +79,7 @@ pub enum PlayerCommand {
Pause, Pause,
Play, Play,
Enqueue(usize), Enqueue(usize),
SetVolume(f64), SetVolume(f32),
PlayNow(Uuid, PlayerLocation), PlayNow(Uuid, PlayerLocation),
} }
@ -90,8 +94,11 @@ pub enum LibraryCommand {
Song(Uuid), Song(Uuid),
AllSongs, AllSongs,
GetLibrary, GetLibrary,
ExternalPlaylist(Uuid),
Playlist(Uuid), Playlist(Uuid),
ImportM3UPlayList(PathBuf) ImportM3UPlayList(PathBuf),
Save,
Playlists,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -100,8 +107,10 @@ pub enum LibraryResponse {
Song(Song, usize), Song(Song, usize),
AllSongs(Vec<Song>), AllSongs(Vec<Song>),
Library(MusicLibrary), Library(MusicLibrary),
Playlist(ExternalPlaylist), ExternalPlaylist(ExternalPlaylist),
ImportM3UPlayList(Uuid, String) Playlist(Playlist),
ImportM3UPlayList(Uuid, String),
Playlists(Vec<(Uuid, String)>),
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -170,6 +179,39 @@ impl ControllerHandle {
} }
} }
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct ControllerState {
path: PathBuf,
volume: f32,
now_playing: Uuid,
}
impl ControllerState {
fn new(path: PathBuf) -> Self {
ControllerState {
path,
volume: 0.35,
..Default::default()
}
}
fn write_file(&self) -> Result<(), std::io::Error> {
OpenOptions::new()
.truncate(true)
.create(true)
.write(true)
.open(&self.path)
.unwrap()
.write_all(&to_string_pretty(self)?.into_bytes())?;
Ok(())
}
fn read_file(path: impl AsRef<Path>) -> Result<Self, std::io::Error> {
let state = serde_json::from_str(&std::fs::read_to_string(path)?)?;
Ok(state)
}
}
#[allow(unused_variables)] #[allow(unused_variables)]
impl<'c, P: Player + Send + Sync> Controller<'c, P> { impl<'c, P: Player + Send + Sync> Controller<'c, P> {
pub async fn start( pub async fn start(
@ -184,23 +226,22 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
where where
P: Player, P: Player,
{ {
//TODO: make a separate event loop for sccessing library that clones borrowed values from inner library loop? let queue: Queue<QueueSong, QueueAlbum> = Queue {
let mut queue: Queue<QueueSong, QueueAlbum> = Queue {
items: Vec::new(), items: Vec::new(),
played: Vec::new(), played: Vec::new(),
loop_: false, loop_: false,
shuffle: None, shuffle: None,
}; };
// for testing porpuses
// for song in &library.library { let state = {
// queue.add_item( let path = &config.read().unwrap().state_path;
// QueueSong { if let Ok(state) = ControllerState::read_file(path) {
// song: song.clone(), state
// location: PlayerLocation::Test, } else {
// }, ControllerState::new(path.clone())
// true, }
// ); };
// }
let queue = queue; let queue = queue;
std::thread::scope(|scope| { std::thread::scope(|scope| {
@ -220,19 +261,27 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
player_mail.1, player_mail.1,
queue_mail.0, queue_mail.0,
_lib_mail, _lib_mail,
state,
) )
.await .await
.unwrap(); .unwrap();
}); });
scope scope
.spawn(async move { .spawn(async move {
Controller::<P>::player_event_loop(player, player_mail.0) Controller::<P>::player_event_loop(
player,
player_mail.0
)
.await .await
.unwrap(); .unwrap();
}); });
scope scope
.spawn(async { .spawn(async {
Controller::<P>::library_loop(lib_mail.1, &mut library) Controller::<P>::library_loop(
lib_mail.1,
&mut library,
config,
)
.await .await
.unwrap(); .unwrap();
}); });
@ -258,8 +307,14 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
player_mail: MailMan<PlayerResponse, PlayerCommand>, player_mail: MailMan<PlayerResponse, PlayerCommand>,
queue_mail: MailMan<QueueCommand, QueueResponse>, queue_mail: MailMan<QueueCommand, QueueResponse>,
lib_mail: MailMan<LibraryCommand, LibraryResponse>, lib_mail: MailMan<LibraryCommand, LibraryResponse>,
mut state: ControllerState,
) -> Result<(), ()> { ) -> Result<(), ()> {
let mut first = true; let mut first = true;
{
let volume = state.volume as f64;
player.write().unwrap().set_volume(volume);
println!("volume set to {volume}");
}
while true { while true {
let _mail = player_mail.recv().await; let _mail = player_mail.recv().await;
if let Ok(mail) = _mail { if let Ok(mail) = _mail {
@ -282,9 +337,12 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
player_mail.send(PlayerResponse::Empty).await.unwrap(); player_mail.send(PlayerResponse::Empty).await.unwrap();
} }
PlayerCommand::SetVolume(volume) => { PlayerCommand::SetVolume(volume) => {
player.write().unwrap().set_volume(volume); player.write().unwrap().set_volume(volume as f64);
println!("volume set to {volume}"); println!("volume set to {volume}");
player_mail.send(PlayerResponse::Empty).await.unwrap(); player_mail.send(PlayerResponse::Empty).await.unwrap();
state.volume = volume;
_ = state.write_file()
} }
PlayerCommand::NextSong => { PlayerCommand::NextSong => {
queue_mail.send(QueueCommand::Next).await.unwrap(); queue_mail.send(QueueCommand::Next).await.unwrap();
@ -384,11 +442,26 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
// ... // ...
// let's just pretend I figured that out already // let's just pretend I figured that out already
lib_mail.send(LibraryCommand::AllSongs).await.unwrap(); let (songs, index) = match location {
let LibraryResponse::AllSongs(songs) = lib_mail.recv().await.unwrap() else { PlayerLocation::Library => {
unreachable!() lib_mail.send(LibraryCommand::AllSongs).await.unwrap();
let LibraryResponse::AllSongs(songs) = lib_mail.recv().await.unwrap() else {
unreachable!()
};
(songs, index)
}
PlayerLocation::Playlist(uuid) => {
lib_mail.send(LibraryCommand::ExternalPlaylist(uuid)).await.unwrap();
let LibraryResponse::ExternalPlaylist(list) = lib_mail.recv().await.unwrap() else {
unreachable!()
};
let index = list.get_index(song.uuid).unwrap();
(list.tracks, index)
}
_ => todo!("Got Location other than Library or Playlist")
}; };
for i in index+1..(index+50) { for i in index+1..(index+50) {
if let Some(song) = songs.get(i) { if let Some(song) = songs.get(i) {
queue_mail.send(QueueCommand::Append(QueueItem::from_item_type(QueueItemType::Single(QueueSong { song: song.clone(), location })), false)).await.unwrap(); queue_mail.send(QueueCommand::Append(QueueItem::from_item_type(QueueItemType::Single(QueueSong { song: song.clone(), location })), false)).await.unwrap();
@ -396,7 +469,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
unreachable!() unreachable!()
}; };
} else { } else {
println!("End of Library"); println!("End of Library / Playlist");
break; break;
} }
} }
@ -414,6 +487,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
async fn library_loop( async fn library_loop(
lib_mail: MailMan<LibraryResponse, LibraryCommand>, lib_mail: MailMan<LibraryResponse, LibraryCommand>,
library: &'c mut MusicLibrary, library: &'c mut MusicLibrary,
config: Arc<RwLock<Config>>,
) -> Result<(), ()> { ) -> Result<(), ()> {
while true { while true {
match lib_mail.recv().await.unwrap() { match lib_mail.recv().await.unwrap() {
@ -424,9 +498,9 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
LibraryCommand::AllSongs => { LibraryCommand::AllSongs => {
lib_mail.send(LibraryResponse::AllSongs(library.library.clone())).await.unwrap(); lib_mail.send(LibraryResponse::AllSongs(library.library.clone())).await.unwrap();
}, },
LibraryCommand::Playlist(uuid) => { LibraryCommand::ExternalPlaylist(uuid) => {
let playlist = library.query_playlist_uuid(&uuid).unwrap(); let playlist = library.query_playlist_uuid(&uuid).unwrap();
lib_mail.send(LibraryResponse::Playlist(ExternalPlaylist::from_playlist(playlist, &library))).await.unwrap(); lib_mail.send(LibraryResponse::ExternalPlaylist(ExternalPlaylist::from_playlist(playlist, &library))).await.unwrap();
} }
LibraryCommand::ImportM3UPlayList(path) => { LibraryCommand::ImportM3UPlayList(path) => {
let playlist = Playlist::from_m3u(path, library).unwrap(); let playlist = Playlist::from_m3u(path, library).unwrap();
@ -436,6 +510,16 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
lib_mail.send(LibraryResponse::ImportM3UPlayList(uuid, name)).await.unwrap(); lib_mail.send(LibraryResponse::ImportM3UPlayList(uuid, name)).await.unwrap();
} }
LibraryCommand::Save => {
library.save({config.read().unwrap().libraries.get_library(&library.uuid).unwrap().path.clone()}).unwrap();
lib_mail.send(LibraryResponse::Ok).await.unwrap();
}
LibraryCommand::Playlists => {
let mut lists = vec![];
library.playlists.lists_recursive().into_par_iter().map(|list| (list.uuid, list.title.clone())).collect_into_vec(&mut lists);
lib_mail.send(LibraryResponse::Playlists(lists)).await.unwrap();
}
_ => { todo!() } _ => { todo!() }
} }
} }

View file

@ -52,6 +52,17 @@ impl PlaylistFolder {
} }
None None
} }
pub fn lists_recursive(&self) -> Vec<&Playlist> {
let mut vec = vec![];
for item in &self.items {
match item {
PlaylistFolderItem::List(ref playlist) => vec.push(playlist),
PlaylistFolderItem::Folder(folder) => vec.append(&mut folder.lists_recursive()),
}
}
vec
}
} }
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
@ -107,13 +118,13 @@ impl Playlist {
} }
pub fn get_index(&self, uuid: Uuid) -> Option<usize> { pub fn get_index(&self, uuid: Uuid) -> Option<usize> {
let mut i = 0; let mut i = 0;
if self.contains(uuid) { if self.tracks.contains(&uuid) {
for track in &self.tracks { for track in &self.tracks {
i += 1;
if &uuid == track { if &uuid == track {
dbg!("Index gotted! ", i); dbg!("Index gotted! ", i);
return Some(i); return Some(i);
} }
i += 1;
} }
} }
None None
@ -351,7 +362,9 @@ pub struct ExternalPlaylist {
impl ExternalPlaylist { impl ExternalPlaylist {
pub(crate) fn from_playlist(playlist: &Playlist, library: &MusicLibrary) -> Self { pub(crate) fn from_playlist(playlist: &Playlist, library: &MusicLibrary) -> Self {
let tracks: Vec<Song> = playlist.tracks.iter().filter_map(|uuid| { let tracks: Vec<Song> = playlist.tracks.iter().filter_map(|uuid| {
library.query_uuid(uuid).map(|res| res.0.clone()) library.query_uuid(uuid).map(|res| {
res.0.clone()
})
}).collect_vec(); }).collect_vec();
Self { Self {
@ -363,6 +376,27 @@ impl ExternalPlaylist {
play_time: playlist.play_time play_time: playlist.play_time
} }
} }
pub fn get_index(&self, uuid: Uuid) -> Option<usize> {
let mut i = 0;
if self.contains(uuid) {
for track in &self.tracks {
if &uuid == &track.uuid {
return Some(i);
}
i += 1;
}
}
None
}
pub fn contains(&self, uuid: Uuid) -> bool {
for track in &self.tracks {
if track.uuid == uuid {
return true;
}
}
false
}
} }

View file

@ -9,6 +9,7 @@ use crate::wrappers::_Song;
#[tauri::command] #[tauri::command]
pub async fn add_song_to_queue(app: AppHandle<Wry>, ctrl_handle: State<'_, ControllerHandle>, uuid: Uuid, location: PlayerLocation) -> Result<(), String> { pub async fn add_song_to_queue(app: AppHandle<Wry>, ctrl_handle: State<'_, ControllerHandle>, uuid: Uuid, location: PlayerLocation) -> Result<(), String> {
dbg!(&location);
ctrl_handle.lib_mail.send(dmp_core::music_controller::controller::LibraryCommand::Song(uuid)).await.unwrap(); ctrl_handle.lib_mail.send(dmp_core::music_controller::controller::LibraryCommand::Song(uuid)).await.unwrap();
let LibraryResponse::Song(song, _) = ctrl_handle.lib_mail.recv().await.unwrap() else { let LibraryResponse::Song(song, _) = ctrl_handle.lib_mail.recv().await.unwrap() else {
unreachable!() unreachable!()

View file

@ -6,7 +6,7 @@ use dmp_core::{config::{Config, ConfigLibrary}, music_controller::controller::{C
use tauri::{http::Response, Emitter, Manager, State, Url, WebviewWindowBuilder, Wry}; use tauri::{http::Response, Emitter, Manager, State, Url, WebviewWindowBuilder, Wry};
use uuid::Uuid; use uuid::Uuid;
use crate::wrappers::{get_library, play, pause, prev, set_volume, get_song, next, get_queue, import_playlist, get_playlist}; use crate::wrappers::{get_library, play, pause, prev, set_volume, get_song, next, get_queue, import_playlist, get_playlist, get_playlists};
pub mod wrappers; pub mod wrappers;
pub mod commands; pub mod commands;
@ -76,6 +76,7 @@ pub fn run() {
play_now, play_now,
import_playlist, import_playlist,
get_playlist, get_playlist,
get_playlists
]).manage(ConfigRx(rx)) ]).manage(ConfigRx(rx))
.manage(LibRx(lib_rx)) .manage(LibRx(lib_rx))
.manage(HandleTx(handle_tx)) .manage(HandleTx(handle_tx))
@ -142,11 +143,15 @@ async fn get_config(state: State<'_, ConfigRx>) -> Result<Config, String> {
// dbg!(&dir); // dbg!(&dir);
let config = if let Ok(c) = Config::read_file(PathBuf::from(path).join("config")) { let config = if let Ok(mut c) = Config::read_file(PathBuf::from(path).join("config")) {
if c.state_path == PathBuf::default() {
c.state_path = PathBuf::from(path).join("state");
}
c c
} else { } else {
let c = Config { let c = Config {
path: PathBuf::from(path).join("config"), path: PathBuf::from(path).join("config"),
state_path: PathBuf::from(path).join("state"),
..Default::default() ..Default::default()
}; };
c.write_file().unwrap(); c.write_file().unwrap();

View file

@ -39,7 +39,7 @@ pub async fn pause(app: AppHandle<Wry>, ctrl_handle: State<'_, ControllerHandle>
#[tauri::command] #[tauri::command]
pub async fn set_volume(ctrl_handle: State<'_, ControllerHandle>, volume: String) -> Result<(), String> { pub async fn set_volume(ctrl_handle: State<'_, ControllerHandle>, volume: String) -> Result<(), String> {
let volume = volume.parse::<f64>().unwrap() / 1000.0; let volume = volume.parse::<f32>().unwrap() / 1000.0;
ctrl_handle.player_mail.send(dmp_core::music_controller::controller::PlayerCommand::SetVolume(volume)).await.unwrap(); ctrl_handle.player_mail.send(dmp_core::music_controller::controller::PlayerCommand::SetVolume(volume)).await.unwrap();
let PlayerResponse::Empty = ctrl_handle.player_mail.recv().await.unwrap() else { let PlayerResponse::Empty = ctrl_handle.player_mail.recv().await.unwrap() else {
unreachable!() unreachable!()
@ -142,14 +142,25 @@ pub async fn get_library(ctrl_handle: State<'_, ControllerHandle>) -> Result<Vec
#[tauri::command] #[tauri::command]
pub async fn get_playlist(ctrl_handle: State<'_, ControllerHandle>, uuid: Uuid) -> Result<Vec<_Song>, String> { pub async fn get_playlist(ctrl_handle: State<'_, ControllerHandle>, uuid: Uuid) -> Result<Vec<_Song>, String> {
ctrl_handle.lib_mail.send(LibraryCommand::Playlist(uuid)).await.unwrap(); ctrl_handle.lib_mail.send(LibraryCommand::ExternalPlaylist(uuid)).await.unwrap();
let LibraryResponse::Playlist(playlist) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; let LibraryResponse::ExternalPlaylist(playlist) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") };
let songs = playlist.tracks.iter().map(|song| _Song::from(song)).collect::<Vec<_>>(); let songs = playlist.tracks.iter().map(|song| _Song::from(song)).collect::<Vec<_>>();
println!("Got Playlist {}, len {}", playlist.title, playlist.tracks.len()); println!("Got Playlist {}, len {}", playlist.title, playlist.tracks.len());
Ok(songs) Ok(songs)
} }
#[tauri::command]
pub async fn get_playlists(app: AppHandle<Wry>, ctrl_handle: State<'_, ControllerHandle>) -> Result<(), String> {
println!("getting Playlists");
ctrl_handle.lib_mail.send(LibraryCommand::Playlists).await.unwrap();
let LibraryResponse::Playlists(lists) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!() };
println!("gotten playlists");
app.emit("playlists_gotten", lists.into_iter().map(|(uuid, name)| PlaylistPayload { uuid, name }).collect_vec()).unwrap();
Ok(())
}
#[tauri::command] #[tauri::command]
pub async fn import_playlist(ctrl_handle: State<'_, ControllerHandle>) -> Result<PlaylistPayload, String> { pub async fn import_playlist(ctrl_handle: State<'_, ControllerHandle>) -> Result<PlaylistPayload, String> {
let file = rfd::AsyncFileDialog::new() let file = rfd::AsyncFileDialog::new()
@ -161,11 +172,13 @@ pub async fn import_playlist(ctrl_handle: State<'_, ControllerHandle>) -> Result
ctrl_handle.lib_mail.send(LibraryCommand::ImportM3UPlayList(PathBuf::from(file.path()))).await.unwrap(); ctrl_handle.lib_mail.send(LibraryCommand::ImportM3UPlayList(PathBuf::from(file.path()))).await.unwrap();
let LibraryResponse::ImportM3UPlayList(uuid, name) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; let LibraryResponse::ImportM3UPlayList(uuid, name) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") };
ctrl_handle.lib_mail.send(LibraryCommand::Save).await.unwrap();
let LibraryResponse::Ok = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!() };
println!("Imported Playlist {name}"); println!("Imported Playlist {name}");
Ok(PlaylistPayload {uuid, name}) Ok(PlaylistPayload {uuid, name})
} }
#[derive(Serialize)] #[derive(Serialize, Clone)]
pub struct PlaylistPayload { pub struct PlaylistPayload {
uuid: Uuid, uuid: Uuid,
name: String name: String

View file

@ -98,7 +98,40 @@ interface PlaylistHeadProps {
function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: PlaylistHeadProps) { function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: PlaylistHeadProps) {
let handle_import = () => { useEffect(() => {
const unlisten = appWindow.listen<any[]>("playlists_gotten", (_res) => {
// console.log(event);
let res = _res.payload;
setPlaylists([
...res.map( (item) => {
return (
<button onClick={ () => {
invoke('get_playlist', { uuid: item.uuid }).then((list) => {
setLibrary([...(list as any[]).map((song) => {
// console.log(song);
return (
<Song
key={ song.uuid }
location={ song.location }
playerLocation={ {"Playlist" : item.uuid } }
uuid={ song.uuid }
plays={ song.plays }
duration={ song.duration }
tags={ song.tags }
/>
)
})])
})
setViewName( item.name )
} } key={ 'playlist_' + item.uuid }>{ item.name }</button>
)
})
])
})
return () => { unlisten.then((f) => f()) }
}, []);
let handle_import = () => {
invoke('import_playlist').then((_res) => { invoke('import_playlist').then((_res) => {
let res = _res as any; let res = _res as any;
@ -114,6 +147,7 @@ function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: Play
<Song <Song
key={ song.uuid } key={ song.uuid }
location={ song.location } location={ song.location }
playerLocation={ {"Playlist" : res.uuid } }
uuid={ song.uuid } uuid={ song.uuid }
plays={ song.plays } plays={ song.plays }
duration={ song.duration } duration={ song.duration }
@ -140,6 +174,7 @@ function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: Play
<Song <Song
key={ song.uuid } key={ song.uuid }
location={ song.location } location={ song.location }
playerLocation="Library"
uuid={ song.uuid } uuid={ song.uuid }
plays={ song.plays } plays={ song.plays }
duration={ song.duration } duration={ song.duration }
@ -150,7 +185,7 @@ function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: Play
}) })
} }>Library</button> } }>Library</button>
{ playlists } { playlists }
<button onClick={ handle_import }>Import .m3u8 Playlist</button> <button onClick={ handle_import }>Import .m3u Playlist</button>
</section> </section>
) )
} }
@ -166,15 +201,16 @@ function MainView({ lib_ref, viewName }: MainViewProps) {
useEffect(() => { useEffect(() => {
const unlisten = appWindow.listen<any>("library_loaded", (_) => { const unlisten = appWindow.listen<any>("library_loaded", (_) => {
console.log("library_loaded"); console.log("library_loaded");
invoke('get_playlists').then(() => {})
invoke('get_library').then((lib) => { invoke('get_library').then((lib) => {
setLibrary([...(lib as any[]).map((song) => { setLibrary([...(lib as any[]).map((song) => {
console.log(song);
return ( return (
<Song <Song
key={ song.uuid } key={ song.uuid }
location={ song.location } location={ song.location }
playerLocation="Library"
uuid={ song.uuid } uuid={ song.uuid }
plays={ song.plays } plays={ song.plays }
duration={ song.duration } duration={ song.duration }
@ -198,6 +234,7 @@ function MainView({ lib_ref, viewName }: MainViewProps) {
interface SongProps { interface SongProps {
location: any, location: any,
playerLocation: string | {"Playlist" : any},
uuid: string, uuid: string,
plays: number, plays: number,
format?: string, format?: string,
@ -218,11 +255,11 @@ function Song(props: SongProps) {
<p className="artist">{ props.tags.AlbumArtist }</p> <p className="artist">{ props.tags.AlbumArtist }</p>
<p className="duration">{ props.duration }</p> <p className="duration">{ props.duration }</p>
<button onClick={(_) => { <button onClick={(_) => {
invoke('add_song_to_queue', { uuid: props.uuid, location: 'Library' }).then(() => {} ) invoke('add_song_to_queue', { uuid: props.uuid, location: props.playerLocation }).then(() => {} )
}} }}
>Add to Queue</button> >Add to Queue</button>
<button onClick={() => { <button onClick={() => {
invoke("play_now", { uuid: props.uuid, location: 'Library' }).then(() => {}) invoke("play_now", { uuid: props.uuid, location: props.playerLocation }).then(() => {})
}}>Play Now</button> }}>Play Now</button>
</div> </div>
) )