mirror of
https://github.com/Dangoware/dmp-core.git
synced 2025-04-19 13:22:54 -05:00
Finished Controller Prototype
This commit is contained in:
parent
3ad8b78e9d
commit
599ddc584c
6 changed files with 139 additions and 25 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -13,3 +13,5 @@ music_database*
|
||||||
*.m3u
|
*.m3u
|
||||||
*.m3u8
|
*.m3u8
|
||||||
*.json
|
*.json
|
||||||
|
*.zip
|
||||||
|
*.xml
|
||||||
|
|
|
@ -71,8 +71,10 @@ impl ConfigLibraries {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_library(&self, uuid: &Uuid) -> Result<ConfigLibrary, ConfigError> {
|
pub fn get_library(&self, uuid: &Uuid) -> Result<ConfigLibrary, ConfigError> {
|
||||||
|
dbg!(&uuid);
|
||||||
for library in &self.libraries {
|
for library in &self.libraries {
|
||||||
if &library.uuid == uuid {
|
if &library.uuid == uuid {
|
||||||
|
dbg!(&library.uuid);
|
||||||
return Ok(library.to_owned())
|
return Ok(library.to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,8 +130,8 @@ impl Config {
|
||||||
let mut file: File = File::open(path)?;
|
let mut file: File = File::open(path)?;
|
||||||
let mut bun: String = String::new();
|
let mut bun: String = String::new();
|
||||||
_ = file.read_to_string(&mut bun);
|
_ = file.read_to_string(&mut bun);
|
||||||
let ny: Config = serde_json::from_str::<Config>(&bun)?;
|
let config: Config = serde_json::from_str::<Config>(&bun)?;
|
||||||
Ok(ny)
|
Ok(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//! player. It manages queues, playback, library access, and
|
//! player. It manages queues, playback, library access, and
|
||||||
//! other functions
|
//! other functions
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use crossbeam_channel::{Sender, Receiver};
|
use crossbeam_channel::{Sender, Receiver};
|
||||||
|
@ -18,7 +18,7 @@ use rayon::iter::Rev;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::music_storage::library::Tag;
|
use crate::music_storage::library::{Tag, URI};
|
||||||
use crate::music_storage::playlist::Playlist;
|
use crate::music_storage::playlist::Playlist;
|
||||||
use crate::{
|
use crate::{
|
||||||
music_player::Player,
|
music_player::Player,
|
||||||
|
@ -31,7 +31,7 @@ use crate::{
|
||||||
struct Queue {
|
struct Queue {
|
||||||
player: Player,
|
player: Player,
|
||||||
name: String,
|
name: String,
|
||||||
songs: Playlist,
|
songs: Vec<Song>,
|
||||||
}
|
}
|
||||||
impl Queue {
|
impl Queue {
|
||||||
fn new() -> Result<Self, Box<dyn Error>> {
|
fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
|
@ -39,10 +39,15 @@ impl Queue {
|
||||||
Queue {
|
Queue {
|
||||||
player: Player::new()?,
|
player: Player::new()?,
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
songs: Playlist::new()
|
songs: Vec::new()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
fn set_tracks(&mut self, tracks: Vec<Song>) {
|
||||||
|
let mut tracks = tracks;
|
||||||
|
self.songs.clear();
|
||||||
|
self.songs.append(&mut tracks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
|
@ -54,39 +59,47 @@ pub struct Controller {
|
||||||
queue_mail: Vec<MailMan<QueueCommand, QueueResponse>>,
|
queue_mail: Vec<MailMan<QueueCommand, QueueResponse>>,
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
||||||
pub enum ControllerCommand {
|
pub enum ControllerCommand {
|
||||||
Default,
|
Default,
|
||||||
Test
|
Test
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum ControllerResponse {
|
enum ControllerResponse {
|
||||||
Empty,
|
Empty,
|
||||||
QueueMailMan(MailMan<QueueCommand, QueueResponse>),
|
QueueMailMan(MailMan<QueueCommand, QueueResponse>),
|
||||||
|
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum DatabaseCommand {
|
pub enum DatabaseCommand {
|
||||||
Default,
|
Default,
|
||||||
Test,
|
Test,
|
||||||
GetSongs,
|
GetSongs,
|
||||||
|
QueryUuid(Uuid),
|
||||||
|
QueryUuids(Vec<Uuid>),
|
||||||
|
ReadFolder(String),
|
||||||
|
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum DatabaseResponse {
|
enum DatabaseResponse {
|
||||||
Empty,
|
Empty,
|
||||||
|
Song(Song),
|
||||||
Songs(Vec<Song>),
|
Songs(Vec<Song>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum QueueCommand {
|
enum QueueCommand {
|
||||||
Default,
|
Default,
|
||||||
Test,
|
Test,
|
||||||
Play,
|
Play,
|
||||||
Pause,
|
Pause,
|
||||||
|
SetSongs(Vec<Song>),
|
||||||
|
// SetLocation(URI),
|
||||||
|
Enqueue(URI),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum QueueResponse {
|
enum QueueResponse {
|
||||||
Default,
|
Default,
|
||||||
|
@ -94,11 +107,11 @@ enum QueueResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
||||||
struct MailMan<T, U> {
|
struct MailMan<T, U> {
|
||||||
pub tx: Sender<T>,
|
pub tx: Sender<T>,
|
||||||
rx: Receiver<U>
|
rx: Receiver<U>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MailMan<T, T> {
|
impl<T> MailMan<T, T> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let (tx, rx) = unbounded::<T>();
|
let (tx, rx) = unbounded::<T>();
|
||||||
|
@ -129,12 +142,13 @@ impl<T, U> MailMan<T, U> {
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
impl Controller {
|
impl Controller {
|
||||||
pub fn start(config: PathBuf) -> Result<Self, Box<dyn Error>> {
|
pub fn start(config_path: String) -> Result<Self, Box<dyn Error>> {
|
||||||
let config = Config::read_file(config)?;
|
let config_path = PathBuf::from(config_path);
|
||||||
|
let config = Config::read_file(config_path)?;
|
||||||
let uuid = config.libraries.get_default()?.uuid;
|
let uuid = config.libraries.get_default()?.uuid;
|
||||||
|
|
||||||
let config = Arc::new(RwLock::from(config));
|
let config = Arc::new(RwLock::from(config));
|
||||||
let lib = MusicLibrary::init(config.clone(), uuid)?;
|
let mut lib = MusicLibrary::init(config.clone(), uuid)?;
|
||||||
|
|
||||||
let (out_thread_controller, in_thread) = MailMan::double();
|
let (out_thread_controller, in_thread) = MailMan::double();
|
||||||
let monitor_thread = spawn(move || {
|
let monitor_thread = spawn(move || {
|
||||||
|
@ -167,6 +181,26 @@ impl Controller {
|
||||||
let songs = lib.query_tracks(&String::from(""), &(vec![Tag::Title]), &(vec![Tag::Title])).unwrap().iter().cloned().cloned().collect();
|
let songs = lib.query_tracks(&String::from(""), &(vec![Tag::Title]), &(vec![Tag::Title])).unwrap().iter().cloned().cloned().collect();
|
||||||
in_thread.send(DatabaseResponse::Songs(songs)).unwrap();
|
in_thread.send(DatabaseResponse::Songs(songs)).unwrap();
|
||||||
},
|
},
|
||||||
|
QueryUuid(uuid) => {
|
||||||
|
match lib.query_uuid(&uuid) {
|
||||||
|
Some(song) => in_thread.send(DatabaseResponse::Song(song.0.clone())).unwrap(),
|
||||||
|
None => in_thread.send(DatabaseResponse::Empty).unwrap(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
QueryUuids(uuids) => {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for uuid in uuids {
|
||||||
|
match lib.query_uuid(&uuid) {
|
||||||
|
Some(song) => vec.push(song.0.clone()),
|
||||||
|
None => unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in_thread.send(DatabaseResponse::Songs(vec)).unwrap();
|
||||||
|
},
|
||||||
|
ReadFolder(folder) => {
|
||||||
|
lib.scan_folder(&folder).unwrap();
|
||||||
|
in_thread.send(DatabaseResponse::Empty).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,6 +218,7 @@ impl Controller {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_db_songs(&self) -> Vec<Song> {
|
fn get_db_songs(&self) -> Vec<Song> {
|
||||||
self.db_mail.send(DatabaseCommand::GetSongs);
|
self.db_mail.send(DatabaseCommand::GetSongs);
|
||||||
match self.db_mail.recv().unwrap() {
|
match self.db_mail.recv().unwrap() {
|
||||||
|
@ -192,29 +227,76 @@ impl Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_queue(&mut self) {
|
pub fn new_queue(&mut self) {
|
||||||
let (out_thread_queue, in_thread) = MailMan::<QueueCommand, QueueResponse>::double();
|
let (out_thread_queue, in_thread) = MailMan::<QueueCommand, QueueResponse>::double();
|
||||||
let queues_monitor = spawn(move || {
|
let queues_monitor = spawn(move || {
|
||||||
use QueueCommand::*;
|
use QueueCommand::*;
|
||||||
|
let mut queue = Queue::new().unwrap();
|
||||||
loop {
|
loop {
|
||||||
let command = in_thread.recv().unwrap();
|
let command = in_thread.recv().unwrap();
|
||||||
match command {
|
match command {
|
||||||
Default => {},
|
Default => {},
|
||||||
Test => {},
|
Test => { in_thread.send(QueueResponse::Test).unwrap() },
|
||||||
Play => {},
|
Play => {
|
||||||
|
queue.player.play().unwrap();
|
||||||
|
in_thread.send(QueueResponse::Default).unwrap();
|
||||||
|
},
|
||||||
Pause => {},
|
Pause => {},
|
||||||
|
SetSongs(songs) => {
|
||||||
|
queue.set_tracks(songs);
|
||||||
|
in_thread.send(QueueResponse::Default).unwrap();
|
||||||
|
},
|
||||||
|
Enqueue(uri) => {
|
||||||
|
queue.player.enqueue_next(&uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.queue_mail.push(out_thread_queue);
|
self.queue_mail.push(out_thread_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn play(&self, index: usize) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mail = &self.queue_mail[index];
|
||||||
|
mail.send(QueueCommand::Play)?;
|
||||||
|
dbg!(mail.recv()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_songs(&self, index: usize, songs: Vec<Song>) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mail = &self.queue_mail[index];
|
||||||
|
mail.send(QueueCommand::SetSongs(songs))?;
|
||||||
|
dbg!(mail.recv()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enqueue(&self, index: usize, uri: URI) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mail = &self.queue_mail[index];
|
||||||
|
mail.send(QueueCommand::Enqueue(uri))?;
|
||||||
|
dbg!(mail.recv()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn scan_folder(&self, folder: String) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mail = &self.db_mail;
|
||||||
|
mail.send(DatabaseCommand::ReadFolder(folder))?;
|
||||||
|
dbg!(mail.recv()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn name() {
|
fn name() {
|
||||||
let a = Controller::start(PathBuf::from("test-config/config_test.json")).unwrap();
|
let mut a = match Controller::start("test-config/config_test.json".to_string()) {
|
||||||
// sleep(Duration::from_millis(5000));
|
Ok(c) => c,
|
||||||
_ = a.controller_mail.send(ControllerCommand::Test);
|
Err(e) => panic!("{e}")
|
||||||
// dbg!(a.get_db_songs());
|
};
|
||||||
// sleep(Duration::from_secs(6));
|
sleep(Duration::from_millis(500));
|
||||||
|
a.scan_folder("test-config/music/".to_string());
|
||||||
|
a.new_queue();
|
||||||
|
let songs = a.get_db_songs();
|
||||||
|
a.enqueue(0, songs[0].location.clone());
|
||||||
|
a.play(0).unwrap();
|
||||||
|
|
||||||
|
sleep(Duration::from_secs(10));
|
||||||
}
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::{fs::File, io::Read, path::Path, time::Duration};
|
use std::{fs::File, io::Read, path::Path, time::Duration};
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::utils::meta_offset;
|
use super::utils::meta_offset;
|
||||||
use crate::music_storage::db_reader::common::{get_bytes, get_bytes_vec};
|
use crate::music_storage::db_reader::common::{get_bytes, get_bytes_vec};
|
||||||
use crate::music_storage::db_reader::extern_library::ExternalLibrary;
|
use crate::music_storage::db_reader::extern_library::ExternalLibrary;
|
||||||
|
@ -177,6 +179,7 @@ impl FoobarPlaylistTrack {
|
||||||
|
|
||||||
Song {
|
Song {
|
||||||
location,
|
location,
|
||||||
|
uuid: Uuid::new_v4(),
|
||||||
plays: 0,
|
plays: 0,
|
||||||
skips: 0,
|
skips: 0,
|
||||||
favorited: false,
|
favorited: false,
|
||||||
|
|
|
@ -170,6 +170,7 @@ impl ExternalLibrary for ITunesLibrary {
|
||||||
|
|
||||||
let ny: Song = Song {
|
let ny: Song = Song {
|
||||||
location: sug,
|
location: sug,
|
||||||
|
uuid: Uuid::new_v4(),
|
||||||
plays: track.plays,
|
plays: track.plays,
|
||||||
skips: 0,
|
skips: 0,
|
||||||
favorited: track.favorited,
|
favorited: track.favorited,
|
||||||
|
|
|
@ -119,6 +119,7 @@ impl ToString for Field {
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||||
pub struct Song {
|
pub struct Song {
|
||||||
pub location: URI,
|
pub location: URI,
|
||||||
|
pub uuid: Uuid,
|
||||||
pub plays: i32,
|
pub plays: i32,
|
||||||
pub skips: i32,
|
pub skips: i32,
|
||||||
pub favorited: bool,
|
pub favorited: bool,
|
||||||
|
@ -256,6 +257,7 @@ impl Song {
|
||||||
|
|
||||||
let new_song = Song {
|
let new_song = Song {
|
||||||
location: URI::Local(binding),
|
location: URI::Local(binding),
|
||||||
|
uuid: Uuid::new_v4(),
|
||||||
plays: 0,
|
plays: 0,
|
||||||
skips: 0,
|
skips: 0,
|
||||||
favorited: false,
|
favorited: false,
|
||||||
|
@ -378,6 +380,7 @@ impl Song {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
uuid: Uuid::new_v4(),
|
||||||
plays: 0,
|
plays: 0,
|
||||||
skips: 0,
|
skips: 0,
|
||||||
favorited: false,
|
favorited: false,
|
||||||
|
@ -552,7 +555,7 @@ pub struct MusicLibrary {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn library_init() {
|
fn library_init() {
|
||||||
let config = Config::read_file(PathBuf::from("config_test.json")).unwrap();
|
let config = Config::read_file(PathBuf::from("test_config/config_test.json")).unwrap();
|
||||||
let target_uuid = config.libraries.libraries[0].uuid;
|
let target_uuid = config.libraries.libraries[0].uuid;
|
||||||
let a = MusicLibrary::init(Arc::new(RwLock::from(config)), target_uuid).unwrap();
|
let a = MusicLibrary::init(Arc::new(RwLock::from(config)), target_uuid).unwrap();
|
||||||
dbg!(a);
|
dbg!(a);
|
||||||
|
@ -575,13 +578,14 @@ impl MusicLibrary {
|
||||||
/// the [MusicLibrary] Vec
|
/// the [MusicLibrary] Vec
|
||||||
pub fn init(config: Arc<RwLock<Config>>, uuid: Uuid) -> Result<Self, Box<dyn Error>> {
|
pub fn init(config: Arc<RwLock<Config>>, uuid: Uuid) -> Result<Self, Box<dyn Error>> {
|
||||||
let global_config = &*config.read().unwrap();
|
let global_config = &*config.read().unwrap();
|
||||||
|
let path = global_config.libraries.get_library(&uuid)?.path;
|
||||||
|
|
||||||
let library: MusicLibrary = match global_config.libraries.get_library(&uuid)?.path.exists() {
|
let library: MusicLibrary = match path.exists() {
|
||||||
true => read_file(global_config.libraries.get_library(&uuid)?.path)?,
|
true => read_file(path)?,
|
||||||
false => {
|
false => {
|
||||||
// If the library does not exist, re-create it
|
// If the library does not exist, re-create it
|
||||||
let lib = MusicLibrary::new(String::new(), uuid);
|
let lib = MusicLibrary::new(String::new(), uuid);
|
||||||
write_file(&lib, global_config.libraries.get_library(&uuid)?.path)?;
|
write_file(&lib, path)?;
|
||||||
lib
|
lib
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -643,6 +647,26 @@ impl MusicLibrary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Queries for a [Song] by its [Uuid], returning a single `Song`
|
||||||
|
/// with the `Uuid` that matches along with its position in the library
|
||||||
|
pub fn query_uuid(&self, uuid: &Uuid) -> Option<(&Song, usize)> {
|
||||||
|
let result = self
|
||||||
|
.library
|
||||||
|
.par_iter()
|
||||||
|
.enumerate()
|
||||||
|
.try_for_each(|(i, track)| {
|
||||||
|
if uuid == &track.uuid {
|
||||||
|
return std::ops::ControlFlow::Break((track, i));
|
||||||
|
}
|
||||||
|
Continue(())
|
||||||
|
});
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Break(song) => Some(song),
|
||||||
|
Continue(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Queries for a [Song] by its [PathBuf], returning a `Vec<&Song>`
|
/// Queries for a [Song] by its [PathBuf], returning a `Vec<&Song>`
|
||||||
/// with matching `PathBuf`s
|
/// with matching `PathBuf`s
|
||||||
fn query_path(&self, path: PathBuf) -> Option<Vec<&Song>> {
|
fn query_path(&self, path: PathBuf) -> Option<Vec<&Song>> {
|
||||||
|
|
Loading…
Reference in a new issue