mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
Added basic Queue functionality
This commit is contained in:
parent
93d6b059a0
commit
d8cf8eeb2b
10 changed files with 223 additions and 93 deletions
|
@ -5,7 +5,7 @@
|
|||
|
||||
use kushi::{Queue, QueueItemType};
|
||||
use kushi::{QueueError, QueueItem};
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::error::Error;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
@ -32,7 +32,7 @@ pub enum ControllerError {
|
|||
}
|
||||
|
||||
// TODO: move this to a different location to be used elsewhere
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum PlayerLocation {
|
||||
Test,
|
||||
|
@ -103,17 +103,21 @@ enum InnerLibraryResponse<'a> {
|
|||
AllSongs(&'a Vec<Song>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum QueueCommand {
|
||||
Append(QueueItem<QueueSong, QueueAlbum>),
|
||||
Next,
|
||||
Prev,
|
||||
GetIndex(usize),
|
||||
NowPlaying,
|
||||
Get
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum QueueResponse {
|
||||
Ok,
|
||||
Item(QueueItem<QueueSong, QueueAlbum>),
|
||||
Get(Vec<QueueItem<QueueSong, QueueAlbum>>)
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,6 +127,10 @@ pub struct ControllerInput {
|
|||
MailMan<PlayerResponse, PlayerCommand>,
|
||||
),
|
||||
lib_mail: MailMan<LibraryResponse, LibraryCommand>,
|
||||
queue_mail: (
|
||||
MailMan<QueueCommand, QueueResponse>,
|
||||
MailMan<QueueResponse, QueueCommand>
|
||||
),
|
||||
library: MusicLibrary,
|
||||
config: Arc<RwLock<Config>>,
|
||||
}
|
||||
|
@ -130,21 +138,25 @@ pub struct ControllerInput {
|
|||
pub struct ControllerHandle {
|
||||
pub lib_mail: MailMan<LibraryCommand, LibraryResponse>,
|
||||
pub player_mail: MailMan<PlayerCommand, PlayerResponse>,
|
||||
pub queue_mail: MailMan<QueueCommand, QueueResponse>,
|
||||
}
|
||||
|
||||
impl ControllerHandle {
|
||||
pub fn new(library: MusicLibrary, config: Arc<RwLock<Config>>) -> (Self, ControllerInput) {
|
||||
let lib_mail = MailMan::double();
|
||||
let player_mail = MailMan::double();
|
||||
let queue_mail = MailMan::double();
|
||||
|
||||
(
|
||||
ControllerHandle {
|
||||
lib_mail: lib_mail.0,
|
||||
player_mail: player_mail.0.clone()
|
||||
player_mail: player_mail.0.clone(),
|
||||
queue_mail: queue_mail.0.clone()
|
||||
},
|
||||
ControllerInput {
|
||||
player_mail,
|
||||
lib_mail: lib_mail.1,
|
||||
queue_mail: queue_mail,
|
||||
library,
|
||||
config
|
||||
}
|
||||
|
@ -158,6 +170,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
ControllerInput {
|
||||
player_mail,
|
||||
lib_mail,
|
||||
queue_mail,
|
||||
mut library,
|
||||
config
|
||||
}: ControllerInput
|
||||
|
@ -173,20 +186,20 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
shuffle: None,
|
||||
};
|
||||
|
||||
for song in &library.library {
|
||||
queue.add_item(
|
||||
QueueSong {
|
||||
song: song.clone(),
|
||||
location: PlayerLocation::Test,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
// for song in &library.library {
|
||||
// queue.add_item(
|
||||
// QueueSong {
|
||||
// song: song.clone(),
|
||||
// location: PlayerLocation::Test,
|
||||
// },
|
||||
// true,
|
||||
// );
|
||||
// }
|
||||
let inner_lib_mail = MailMan::double();
|
||||
let queue = queue;
|
||||
|
||||
std::thread::scope(|scope| {
|
||||
let queue_mail = MailMan::double();
|
||||
let queue_mail = queue_mail;
|
||||
let a = scope.spawn(|| {
|
||||
futures::executor::block_on(async {
|
||||
moro::async_scope!(|scope| {
|
||||
|
@ -243,14 +256,19 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
player_mail: MailMan<PlayerResponse, PlayerCommand>,
|
||||
queue_mail: MailMan<QueueCommand, QueueResponse>,
|
||||
) -> Result<(), ()> {
|
||||
{
|
||||
player.write().unwrap().set_volume(0.05);
|
||||
}
|
||||
let mut first = true;
|
||||
while true {
|
||||
let _mail = player_mail.recv().await;
|
||||
if let Ok(mail) = _mail {
|
||||
match mail {
|
||||
PlayerCommand::Play => {
|
||||
if first {
|
||||
queue_mail.send(QueueCommand::NowPlaying).await.unwrap();
|
||||
let QueueResponse::Item(item) = queue_mail.recv().await.unwrap() else { unimplemented!() };
|
||||
let QueueItemType::Single(song) = item.item else { unimplemented!("This is temporary, handle queueItemTypes at some point") };
|
||||
player.write().unwrap().enqueue_next(song.song.primary_uri().unwrap().0).unwrap();
|
||||
first = false
|
||||
}
|
||||
player.write().unwrap().play().unwrap();
|
||||
player_mail.send(PlayerResponse::Empty).await.unwrap();
|
||||
}
|
||||
|
@ -272,8 +290,8 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
_ => unimplemented!(),
|
||||
};
|
||||
player.write().unwrap().enqueue_next(uri).unwrap();
|
||||
let QueueItemType::Single(x) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")};
|
||||
player_mail.send(PlayerResponse::NowPlaying(x.song.clone())).await.unwrap();
|
||||
let QueueItemType::Single(np_song) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")};
|
||||
player_mail.send(PlayerResponse::NowPlaying(np_song.song.clone())).await.unwrap();
|
||||
}
|
||||
}
|
||||
PlayerCommand::PrevSong => {
|
||||
|
@ -284,8 +302,8 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
QueueItemType::Single(song) => song.song.primary_uri().unwrap().0,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
player.write().unwrap().enqueue_next(uri).unwrap();
|
||||
player_mail.send(PlayerResponse::Empty).await.unwrap();
|
||||
let QueueItemType::Single(np_song) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")};
|
||||
player_mail.send(PlayerResponse::NowPlaying(np_song.song.clone())).await.unwrap();;
|
||||
}
|
||||
}
|
||||
PlayerCommand::Enqueue(index) => {
|
||||
|
@ -319,16 +337,17 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
lib_mail: MailMan<LibraryResponse, LibraryCommand>,
|
||||
inner_lib_mail: MailMan<InnerLibraryCommand, InnerLibraryResponse<'c>>,
|
||||
) -> Result<(), ()> {
|
||||
println!("outer lib loop");
|
||||
while true {
|
||||
match lib_mail.recv().await.unwrap() {
|
||||
LibraryCommand::Song(uuid) => {
|
||||
println!("got song commandf");
|
||||
inner_lib_mail
|
||||
.send(InnerLibraryCommand::Song(uuid))
|
||||
.await
|
||||
.unwrap();
|
||||
let x = inner_lib_mail.recv().await.unwrap();
|
||||
let InnerLibraryResponse::Song(song) = inner_lib_mail.recv().await.unwrap() else {
|
||||
unimplemented!();
|
||||
};
|
||||
lib_mail.send(LibraryResponse::Song(song.clone())).await.unwrap();
|
||||
}
|
||||
LibraryCommand::AllSongs => {
|
||||
inner_lib_mail
|
||||
|
@ -386,9 +405,15 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
) {
|
||||
while true {
|
||||
match queue_mail.recv().await.unwrap() {
|
||||
QueueCommand::Append(item) => match item.item {
|
||||
QueueItemType::Single(song) => queue.add_item(song, true),
|
||||
_ => unimplemented!(),
|
||||
QueueCommand::Append(item) => {
|
||||
match item.item {
|
||||
QueueItemType::Single(song) => queue.add_item(song, true),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
queue_mail
|
||||
.send(QueueResponse::Ok)
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
QueueCommand::Next => {
|
||||
let next = queue.next().unwrap();
|
||||
|
@ -405,7 +430,7 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
.unwrap();
|
||||
}
|
||||
QueueCommand::GetIndex(index) => {
|
||||
let item = queue.items[index].clone();
|
||||
let item = queue.items.get(index).expect("No item in the queue at index {index}").clone();
|
||||
queue_mail.send(QueueResponse::Item(item)).await.unwrap();
|
||||
}
|
||||
QueueCommand::NowPlaying => {
|
||||
|
@ -415,6 +440,9 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> {
|
|||
.await
|
||||
.unwrap();
|
||||
}
|
||||
QueueCommand::Get => {
|
||||
queue_mail.send(QueueResponse::Get(queue.items.clone())).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +473,7 @@ mod test_super {
|
|||
new_config_lib();
|
||||
|
||||
let config = Config::read_file(PathBuf::from(std::env!("CONFIG-PATH"))).unwrap();
|
||||
let mut library = {
|
||||
let library = {
|
||||
MusicLibrary::init(
|
||||
config.libraries.get_default().unwrap().path.clone(),
|
||||
config.libraries.get_default().unwrap().uuid,
|
||||
|
|
|
@ -475,18 +475,23 @@ impl Song {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn album_art(&self, i: usize) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
match self.album_art.get(i).unwrap() {
|
||||
AlbumArt::Embedded(j) => {
|
||||
let file = lofty::read_from_path(self.primary_uri()?.0.path())?;
|
||||
Ok(file.tag(file.primary_tag_type()).unwrap().pictures()[*j].data().to_vec())
|
||||
},
|
||||
AlbumArt::External(ref path) => {
|
||||
let mut buf = vec![];
|
||||
std::fs::File::open(path.path())?.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
pub fn album_art(&self, i: usize) -> Result<Option<Vec<u8>>, Box<dyn Error>> {
|
||||
if let Some(art) = self.album_art.get(i) {
|
||||
match art {
|
||||
AlbumArt::Embedded(j) => {
|
||||
let file = lofty::read_from_path(self.primary_uri()?.0.path())?;
|
||||
Ok(Some(file.tag(file.primary_tag_type()).unwrap().pictures()[*j].data().to_vec()))
|
||||
},
|
||||
AlbumArt::External(ref path) => {
|
||||
let mut buf = vec![];
|
||||
std::fs::File::open(path.path())?.read_to_end(&mut buf)?;
|
||||
Ok(Some(buf))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ QueueItem<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Queue<
|
||||
T: Debug + Clone + PartialEq, // T: The Singular Item Type
|
||||
U: Debug + PartialEq + Clone + IntoIterator, // U: The Multi-Item Type. Needs to be tracked as multiple items
|
||||
|
|
|
@ -19,6 +19,7 @@ tauri-build = { version = "2", features = [] }
|
|||
|
||||
[dependencies]
|
||||
dmp-core = { path = "../dmp-core" }
|
||||
kushi = { path = "../kushi-queue" }
|
||||
tauri = { version = "2", features = [ "protocol-asset", "unstable"] }
|
||||
tauri-plugin-shell = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
@ -26,11 +27,12 @@ serde_json = "1"
|
|||
futures = "0.3.31"
|
||||
crossbeam = "0.8.4"
|
||||
directories = "5.0.1"
|
||||
uuid = { version = "1.11.0", features = ["v4"] }
|
||||
uuid = { version = "1.11.0", features = ["v4", "serde"] }
|
||||
ciborium = "0.2.2"
|
||||
mime = "0.3.17"
|
||||
file-format = "0.26.0"
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
itertools = "0.13.0"
|
||||
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
|
|
20
src-tauri/src/commands.rs
Normal file
20
src-tauri/src/commands.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use dmp_core::music_controller::{controller::{ControllerHandle, LibraryResponse, PlayerLocation, QueueResponse}, queue::QueueSong};
|
||||
use kushi::QueueItem;
|
||||
use tauri::{AppHandle, Emitter, State, Wry};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn add_song_to_queue(app: AppHandle<Wry>, ctrl_handle: State<'_, ControllerHandle>, uuid: Uuid, location: PlayerLocation) -> Result<(), String> {
|
||||
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 {
|
||||
unreachable!()
|
||||
};
|
||||
ctrl_handle.queue_mail.send(dmp_core::music_controller::controller::QueueCommand::Append(QueueItem::from_item_type(kushi::QueueItemType::Single(QueueSong { song, location })))).await.unwrap();
|
||||
let QueueResponse::Ok = ctrl_handle.queue_mail.recv().await.unwrap() else {
|
||||
panic!()
|
||||
};
|
||||
app.emit("queue_updated", ()).unwrap();
|
||||
Ok(())
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
use std::{fs, io::Read, path::PathBuf, str::FromStr, thread::spawn, time::Duration};
|
||||
use std::{fs, path::PathBuf, str::FromStr, thread::spawn};
|
||||
|
||||
use commands::add_song_to_queue;
|
||||
use crossbeam::channel::{unbounded, Receiver, Sender};
|
||||
use dmp_core::{config::{Config, ConfigLibrary}, music_controller::controller::{Controller, ControllerHandle}, music_player::gstreamer::GStreamer, music_storage::library::{AlbumArt, MusicLibrary}};
|
||||
use dmp_core::{config::{Config, ConfigLibrary}, music_controller::controller::{Controller, ControllerHandle, LibraryResponse}, music_player::gstreamer::GStreamer, music_storage::library::{AlbumArt, MusicLibrary}};
|
||||
use tauri::{http::Response, Manager, State, Url, WebviewWindowBuilder, Wry};
|
||||
use uuid::Uuid;
|
||||
use wrappers::ArtworkRx;
|
||||
|
||||
use crate::wrappers::{get_library, play, pause, prev, set_volume, get_song, next};
|
||||
use crate::wrappers::{get_library, play, pause, prev, set_volume, get_song, next, get_queue};
|
||||
|
||||
pub mod wrappers;
|
||||
pub mod commands;
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let (rx, tx) = unbounded::<Config>();
|
||||
let (lib_rx, lib_tx) = unbounded::<Option<PathBuf>>();
|
||||
let (handle_rx, handle_tx) = unbounded::<ControllerHandle>();
|
||||
let (art_rx, art_tx) = unbounded::<Vec<u8>>();
|
||||
|
||||
let controller_thread = spawn(move || {
|
||||
let mut config = { tx.recv().unwrap() } ;
|
||||
|
@ -32,10 +32,10 @@ pub fn run() {
|
|||
).unwrap();
|
||||
|
||||
let scan_path = scan_path.unwrap_or_else(|| config.libraries.get_default().unwrap().scan_folders.as_ref().unwrap()[0].clone());
|
||||
library.scan_folder(&scan_path).unwrap();
|
||||
// library.scan_folder(&scan_path).unwrap();
|
||||
|
||||
if config.libraries.get_default().is_err() {
|
||||
config.push_library( ConfigLibrary::new(save_path, String::from("Library"), Some(vec![scan_path.clone()])));
|
||||
config.push_library( ConfigLibrary::new(save_path.clone(), String::from("Library"), Some(vec![scan_path.clone()])));
|
||||
}
|
||||
if library.library.is_empty() {
|
||||
println!("library is empty");
|
||||
|
@ -44,6 +44,8 @@ pub fn run() {
|
|||
}
|
||||
println!("scan_path: {}", scan_path.display());
|
||||
|
||||
library.save(save_path).unwrap();
|
||||
|
||||
let (handle, input) = ControllerHandle::new(
|
||||
library,
|
||||
std::sync::Arc::new(std::sync::RwLock::new(config))
|
||||
|
@ -51,9 +53,8 @@ pub fn run() {
|
|||
|
||||
handle_rx.send(handle).unwrap();
|
||||
|
||||
let controller = futures::executor::block_on(Controller::<GStreamer>::start(input)).unwrap();
|
||||
let _controller = futures::executor::block_on(Controller::<GStreamer>::start(input)).unwrap();
|
||||
});
|
||||
|
||||
let app = tauri::Builder::default()
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
|
@ -68,20 +69,37 @@ pub fn run() {
|
|||
prev,
|
||||
get_song,
|
||||
lib_already_created,
|
||||
|
||||
get_queue,
|
||||
add_song_to_queue,
|
||||
]).manage(ConfigRx(rx))
|
||||
.manage(LibRx(lib_rx))
|
||||
.manage(HandleTx(handle_tx))
|
||||
.manage(ArtworkRx(art_rx))
|
||||
.register_asynchronous_uri_scheme_protocol("asset", move |_, req, res| {
|
||||
dbg!(req);
|
||||
let buf = art_tx.recv().unwrap_or_else(|_| Vec::new());
|
||||
.register_asynchronous_uri_scheme_protocol("asset", move |ctx, req, res| {
|
||||
let query = req
|
||||
.clone()
|
||||
.uri()
|
||||
.clone()
|
||||
.into_parts()
|
||||
.path_and_query
|
||||
.unwrap()
|
||||
.query()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let bytes = futures::executor::block_on(async move {
|
||||
let controller = ctx.app_handle().state::<ControllerHandle>();
|
||||
controller.lib_mail.send(dmp_core::music_controller::controller::LibraryCommand::Song(Uuid::parse_str(query.as_str()).unwrap())).await.unwrap();
|
||||
let LibraryResponse::Song(song) = controller.lib_mail.recv().await.unwrap() else { unreachable!() };
|
||||
song.album_art(0).unwrap_or_else(|_| None).unwrap_or_default()
|
||||
});
|
||||
|
||||
|
||||
res.respond(
|
||||
Response::builder()
|
||||
.header("Origin", "*")
|
||||
.header("Content-Length", buf.len())
|
||||
.header("Content-Length", bytes.len())
|
||||
.status(200)
|
||||
.body(buf)
|
||||
.body(bytes)
|
||||
.unwrap()
|
||||
);
|
||||
println!("res sent")
|
||||
|
@ -96,7 +114,7 @@ pub fn run() {
|
|||
}
|
||||
_ => {}
|
||||
});
|
||||
// controller_thread.join().unwrap();
|
||||
std::mem::drop(controller_thread)
|
||||
}
|
||||
|
||||
struct ConfigRx(Sender<Config>);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
pub mod wrappers;
|
||||
|
||||
|
||||
fn main() {
|
||||
dango_music_player_lib::run()
|
||||
|
|
|
@ -2,7 +2,9 @@ use std::collections::BTreeMap;
|
|||
|
||||
use chrono::{DateTime, Utc, serde::ts_milliseconds_option};
|
||||
use crossbeam::channel::Sender;
|
||||
use dmp_core::{music_controller::controller::{ControllerHandle, LibraryCommand, LibraryResponse, PlayerResponse}, music_storage::library::{BannedType, Song, URI}};
|
||||
use dmp_core::{music_controller::controller::{ControllerHandle, LibraryCommand, LibraryResponse, PlayerResponse, QueueCommand, QueueResponse}, music_storage::library::{BannedType, Song, URI}};
|
||||
use itertools::Itertools;
|
||||
use kushi::QueueItemType;
|
||||
use serde::Serialize;
|
||||
use tauri::{ipc::Response, AppHandle, Emitter, State, Wry};
|
||||
use uuid::Uuid;
|
||||
|
@ -44,24 +46,26 @@ pub async fn get_volume(ctrl_handle: State<'_, ControllerHandle>) -> Result<(),
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn next(app: AppHandle<Wry>, ctrl_handle: State<'_, ControllerHandle>, art_rx: State<'_, ArtworkRx>) -> Result<(), String> {
|
||||
pub async fn next(app: AppHandle<Wry>, ctrl_handle: State<'_, ControllerHandle>) -> Result<(), String> {
|
||||
ctrl_handle.player_mail.send(dmp_core::music_controller::controller::PlayerCommand::NextSong).await.unwrap();
|
||||
let PlayerResponse::NowPlaying(song) = ctrl_handle.player_mail.recv().await.unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
let _song = _Song::from(&song);
|
||||
art_rx.0.send(song.album_art(0).unwrap()).unwrap();
|
||||
println!("next");
|
||||
app.emit("now_playing_change", _song).unwrap();
|
||||
app.emit("now_playing_change", _Song::from(&song)).unwrap();
|
||||
app.emit("queue_updated", ()).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn prev(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), String> {
|
||||
pub async fn prev(app: AppHandle<Wry>, ctrl_handle: State<'_, ControllerHandle>) -> Result<(), String> {
|
||||
ctrl_handle.player_mail.send(dmp_core::music_controller::controller::PlayerCommand::PrevSong).await.unwrap();
|
||||
let PlayerResponse::Empty = ctrl_handle.player_mail.recv().await.unwrap() else {
|
||||
let PlayerResponse::NowPlaying(song) = ctrl_handle.player_mail.recv().await.unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
println!("prev");
|
||||
app.emit("now_playing_change", _Song::from(&song)).unwrap();
|
||||
app.emit("queue_updated", ()).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -71,6 +75,17 @@ pub async fn now_playing(ctrl_handle: State<'_, ControllerHandle>) -> Result<(),
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_queue(ctrl_handle: State<'_, ControllerHandle>) -> Result<Vec<_Song>, String> {
|
||||
ctrl_handle.queue_mail.send(QueueCommand::Get).await.unwrap();
|
||||
let QueueResponse::Get(queue) = ctrl_handle.queue_mail.recv().await.unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
Ok(queue.into_iter().map(|item| {
|
||||
let QueueItemType::Single(song) = item.item else { unreachable!("There should be no albums in the queue right now") };
|
||||
_Song::from(&song.song)
|
||||
}).collect_vec())
|
||||
}
|
||||
|
||||
//Grab Album art from custom protocol
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
|
@ -121,11 +136,4 @@ pub async fn get_song(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), St
|
|||
let LibraryResponse::Song(_) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") };
|
||||
println!("got songs");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct NowPlaying {
|
||||
title: String,
|
||||
artist: String,
|
||||
album: String,
|
||||
}
|
29
src/App.css
29
src/App.css
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
|
||||
.leftSide {
|
||||
width: 85%;
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -19,7 +19,7 @@
|
|||
.rightSide {
|
||||
position: relative;
|
||||
align-self:flex-end;
|
||||
width: 15%;
|
||||
width: 20%;
|
||||
height: 100%;
|
||||
background-color: #c1bcd1;
|
||||
display: flex;
|
||||
|
@ -71,6 +71,31 @@
|
|||
bottom: -25%;
|
||||
background-color: burlywood;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.queueSongButton {
|
||||
height: 15%;
|
||||
padding: 0%;
|
||||
margin: 0%;
|
||||
}
|
||||
|
||||
.queueSong {
|
||||
height: 15%;
|
||||
width: 90%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.queueSongCoverArt {
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.queueSongTags {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.song {
|
||||
|
|
62
src/App.tsx
62
src/App.tsx
|
@ -10,7 +10,8 @@ import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
|||
const appWindow = getCurrentWebviewWindow();
|
||||
|
||||
function App() {
|
||||
const library = useState<JSX.Element[]>();
|
||||
const library = useState<JSX.Element[]>([]);
|
||||
const [queue, setQueue] = useState<JSX.Element[]>([]);
|
||||
|
||||
const [nowPlaying, setNowPlaying] = useState<JSX.Element>(
|
||||
<NowPlaying
|
||||
|
@ -24,7 +25,6 @@ function App() {
|
|||
useEffect(() => {
|
||||
const unlisten = appWindow.listen<any>("now_playing_change", ({ event, payload }) => {
|
||||
// console.log(event);
|
||||
|
||||
setNowPlaying(
|
||||
<NowPlaying
|
||||
title={ payload.tags.TrackTitle }
|
||||
|
@ -35,7 +35,19 @@ function App() {
|
|||
)
|
||||
|
||||
})
|
||||
return () => { unlisten.then((f) => f()) }
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unlisten = appWindow.listen<any>("queue_updated", (_) => {
|
||||
// console.log(event);
|
||||
invoke('get_queue').then((_songs) => {
|
||||
let songs = _songs as any[]
|
||||
setQueue(
|
||||
songs.filter((_, i) => i != 0).map((song) => <QueueSong song={ song } key={ song.uuid + '_' + Math.floor((Math.random() * 100_000) + 1) + '_' + Date.now() } />)
|
||||
)
|
||||
})
|
||||
})
|
||||
return () => { unlisten.then((f) => f()) }
|
||||
}, []);
|
||||
|
||||
|
@ -52,20 +64,13 @@ function App() {
|
|||
</div>
|
||||
<div className="rightSide">
|
||||
{ nowPlaying }
|
||||
<Queue />
|
||||
<Queue songs={queue} setSongs={ setQueue } />
|
||||
</div>
|
||||
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
interface L {
|
||||
uuid: number,
|
||||
}
|
||||
function LI({uuid}: L) {
|
||||
return ( <img src={convertFileSrc("abc") + "?" + uuid } id="nowPlayingArtwork" alt="Some Image" key={uuid} /> )
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
function getConfig(): any {
|
||||
|
@ -99,7 +104,7 @@ function PlaylistHead() {
|
|||
}
|
||||
|
||||
interface MainViewProps {
|
||||
lib_ref: [JSX.Element[] | undefined, React.Dispatch<React.SetStateAction<JSX.Element[] | undefined>>],
|
||||
lib_ref: [JSX.Element[], React.Dispatch<React.SetStateAction<JSX.Element[]>>],
|
||||
}
|
||||
|
||||
function MainView({ lib_ref }: MainViewProps) {
|
||||
|
@ -145,7 +150,7 @@ interface SongProps {
|
|||
}
|
||||
|
||||
function Song(props: SongProps) {
|
||||
console.log(props.tags);
|
||||
// console.log(props.tags);
|
||||
|
||||
return(
|
||||
<div className="song">
|
||||
|
@ -153,6 +158,10 @@ function Song(props: SongProps) {
|
|||
<p className="album">{ props.tags.Album }</p>
|
||||
<p className="artist">{ props.tags.AlbumArtist }</p>
|
||||
<p className="duration">{ props.duration }</p>
|
||||
<button onClick={(_) => {
|
||||
invoke('add_song_to_queue', { uuid: props.uuid, location: 'Library' }).then(() => {} )
|
||||
}}
|
||||
>Add to Queue</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -208,17 +217,34 @@ function NowPlaying({ title, artist, album, artwork }: NowPlayingProps) {
|
|||
)
|
||||
}
|
||||
|
||||
function Queue() {
|
||||
interface QueueProps {
|
||||
songs: JSX.Element[],
|
||||
setSongs: React.Dispatch<React.SetStateAction<JSX.Element[]>>
|
||||
}
|
||||
function Queue({ songs, setSongs }: QueueProps) {
|
||||
return (
|
||||
<section className="Queue">
|
||||
This is where the Queue be
|
||||
{ songs }
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
interface CurrentArtProps {
|
||||
uuid: number,
|
||||
interface QueueSongProps {
|
||||
song: any
|
||||
}
|
||||
function CurrentArt({uuid}: CurrentArtProps) {
|
||||
return <img src={convertFileSrc("abc") + "?" + uuid } id="nowPlayingArtwork" alt="Now Playing Artwork" key={uuid} />
|
||||
|
||||
function QueueSong({ song }: QueueSongProps) {
|
||||
console.log(song.tags);
|
||||
|
||||
return (
|
||||
// <button className="queueSongButton">
|
||||
<div className="queueSong">
|
||||
<img className="queueSongCoverArt" src={ convertFileSrc('abc') + '?' + song.uuid } key={ 'coverArt_' + song.uuid }/>
|
||||
<div className="queueSongTags">
|
||||
<h3 className="queueSongTitle">{ song.tags.TrackTitle }</h3>
|
||||
<h4 className="queueSongArtist">{ song.tags.TrackArtist }</h4>
|
||||
</div>
|
||||
</div>
|
||||
// </button>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue