mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
Created player monitor loop
This commit is contained in:
parent
eda7e6f200
commit
75393a010e
4 changed files with 131 additions and 82 deletions
|
@ -4,12 +4,14 @@
|
||||||
#![allow(while_true)]
|
#![allow(while_true)]
|
||||||
|
|
||||||
use chrono::TimeDelta;
|
use chrono::TimeDelta;
|
||||||
|
use crossbeam::atomic::AtomicCell;
|
||||||
use kushi::{Queue, QueueItemType};
|
use kushi::{Queue, QueueItemType};
|
||||||
use kushi::{QueueError, QueueItem};
|
use kushi::{QueueError, QueueItem};
|
||||||
use prismriver::{Prismriver, Volume, Error as PrismError};
|
use prismriver::{Prismriver, Volume, Error as PrismError};
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::to_string_pretty;
|
use serde_json::to_string_pretty;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
@ -158,7 +160,7 @@ pub struct ControllerInput {
|
||||||
),
|
),
|
||||||
library: MusicLibrary,
|
library: MusicLibrary,
|
||||||
config: Arc<RwLock<Config>>,
|
config: Arc<RwLock<Config>>,
|
||||||
playback_info: crossbeam::channel::Sender<PlaybackInfo>,
|
playback_info: Arc<AtomicCell<PlaybackInfo>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ControllerHandle {
|
pub struct ControllerHandle {
|
||||||
|
@ -168,11 +170,11 @@ pub struct ControllerHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ControllerHandle {
|
impl ControllerHandle {
|
||||||
pub fn new(library: MusicLibrary, config: Arc<RwLock<Config>>) -> (Self, ControllerInput, crossbeam::channel::Receiver<PlaybackInfo>) {
|
pub fn new(library: MusicLibrary, config: Arc<RwLock<Config>>) -> (Self, ControllerInput, Arc<AtomicCell<PlaybackInfo>>) {
|
||||||
let lib_mail = MailMan::double();
|
let lib_mail = MailMan::double();
|
||||||
let player_mail = MailMan::double();
|
let player_mail = MailMan::double();
|
||||||
let queue_mail = MailMan::double();
|
let queue_mail = MailMan::double();
|
||||||
let (playback_info_rx, playback_info_tx) = crossbeam::channel::bounded(1);
|
let playback_info = Arc::new(AtomicCell::new(PlaybackInfo::default()));
|
||||||
(
|
(
|
||||||
ControllerHandle {
|
ControllerHandle {
|
||||||
lib_mail: lib_mail.0.clone(),
|
lib_mail: lib_mail.0.clone(),
|
||||||
|
@ -185,9 +187,9 @@ impl ControllerHandle {
|
||||||
queue_mail,
|
queue_mail,
|
||||||
library,
|
library,
|
||||||
config,
|
config,
|
||||||
playback_info: playback_info_rx,
|
playback_info: Arc::clone(&playback_info),
|
||||||
},
|
},
|
||||||
playback_info_tx,
|
playback_info,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,13 +255,16 @@ impl Controller {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let player = Arc::new(RwLock::new(Prismriver::new()));
|
||||||
|
|
||||||
std::thread::scope(|scope| {
|
std::thread::scope(|scope| {
|
||||||
let queue_mail = queue_mail;
|
let a = scope.spawn({
|
||||||
let a = scope.spawn(|| {
|
let player = Arc::clone(&player);
|
||||||
|
let queue_mail = queue_mail.clone();
|
||||||
|
move || {
|
||||||
futures::executor::block_on(async {
|
futures::executor::block_on(async {
|
||||||
moro::async_scope!(|scope| {
|
moro::async_scope!(|scope| {
|
||||||
println!("async scope created");
|
println!("async scope created");
|
||||||
let player = Arc::new(RwLock::new(Prismriver::new()));
|
|
||||||
|
|
||||||
let _player = player.clone();
|
let _player = player.clone();
|
||||||
let _lib_mail = lib_mail.0.clone();
|
let _lib_mail = lib_mail.0.clone();
|
||||||
|
@ -276,17 +281,6 @@ impl Controller {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
scope
|
|
||||||
.spawn(async move {
|
|
||||||
Controller::player_monitor_loop(
|
|
||||||
player,
|
|
||||||
player_mail.0,
|
|
||||||
queue_mail.0,
|
|
||||||
playback_info,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
});
|
|
||||||
scope
|
scope
|
||||||
.spawn(async {
|
.spawn(async {
|
||||||
Controller::library_loop(
|
Controller::library_loop(
|
||||||
|
@ -300,6 +294,7 @@ impl Controller {
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
})
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let b = scope.spawn(|| {
|
let b = scope.spawn(|| {
|
||||||
|
@ -307,8 +302,19 @@ impl Controller {
|
||||||
Controller::queue_loop(queue, queue_mail.1).await;
|
Controller::queue_loop(queue, queue_mail.1).await;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let c = scope.spawn(|| {
|
||||||
|
Controller::player_monitor_loop(
|
||||||
|
player,
|
||||||
|
player_mail.0,
|
||||||
|
queue_mail.0,
|
||||||
|
playback_info,
|
||||||
|
).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
a.join().unwrap();
|
a.join().unwrap();
|
||||||
b.join().unwrap();
|
b.join().unwrap();
|
||||||
|
c.join().unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -532,20 +538,48 @@ impl Controller {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn player_monitor_loop(
|
fn player_monitor_loop(
|
||||||
player: Arc<RwLock<Prismriver>>,
|
player: Arc<RwLock<Prismriver>>,
|
||||||
player_mail: MailMan<PlayerCommand, PlayerResponse>,
|
player_mail: MailMan<PlayerCommand, PlayerResponse>,
|
||||||
queue_mail: MailMan<QueueCommand, QueueResponse>,
|
queue_mail: MailMan<QueueCommand, QueueResponse>,
|
||||||
player_info: crossbeam_channel::Sender<PlaybackInfo>
|
player_info: Arc<AtomicCell<PlaybackInfo>>,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
|
|
||||||
|
let finished_recv = player.read().unwrap().get_finished_recv();
|
||||||
|
|
||||||
|
std::thread::scope(|s| {
|
||||||
|
// Thread for timing and metadata
|
||||||
|
s.spawn({
|
||||||
|
let player = Arc::clone(&player);
|
||||||
|
move || {
|
||||||
|
while true {
|
||||||
|
let player = player.read().unwrap();
|
||||||
|
player_info.store(PlaybackInfo {
|
||||||
|
duration: player.duration(),
|
||||||
|
position: player.position(),
|
||||||
|
metadata: player.metadata(),
|
||||||
|
});
|
||||||
|
drop(player);
|
||||||
|
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Thread for End of Track
|
||||||
|
s.spawn(move || {
|
||||||
|
while true {
|
||||||
|
let _ = finished_recv.recv();
|
||||||
|
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for duration and spit it out
|
||||||
|
// Check for end of song to get the next
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
// std::thread::scope(|s| {
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Check for duration and spit it out
|
|
||||||
// // Check for end of song to get the next
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -590,6 +624,7 @@ impl Controller {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn queue_loop(
|
async fn queue_loop(
|
||||||
mut queue: Queue<QueueSong, QueueAlbum>,
|
mut queue: Queue<QueueSong, QueueAlbum>,
|
||||||
queue_mail: MailMan<QueueResponse, QueueCommand>,
|
queue_mail: MailMan<QueueResponse, QueueCommand>,
|
||||||
|
@ -646,8 +681,9 @@ impl Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct PlaybackInfo {
|
pub struct PlaybackInfo {
|
||||||
pub duration: TimeDelta,
|
pub duration: Option<TimeDelta>,
|
||||||
pub position: TimeDelta
|
pub position: Option<TimeDelta>,
|
||||||
|
pub metadata: HashMap<String, String>,
|
||||||
}
|
}
|
|
@ -1033,7 +1033,6 @@ impl MusicLibrary {
|
||||||
sort_by: &Vec<Tag>, // Tags to sort the resulting data by
|
sort_by: &Vec<Tag>, // Tags to sort the resulting data by
|
||||||
) -> Option<Vec<&Song>> {
|
) -> Option<Vec<&Song>> {
|
||||||
let songs = Arc::new(Mutex::new(Vec::new()));
|
let songs = Arc::new(Mutex::new(Vec::new()));
|
||||||
//let matcher = SkimMatcherV2::default();
|
|
||||||
|
|
||||||
self.library.par_iter().for_each(|track| {
|
self.library.par_iter().for_each(|track| {
|
||||||
for tag in target_tags {
|
for tag in target_tags {
|
||||||
|
@ -1048,18 +1047,6 @@ impl MusicLibrary {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
let match_level = match matcher.fuzzy_match(&normalize(&track_result), &normalize(query_string)) {
|
|
||||||
Some(conf) => conf,
|
|
||||||
None => continue
|
|
||||||
};
|
|
||||||
|
|
||||||
if match_level > 100 {
|
|
||||||
songs.lock().unwrap().push(track);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if normalize(&track_result.to_string())
|
if normalize(&track_result.to_string())
|
||||||
.contains(&normalize(&query_string.to_owned()))
|
.contains(&normalize(&query_string.to_owned()))
|
||||||
{
|
{
|
||||||
|
@ -1249,13 +1236,13 @@ impl MusicLibrary {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::{
|
use std::{path::PathBuf, time::Instant};
|
||||||
path::PathBuf,
|
use crate::music_storage::library::Tag;
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{tests::new_config_lib, Config},
|
config::Config,
|
||||||
music_storage::library::MusicLibrary,
|
music_storage::library::MusicLibrary,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1270,4 +1257,31 @@ mod test {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
dbg!(a);
|
dbg!(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn library_search() {
|
||||||
|
let lib = MusicLibrary::init(
|
||||||
|
PathBuf::from("/media/g2/Storage4/Media-Files/Music/Albums/library.dlib"),
|
||||||
|
Uuid::new_v4(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let timer = Instant::now();
|
||||||
|
let result = lib.query_tracks(
|
||||||
|
&String::from(""),
|
||||||
|
&vec![],
|
||||||
|
&vec![
|
||||||
|
Tag::Field("location".to_string()),
|
||||||
|
Tag::Album,
|
||||||
|
Tag::Disk,
|
||||||
|
Tag::Track,
|
||||||
|
],
|
||||||
|
).unwrap();
|
||||||
|
println!("{} songs in {}ms", result.len(), timer.elapsed().as_millis());
|
||||||
|
|
||||||
|
/*
|
||||||
|
for song in result {
|
||||||
|
println!("{:?}", song.tags.get(&Tag::Title));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ pub fn run() {
|
||||||
let (rx, tx) = unbounded::<Config>();
|
let (rx, tx) = unbounded::<Config>();
|
||||||
let (lib_rx, lib_tx) = unbounded::<Option<PathBuf>>();
|
let (lib_rx, lib_tx) = unbounded::<Option<PathBuf>>();
|
||||||
let (handle_rx, handle_tx) = unbounded::<ControllerHandle>();
|
let (handle_rx, handle_tx) = unbounded::<ControllerHandle>();
|
||||||
let (playback_handle_rx, playback_handle_tx) = unbounded::<crossbeam::channel::Receiver<PlaybackInfo>>();
|
|
||||||
|
|
||||||
let controller_thread = spawn(move || {
|
let controller_thread = spawn(move || {
|
||||||
let mut config = { tx.recv().unwrap() } ;
|
let mut config = { tx.recv().unwrap() } ;
|
||||||
|
@ -55,7 +54,7 @@ pub fn run() {
|
||||||
|
|
||||||
library.save(save_path).unwrap();
|
library.save(save_path).unwrap();
|
||||||
|
|
||||||
let (handle, input, playback_tx) = ControllerHandle::new(
|
let (handle, input, playback_info) = ControllerHandle::new(
|
||||||
library,
|
library,
|
||||||
std::sync::Arc::new(std::sync::RwLock::new(config))
|
std::sync::Arc::new(std::sync::RwLock::new(config))
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue