mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
Merge 27cae8ec41
into 359990b36a
This commit is contained in:
commit
7e4ec49639
4 changed files with 130 additions and 187 deletions
|
@ -1,5 +1,4 @@
|
||||||
use std::{fs::OpenOptions, io::Write};
|
use std::{fs::OpenOptions, io::Write};
|
||||||
|
|
||||||
use dmp_core::{
|
use dmp_core::{
|
||||||
music_controller::{
|
music_controller::{
|
||||||
connections::LastFMAuth,
|
connections::LastFMAuth,
|
||||||
|
@ -49,7 +48,7 @@ pub async fn play_now(
|
||||||
};
|
};
|
||||||
app.emit("queue_updated", ()).unwrap();
|
app.emit("queue_updated", ()).unwrap();
|
||||||
app.emit("now_playing_change", _Song::from(&song)).unwrap();
|
app.emit("now_playing_change", _Song::from(&song)).unwrap();
|
||||||
app.emit("playing", ()).unwrap();
|
app.emit("playing", true).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,12 @@ use std::{
|
||||||
fs,
|
fs,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
thread::{scope, spawn},
|
thread::{scope, spawn, JoinHandle},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use config::{close_window, get_config, open_config_window, save_config};
|
use config::{close_window, get_config, open_config_window, save_config};
|
||||||
use crossbeam::channel::{bounded, unbounded, Receiver, Sender};
|
use crossbeam::channel::{bounded, Receiver};
|
||||||
use dmp_core::{
|
use dmp_core::{
|
||||||
config::{Config, ConfigLibrary},
|
config::{Config, ConfigLibrary},
|
||||||
music_controller::{
|
music_controller::{
|
||||||
|
@ -19,7 +19,7 @@ use dmp_core::{
|
||||||
music_storage::library::{MusicLibrary, Song},
|
music_storage::library::{MusicLibrary, Song},
|
||||||
};
|
};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use tauri::{http::Response, Emitter, Manager, State, Wry};
|
use tauri::{http::Response, AppHandle, Emitter, Listener, Manager};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wrappers::{_Song, stop};
|
use wrappers::{_Song, stop};
|
||||||
|
|
||||||
|
@ -41,99 +41,11 @@ const LAST_FM_API_SECRET: &str = env!("LAST_FM_API_SECRET", "None");
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
let (config_rx, config_tx) = unbounded::<Config>();
|
let (sync_rx, sync_tx) = bounded::<()>(1);
|
||||||
let (lib_rx, lib_tx) = unbounded::<Option<PathBuf>>();
|
|
||||||
let (handle_rx, handle_tx) = unbounded::<ControllerHandle>();
|
|
||||||
let (playback_info_rx, playback_info_tx) = bounded(1);
|
|
||||||
let (next_rx, next_tx) = bounded(1);
|
|
||||||
|
|
||||||
let _controller_thread = spawn(move || {
|
|
||||||
let mut config = { config_tx.recv().unwrap() };
|
|
||||||
let scan_path = { lib_tx.recv().unwrap() };
|
|
||||||
let _temp_config = ConfigLibrary::default();
|
|
||||||
let _lib = config.libraries.get_default().unwrap_or(&_temp_config);
|
|
||||||
|
|
||||||
let save_path = if _lib.path == PathBuf::default() {
|
|
||||||
let p = scan_path.as_ref().unwrap().clone().canonicalize().unwrap();
|
|
||||||
|
|
||||||
if cfg!(windows) {
|
|
||||||
p.join("library_windows.dlib")
|
|
||||||
} else if cfg!(unix) {
|
|
||||||
p.join("library_unix.dlib")
|
|
||||||
} else {
|
|
||||||
p.join("library.dlib")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_lib.path.clone()
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"save_path: {}\nscan_path:{scan_path:?}",
|
|
||||||
save_path.display()
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut library = MusicLibrary::init(save_path.clone(), _lib.uuid).unwrap();
|
|
||||||
|
|
||||||
let scan_path = scan_path.unwrap_or_else(|| {
|
|
||||||
config
|
|
||||||
.libraries
|
|
||||||
.get_default()
|
|
||||||
.unwrap()
|
|
||||||
.scan_folders
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()[0]
|
|
||||||
.clone()
|
|
||||||
});
|
|
||||||
|
|
||||||
if config.libraries.get_default().is_err() {
|
|
||||||
library.scan_folder(&scan_path).unwrap();
|
|
||||||
config.push_library(ConfigLibrary::new(
|
|
||||||
save_path.clone(),
|
|
||||||
String::from("Library"),
|
|
||||||
Some(vec![scan_path.clone()]),
|
|
||||||
Some(library.uuid),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if library.library.is_empty() {
|
|
||||||
println!("library is empty");
|
|
||||||
} else {
|
|
||||||
config.write_file().unwrap();
|
|
||||||
}
|
|
||||||
println!("scan_path: {}", scan_path.display());
|
|
||||||
|
|
||||||
library.save(save_path).unwrap();
|
|
||||||
|
|
||||||
let last_fm_session = config.connections.last_fm_session.clone();
|
|
||||||
let listenbrainz_token = config.connections.listenbrainz_token.clone();
|
|
||||||
|
|
||||||
let (handle, input, playback_info, next_song_notification) =
|
|
||||||
ControllerHandle::new(library, std::sync::Arc::new(RwLock::new(config)));
|
|
||||||
|
|
||||||
handle.discord_rpc(DISCORD_CLIENT_ID);
|
|
||||||
if let Some(token) = listenbrainz_token {
|
|
||||||
handle.listenbrainz_scrobble_auth(token);
|
|
||||||
} else {
|
|
||||||
println!("No ListenBrainz token found");
|
|
||||||
}
|
|
||||||
if let Some(session) = last_fm_session {
|
|
||||||
handle.last_fm_scrobble_auth(
|
|
||||||
LAST_FM_API_KEY.to_string(),
|
|
||||||
LAST_FM_API_SECRET.to_string(),
|
|
||||||
LastFMAuth::Session(Some(session)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_rx.send(handle).unwrap();
|
|
||||||
playback_info_rx.send(playback_info).unwrap();
|
|
||||||
next_rx.send(next_song_notification).unwrap();
|
|
||||||
|
|
||||||
let _controller = futures::executor::block_on(Controller::start(input)).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let app = tauri::Builder::default()
|
let app = tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
init_get_config,
|
|
||||||
create_new_library,
|
|
||||||
get_library,
|
get_library,
|
||||||
play,
|
play,
|
||||||
pause,
|
pause,
|
||||||
|
@ -142,7 +54,6 @@ pub fn run() {
|
||||||
next,
|
next,
|
||||||
prev,
|
prev,
|
||||||
get_song,
|
get_song,
|
||||||
lib_already_created,
|
|
||||||
get_queue,
|
get_queue,
|
||||||
add_song_to_queue,
|
add_song_to_queue,
|
||||||
play_now,
|
play_now,
|
||||||
|
@ -157,53 +68,10 @@ pub fn run() {
|
||||||
get_config,
|
get_config,
|
||||||
save_config,
|
save_config,
|
||||||
close_window,
|
close_window,
|
||||||
|
start_controller,
|
||||||
])
|
])
|
||||||
.manage(ConfigRx(config_rx))
|
|
||||||
.manage(LibRx(lib_rx))
|
|
||||||
.manage(HandleTx(handle_tx))
|
|
||||||
.manage(tempfile::TempDir::new().unwrap())
|
.manage(tempfile::TempDir::new().unwrap())
|
||||||
.setup(|app| {
|
.manage(sync_rx)
|
||||||
let _app = app.handle().clone();
|
|
||||||
let app = _app.clone();
|
|
||||||
|
|
||||||
std::thread::Builder::new()
|
|
||||||
.name("PlaybackInfo handler".to_string())
|
|
||||||
.spawn(move || {
|
|
||||||
let mut _info: Arc<RwLock<PlaybackInfo>> =
|
|
||||||
Arc::new(RwLock::new(PlaybackInfo::default()));
|
|
||||||
let mut _now_playing: Arc<RwLock<Option<Song>>> = Arc::new(RwLock::new(None));
|
|
||||||
|
|
||||||
scope(|s| {
|
|
||||||
let info = _info.clone();
|
|
||||||
s.spawn(|| {
|
|
||||||
let info = info;
|
|
||||||
let playback_info = playback_info_tx.recv().unwrap();
|
|
||||||
while true {
|
|
||||||
let i = playback_info.take();
|
|
||||||
app.emit("playback_info", i.clone()).unwrap();
|
|
||||||
*info.write() = i;
|
|
||||||
std::thread::sleep(Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let now_playing = _now_playing.clone();
|
|
||||||
s.spawn(|| {
|
|
||||||
let now_playing = now_playing;
|
|
||||||
let next_song_notification = next_tx.recv().unwrap();
|
|
||||||
while true {
|
|
||||||
let song = next_song_notification.recv().unwrap();
|
|
||||||
app.emit("now_playing_change", _Song::from(&song)).unwrap();
|
|
||||||
app.emit("queue_updated", ()).unwrap();
|
|
||||||
app.emit("playing", ()).unwrap();
|
|
||||||
_ = now_playing.write().insert(song);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.register_asynchronous_uri_scheme_protocol("asset", move |ctx, req, res| {
|
.register_asynchronous_uri_scheme_protocol("asset", move |ctx, req, res| {
|
||||||
let query = req
|
let query = req
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -254,13 +122,110 @@ pub fn run() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConfigRx(Sender<Config>);
|
|
||||||
|
|
||||||
struct LibRx(Sender<Option<PathBuf>>);
|
|
||||||
struct HandleTx(Receiver<ControllerHandle>);
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn init_get_config(state: State<'_, ConfigRx>) -> Result<Config, String> {
|
fn start_controller(app: AppHandle) -> Result<(), String> {
|
||||||
|
spawn(move || {
|
||||||
|
let mut config = init_get_config().unwrap();
|
||||||
|
|
||||||
|
let (lib_path, lib_uuid) = match config.libraries.get_default() {
|
||||||
|
Ok(library) => {
|
||||||
|
(library.path.clone(), library.uuid)
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
(create_new_library().unwrap(), Uuid::new_v4())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let scan_path = lib_path.parent().unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"lib_path: {}\nscan_path:{scan_path:?}",
|
||||||
|
lib_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut library = MusicLibrary::init(lib_path.clone(), lib_uuid).unwrap();
|
||||||
|
|
||||||
|
if config.libraries.get_default().is_err() {
|
||||||
|
library.scan_folder(&scan_path).unwrap();
|
||||||
|
config.push_library(ConfigLibrary::new(
|
||||||
|
lib_path.clone(),
|
||||||
|
String::from("Library"),
|
||||||
|
Some(vec![scan_path.into()]),
|
||||||
|
Some(library.uuid),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if library.library.is_empty() {
|
||||||
|
println!("library is empty");
|
||||||
|
} else {
|
||||||
|
config.write_file().unwrap();
|
||||||
|
}
|
||||||
|
library.save(lib_path.to_path_buf()).unwrap();
|
||||||
|
app.emit("library_loaded", ()).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
let last_fm_session = config.connections.last_fm_session.clone();
|
||||||
|
let listenbrainz_token = config.connections.listenbrainz_token.clone();
|
||||||
|
|
||||||
|
let (handle, input, playback_info, next_song_notification) =
|
||||||
|
ControllerHandle::new(library, std::sync::Arc::new(RwLock::new(config)));
|
||||||
|
|
||||||
|
handle.discord_rpc(DISCORD_CLIENT_ID);
|
||||||
|
if let Some(token) = listenbrainz_token {
|
||||||
|
handle.listenbrainz_scrobble_auth(token);
|
||||||
|
} else {
|
||||||
|
println!("No ListenBrainz token found");
|
||||||
|
}
|
||||||
|
if let Some(session) = last_fm_session {
|
||||||
|
handle.last_fm_scrobble_auth(
|
||||||
|
LAST_FM_API_KEY.to_string(),
|
||||||
|
LAST_FM_API_SECRET.to_string(),
|
||||||
|
LastFMAuth::Session(Some(session)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.manage(handle);
|
||||||
|
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name("PlaybackInfo handler".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
let mut _info: Arc<RwLock<PlaybackInfo>> =
|
||||||
|
Arc::new(RwLock::new(PlaybackInfo::default()));
|
||||||
|
let mut _now_playing: Arc<RwLock<Option<Song>>> = Arc::new(RwLock::new(None));
|
||||||
|
|
||||||
|
scope(|s| {
|
||||||
|
let info = _info.clone();
|
||||||
|
s.spawn(|| {
|
||||||
|
let info = info;
|
||||||
|
let playback_info = playback_info;
|
||||||
|
while true {
|
||||||
|
let i = playback_info.take();
|
||||||
|
app.emit("playback_info", i.clone()).unwrap();
|
||||||
|
*info.write() = i;
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let now_playing = _now_playing.clone();
|
||||||
|
s.spawn(|| {
|
||||||
|
let now_playing = now_playing;
|
||||||
|
let next_song_notification = next_song_notification;
|
||||||
|
while true {
|
||||||
|
let song = next_song_notification.recv().unwrap();
|
||||||
|
app.emit("now_playing_change", _Song::from(&song)).unwrap();
|
||||||
|
app.emit("queue_updated", ()).unwrap();
|
||||||
|
app.emit("playing", true).unwrap();
|
||||||
|
_ = now_playing.write().insert(song);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let _controller = futures::executor::block_on(Controller::start(input)).unwrap();
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_get_config() -> Result<Config, String> {
|
||||||
if let Some(dir) = directories::ProjectDirs::from("", "Dangoware", "dmp") {
|
if let Some(dir) = directories::ProjectDirs::from("", "Dangoware", "dmp") {
|
||||||
let path = dir.config_dir();
|
let path = dir.config_dir();
|
||||||
fs::create_dir_all(path)
|
fs::create_dir_all(path)
|
||||||
|
@ -288,8 +253,6 @@ async fn init_get_config(state: State<'_, ConfigRx>) -> Result<Config, String> {
|
||||||
c
|
c
|
||||||
};
|
};
|
||||||
|
|
||||||
state.inner().0.send(config.clone()).unwrap();
|
|
||||||
|
|
||||||
println!("got config");
|
println!("got config");
|
||||||
Ok(config)
|
Ok(config)
|
||||||
} else {
|
} else {
|
||||||
|
@ -297,19 +260,14 @@ async fn init_get_config(state: State<'_, ConfigRx>) -> Result<Config, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn create_new_library(
|
fn create_new_library() -> Result<PathBuf, String> {
|
||||||
app: tauri::AppHandle<Wry>,
|
let dir = rfd::FileDialog::new()
|
||||||
lib_rx: State<'_, LibRx>,
|
|
||||||
handle_tx: State<'_, HandleTx>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let dir = rfd::AsyncFileDialog::new()
|
|
||||||
.set_title("Pick a library path")
|
.set_title("Pick a library path")
|
||||||
.pick_folder()
|
.pick_folder()
|
||||||
.await
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let path = dir.path().canonicalize().unwrap();
|
let path = dir.as_path().canonicalize().unwrap();
|
||||||
println!("{}", path.display());
|
println!("{}", path.display());
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
|
@ -318,21 +276,14 @@ async fn create_new_library(
|
||||||
panic!("Path {} is not a directory!", path.display())
|
panic!("Path {} is not a directory!", path.display())
|
||||||
}
|
}
|
||||||
|
|
||||||
lib_rx.inner().0.send(Some(path)).unwrap();
|
let path = if cfg!(windows) {
|
||||||
app.manage(handle_tx.inner().0.recv().unwrap());
|
path.join("library_windows.dlib")
|
||||||
app.emit("library_loaded", ()).unwrap();
|
} else if cfg!(unix) {
|
||||||
Ok(())
|
path.join("library_unix.dlib")
|
||||||
|
} else {
|
||||||
|
path.join("library.dlib")
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn lib_already_created(
|
|
||||||
app: tauri::AppHandle<Wry>,
|
|
||||||
lib_rx: State<'_, LibRx>,
|
|
||||||
handle_tx: State<'_, HandleTx>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
println!("lib already created");
|
|
||||||
lib_rx.inner().0.send(None).unwrap();
|
|
||||||
app.manage(handle_tx.inner().0.recv().unwrap());
|
|
||||||
app.emit("library_loaded", ()).unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub async fn play(
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
match ctrl_handle.play().await {
|
match ctrl_handle.play().await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
app.emit("playing", ()).unwrap();
|
app.emit("playing", true).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => Err(e.to_string()),
|
Err(e) => Err(e.to_string()),
|
||||||
|
@ -37,7 +37,7 @@ pub async fn pause(
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
match ctrl_handle.pause().await {
|
match ctrl_handle.pause().await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
app.emit("paused", ()).unwrap();
|
app.emit("playing", false).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => Err(e.to_string()),
|
Err(e) => Err(e.to_string()),
|
||||||
|
@ -84,7 +84,7 @@ pub async fn next(
|
||||||
};
|
};
|
||||||
app.emit("now_playing_change", _Song::from(&song)).unwrap();
|
app.emit("now_playing_change", _Song::from(&song)).unwrap();
|
||||||
app.emit("queue_updated", ()).unwrap();
|
app.emit("queue_updated", ()).unwrap();
|
||||||
app.emit("playing", ()).unwrap();
|
app.emit("playing", true).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/App.tsx
13
src/App.tsx
|
@ -65,21 +65,14 @@ function App() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisten = appWindow.listen<any>("playing", (_) => {
|
const unlisten = appWindow.listen<any>("playing", (isPlaying) => {
|
||||||
setPlaying(true)
|
setPlaying(isPlaying.payload as boolean)
|
||||||
})
|
})
|
||||||
return () => { unlisten.then((f) => f()) }
|
return () => { unlisten.then((f) => f()) }
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisten = appWindow.listen<any>("paused", (_) => {
|
const invoke_: any = invoke("start_controller").then(() => {});
|
||||||
setPlaying(false)
|
|
||||||
})
|
|
||||||
return () => { unlisten.then((f) => f()) }
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getConfig();
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
Loading…
Reference in a new issue