mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
started work on controller protoype
This commit is contained in:
parent
96069fd9bc
commit
3ad8b78e9d
3 changed files with 226 additions and 13 deletions
|
@ -2,25 +2,219 @@
|
||||||
//! 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::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::time::Duration;
|
||||||
|
use crossbeam_channel::{Sender, Receiver};
|
||||||
|
// use std::sync::mpsc;
|
||||||
|
use crossbeam_channel;
|
||||||
|
use gstreamer::format::Default;
|
||||||
|
use gstreamer::query::Uri;
|
||||||
|
use std::thread::{self, sleep, spawn};
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use crossbeam_channel::unbounded;
|
||||||
|
use rayon::iter::Rev;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
use crate::music_storage::library::Tag;
|
||||||
|
use crate::music_storage::playlist::Playlist;
|
||||||
use crate::{
|
use crate::{
|
||||||
music_player::Player,
|
music_player::Player,
|
||||||
music_storage::library::Song,
|
music_storage::{
|
||||||
config::config::Config
|
library::{MusicLibrary, Song}
|
||||||
|
},
|
||||||
|
config::config::Config,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Queue {
|
struct Queue {
|
||||||
player: Player,
|
player: Player,
|
||||||
name: String,
|
name: String,
|
||||||
songs: Vec<Song>,
|
songs: Playlist,
|
||||||
|
}
|
||||||
|
impl Queue {
|
||||||
|
fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
|
Ok(
|
||||||
|
Queue {
|
||||||
|
player: Player::new()?,
|
||||||
|
name: String::new(),
|
||||||
|
songs: Playlist::new()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
queues: Vec<Queue>,
|
// queues: Vec<Queue>,
|
||||||
config: Arc<RwLock<Config>>,
|
config: Arc<RwLock<Config>>,
|
||||||
|
// library: MusicLibrary,
|
||||||
|
controller_mail: MailMan<ControllerCommand, ControllerResponse>,
|
||||||
|
db_mail: MailMan<DatabaseCommand, DatabaseResponse>,
|
||||||
|
queue_mail: Vec<MailMan<QueueCommand, QueueResponse>>,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
|
||||||
|
pub enum ControllerCommand {
|
||||||
|
Default,
|
||||||
|
Test
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
|
||||||
|
enum ControllerResponse {
|
||||||
|
Empty,
|
||||||
|
QueueMailMan(MailMan<QueueCommand, QueueResponse>),
|
||||||
|
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
|
||||||
|
pub enum DatabaseCommand {
|
||||||
|
Default,
|
||||||
|
Test,
|
||||||
|
GetSongs,
|
||||||
|
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
|
||||||
|
enum DatabaseResponse {
|
||||||
|
Empty,
|
||||||
|
Songs(Vec<Song>),
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum QueueCommand {
|
||||||
|
Default,
|
||||||
|
Test,
|
||||||
|
Play,
|
||||||
|
Pause,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum QueueResponse {
|
||||||
|
Default,
|
||||||
|
Test,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Controller {
|
#[derive(Debug)]
|
||||||
// more stuff to come
|
|
||||||
|
struct MailMan<T, U> {
|
||||||
|
pub tx: Sender<T>,
|
||||||
|
rx: Receiver<U>
|
||||||
}
|
}
|
||||||
|
impl<T> MailMan<T, T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (tx, rx) = unbounded::<T>();
|
||||||
|
MailMan { tx, rx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T, U> MailMan<T, U> {
|
||||||
|
pub fn double() -> (MailMan<T, U>, MailMan<U, T>) {
|
||||||
|
let (tx, rx) = unbounded::<T>();
|
||||||
|
let (tx1, rx1) = unbounded::<U>();
|
||||||
|
|
||||||
|
(
|
||||||
|
MailMan { tx, rx: rx1 },
|
||||||
|
MailMan { tx: tx1, rx }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&self, mail: T) -> Result<(), Box<dyn Error>> {
|
||||||
|
&self.tx.send(mail).unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv(&self) -> Result<U, Box<dyn Error>> {
|
||||||
|
let u = self.rx.recv().unwrap();
|
||||||
|
Ok(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
impl Controller {
|
||||||
|
pub fn start(config: PathBuf) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let config = Config::read_file(config)?;
|
||||||
|
let uuid = config.libraries.get_default()?.uuid;
|
||||||
|
|
||||||
|
let config = Arc::new(RwLock::from(config));
|
||||||
|
let lib = MusicLibrary::init(config.clone(), uuid)?;
|
||||||
|
|
||||||
|
let (out_thread_controller, in_thread) = MailMan::double();
|
||||||
|
let monitor_thread = spawn(move || {
|
||||||
|
use ControllerCommand::*;
|
||||||
|
loop {
|
||||||
|
let command = in_thread.recv().unwrap();
|
||||||
|
|
||||||
|
match command {
|
||||||
|
Default => (),
|
||||||
|
Test => {
|
||||||
|
in_thread.send(ControllerResponse::Empty).unwrap();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let (out_thread_db, in_thread) = MailMan::double();
|
||||||
|
let db_monitor = spawn(move || {
|
||||||
|
use DatabaseCommand::*;
|
||||||
|
loop {
|
||||||
|
let command = in_thread.recv().unwrap();
|
||||||
|
|
||||||
|
match command {
|
||||||
|
Default => {},
|
||||||
|
Test => {
|
||||||
|
in_thread.send(DatabaseResponse::Empty).unwrap();
|
||||||
|
},
|
||||||
|
GetSongs => {
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
Controller {
|
||||||
|
// queues: Vec::new(),
|
||||||
|
config,
|
||||||
|
controller_mail: out_thread_controller,
|
||||||
|
db_mail: out_thread_db,
|
||||||
|
queue_mail: Vec::new(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn get_db_songs(&self) -> Vec<Song> {
|
||||||
|
self.db_mail.send(DatabaseCommand::GetSongs);
|
||||||
|
match self.db_mail.recv().unwrap() {
|
||||||
|
DatabaseResponse::Songs(songs) => songs,
|
||||||
|
_ => Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
pub fn new_queue(&mut self) {
|
||||||
|
let (out_thread_queue, in_thread) = MailMan::<QueueCommand, QueueResponse>::double();
|
||||||
|
let queues_monitor = spawn(move || {
|
||||||
|
use QueueCommand::*;
|
||||||
|
loop {
|
||||||
|
let command = in_thread.recv().unwrap();
|
||||||
|
match command {
|
||||||
|
Default => {},
|
||||||
|
Test => {},
|
||||||
|
Play => {},
|
||||||
|
Pause => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.queue_mail.push(out_thread_queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn name() {
|
||||||
|
let a = Controller::start(PathBuf::from("test-config/config_test.json")).unwrap();
|
||||||
|
// sleep(Duration::from_millis(5000));
|
||||||
|
_ = a.controller_mail.send(ControllerCommand::Test);
|
||||||
|
// dbg!(a.get_db_songs());
|
||||||
|
// sleep(Duration::from_secs(6));
|
||||||
|
}
|
|
@ -2,17 +2,20 @@ use file_format::FileFormat;
|
||||||
use lofty::{AudioFile, LoftyError, ParseOptions, Probe, TagType, TaggedFileExt};
|
use lofty::{AudioFile, LoftyError, ParseOptions, Probe, TagType, TaggedFileExt};
|
||||||
use quick_xml::events::Event;
|
use quick_xml::events::Event;
|
||||||
use quick_xml::reader::Reader;
|
use quick_xml::reader::Reader;
|
||||||
|
use uuid::Uuid;
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
use std::time::Duration as StdDur;
|
use std::time::Duration as StdDur;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
|
||||||
|
use crate::config::config::{Config, ConfigLibrary};
|
||||||
use crate::music_storage::db_reader::extern_library::ExternalLibrary;
|
use crate::music_storage::db_reader::extern_library::ExternalLibrary;
|
||||||
use crate::music_storage::library::{AlbumArt, Service, Song, Tag, URI};
|
use crate::music_storage::library::{AlbumArt, MusicLibrary, Service, Song, Tag, URI};
|
||||||
use crate::music_storage::utils;
|
use crate::music_storage::utils;
|
||||||
|
|
||||||
use urlencoding::decode;
|
use urlencoding::decode;
|
||||||
|
@ -320,4 +323,20 @@ impl ITunesSong {
|
||||||
// println!("{:.2?}", song);
|
// println!("{:.2?}", song);
|
||||||
Ok(song)
|
Ok(song)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn itunes_lib_test() {
|
||||||
|
let mut config = Config::read_file(PathBuf::from("test-config/config_test.json")).unwrap();
|
||||||
|
let config_lib = ConfigLibrary::new(PathBuf::from("test-config/library2"), String::from("library2"), None);
|
||||||
|
config.libraries.libraries.push(config_lib.clone());
|
||||||
|
|
||||||
|
let songs = ITunesLibrary::from_file(Path::new("test-config\\iTunesLib.xml")).to_songs();
|
||||||
|
|
||||||
|
let mut library = MusicLibrary::init(Arc::new(RwLock::from(config.clone())), config_lib.uuid).unwrap();
|
||||||
|
|
||||||
|
songs.iter().for_each(|song| library.add_song(song.to_owned()).unwrap());
|
||||||
|
|
||||||
|
config.write_file().unwrap();
|
||||||
|
library.save(config).unwrap();
|
||||||
}
|
}
|
|
@ -23,15 +23,15 @@ pub enum SortOrder {
|
||||||
Tag(Tag)
|
Tag(Tag)
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Playlist<'a> {
|
pub struct Playlist {
|
||||||
title: String,
|
title: String,
|
||||||
cover: Option<&'a AlbumArt>,
|
cover: Option<AlbumArt>,
|
||||||
tracks: Vec<Uuid>,
|
tracks: Vec<Uuid>,
|
||||||
sort_order: SortOrder,
|
sort_order: SortOrder,
|
||||||
play_count: i32,
|
play_count: i32,
|
||||||
play_time: Duration,
|
play_time: Duration,
|
||||||
}
|
}
|
||||||
impl<'a> Playlist<'a> {
|
impl Playlist {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ impl<'a> Playlist<'a> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
m3u8.write_to(&mut file).unwrap();
|
m3u8.write_to(&mut file).unwrap();
|
||||||
}
|
}
|
||||||
pub fn from_m3u8(path: &str) -> Result<Playlist<'a>, Error> {
|
pub fn from_m3u8(path: &str) -> Result<Playlist, Error> {
|
||||||
let mut file = match File::open(path) {
|
let mut file = match File::open(path) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
|
@ -137,7 +137,7 @@ impl<'a> Playlist<'a> {
|
||||||
&self.title
|
&self.title
|
||||||
}
|
}
|
||||||
fn cover(&self) -> Option<&AlbumArt> {
|
fn cover(&self) -> Option<&AlbumArt> {
|
||||||
match self.cover {
|
match &self.cover {
|
||||||
Some(e) => Some(e),
|
Some(e) => Some(e),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ impl<'a> Playlist<'a> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl Default for Playlist<'_> {
|
impl Default for Playlist {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Playlist {
|
Playlist {
|
||||||
title: String::default(),
|
title: String::default(),
|
||||||
|
|
Loading…
Reference in a new issue