mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
Added Controller State and Playlist loading on startup
This commit is contained in:
parent
10223b23e2
commit
4f2d5ab64a
8 changed files with 220 additions and 55 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {}
|
|
|
@ -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!() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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!()
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
47
src/App.tsx
47
src/App.tsx
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue