updated Kushi, mode changes to the moved location field to wrapper structs rather than as part of the queue

This commit is contained in:
MrDulfin 2024-07-07 19:27:13 -04:00
parent 67f2385c9d
commit e97075401d
4 changed files with 28 additions and 411 deletions

View file

@ -48,4 +48,4 @@ tempfile = "3.10.1"
listenbrainz = "0.7.0"
discord-rpc-client = "0.4.0"
nestify = "0.3.3"
kushi = "0.1.2"
kushi = "0.1.3"

View file

@ -11,7 +11,7 @@ pub mod music_storage {
pub mod music_controller {
pub mod controller;
pub mod connections;
// pub mod queue;
pub mod queue;
}
pub mod music_player {

View file

@ -4,8 +4,7 @@
use crossbeam_channel;
use crossbeam_channel::{Receiver, Sender};
use kushi::error::QueueError;
use kushi::Location;
use kushi::QueueError;
use kushi::{Queue, QueueItemType};
use std::path::PathBuf;
use std::sync::{Arc, Mutex, RwLock};
@ -18,13 +17,15 @@ use uuid::Uuid;
use crate::config::ConfigError;
use crate::music_player::player::{Player, PlayerCommand, PlayerError};
use crate::music_storage::library::{Album, Song};
use crate::{
config::Config, music_storage::library::MusicLibrary,
};
use super::queue::{QueueAlbum, QueueSong};
pub struct Controller<P: Player + Send + Sync> {
pub queue: Arc<RwLock<Queue<Song, Album, PlayerLocation>>>,
pub queue: Arc<RwLock<Queue<QueueSong, QueueAlbum>>>,
pub config: Arc<RwLock<Config>>,
pub library: MusicLibrary,
pub player: Arc<Mutex<P>>,
@ -51,8 +52,6 @@ pub enum PlayerLocation {
Custom,
}
impl Location for PlayerLocation {}
#[derive(Debug)]
pub(super) struct MailMan<T: Send, U: Send> {
pub tx: Sender<T>,
@ -99,7 +98,7 @@ impl<P: Player + Send + Sync + Sized + 'static> Controller<P> {
let config_ = Arc::new(RwLock::from(config));
let library = MusicLibrary::init(config_.clone(), uuid)?;
let queue: Queue<Song, Album, PlayerLocation> = Queue {
let queue: Queue<QueueSong, QueueAlbum> = Queue {
items: Vec::new(),
played: Vec::new(),
loop_: false,
@ -135,7 +134,7 @@ impl<P: Player + Send + Sync + Sized + 'static> Controller<P> {
.unwrap()
.enqueue_next(&{
match uri.item {
QueueItemType::Single(song) => song.primary_uri().unwrap().0.clone(),
QueueItemType::Single(song) => song.song.primary_uri().unwrap().0.clone(),
_ => unimplemented!()
}
})
@ -152,9 +151,9 @@ impl<P: Player + Send + Sync + Sized + 'static> Controller<P> {
Ok(controller)
}
pub fn q_add(&mut self, item: &Uuid, source: Option<PlayerLocation>, by_human: bool) {
pub fn q_add(&mut self, item: &Uuid, source: PlayerLocation, by_human: bool) {
let item = self.library.query_uuid(item).unwrap().0.to_owned();
self.queue.write().unwrap().add_item(item, source, by_human)
self.queue.write().unwrap().add_item(QueueSong { song: item, location: source }, by_human)
}
}
@ -162,7 +161,7 @@ impl<P: Player + Send + Sync + Sized + 'static> Controller<P> {
mod test_super {
use std::{thread::sleep, time::Duration};
use crate::{config::tests::read_config_lib, music_controller::controller::PlayerLocation, music_player::{gstreamer::GStreamer, player::Player}};
use crate::{config::tests::read_config_lib, music_controller::controller::{PlayerLocation, QueueSong}, music_player::{gstreamer::GStreamer, player::Player}};
use super::Controller;
@ -177,7 +176,7 @@ mod test_super {
{
let mut queue = controller.queue.write().unwrap();
for x in config.1.library {
queue.add_item(x, Some(PlayerLocation::Library), true);
queue.add_item(QueueSong { song: x, location: PlayerLocation::Library }, true);
}
}
{

View file

@ -1,407 +1,25 @@
use crate::music_storage::library::Song;
use uuid::Uuid;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum QueueError {
#[error("Index out of bounds! Index {index} is over len {len}")]
OutOfBounds { index: usize, len: usize },
#[error("The Queue is empty!")]
EmptyQueue,
#[error("There are no past played songs!")]
EmptyPlayed,
#[error("There is no item after this in the Queue")]
NoNext,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum QueueState {
Played,
First,
AddHere,
NoState,
}
use std::vec::IntoIter;
use crate::music_storage::library::{Album, AlbumTrack, Song};
use super::controller::PlayerLocation;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct QueueItem {
pub(super) item: Song,
pub(super) state: QueueState,
pub(super) source: PlayerLocation,
pub(super) by_human: bool,
pub struct QueueSong {
pub song: Song,
pub location: PlayerLocation,
}
impl QueueItem {
fn from_song(song: Song, source: PlayerLocation) -> Self {
QueueItem {
item: song,
state: QueueState::NoState,
source,
by_human: false,
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct QueueAlbum {
pub album: Album,
pub location: PlayerLocation,
}
#[derive(Debug, Default)]
pub struct Queue {
pub items: Vec<QueueItem>,
pub played: Vec<QueueItem>,
pub loop_: bool,
pub shuffle: Option<Vec<usize>>,
}
// TODO: HAndle the First QueueState[looping] and shuffle
impl Queue {
fn has_addhere(&self) -> bool {
for item in &self.items {
if item.state == QueueState::AddHere {
return true;
}
}
false
}
#[allow(unused)]
pub(super) fn dbg_items(&self) {
dbg!(
self.items
.iter()
.map(|item| (item.item.uuid, item.state))
.collect::<Vec<(Uuid, QueueState)>>(),
self.items.len()
);
}
pub fn set_items(&mut self, tracks: Vec<QueueItem>) {
let mut tracks = tracks;
self.items.clear();
self.items.append(&mut tracks);
}
/// Inserts an item after the AddHere item
pub fn add_item(&mut self, item: Song, source: PlayerLocation, by_human: bool) {
let mut i: usize = 0;
self.items = self
.items
.iter()
.enumerate()
.map(|(j, item_)| {
let mut item_ = item_.to_owned();
// get the index of the current AddHere item and give it to i
if item_.state == QueueState::AddHere {
i = j;
item_.state = QueueState::NoState;
}
item_
})
.collect::<Vec<QueueItem>>();
self.items.insert(
i + if self.items.is_empty() { 0 } else { 1 },
QueueItem {
item,
state: QueueState::AddHere,
source,
by_human,
},
);
}
/// Inserts an item after the currently playing item
pub fn add_item_next(&mut self, item: Song, source: PlayerLocation) {
use QueueState::*;
let empty = self.items.is_empty();
self.items.insert(
if empty { 0 } else { 1 },
QueueItem {
item,
state: if (self.items.get(1).is_none()
|| !self.has_addhere() && self.items.get(1).is_some())
|| empty
{
AddHere
} else {
NoState
},
source,
by_human: true,
},
)
}
pub fn add_multi(&mut self, items: Vec<Song>, source: PlayerLocation, by_human: bool) {
let mut i: usize = 0;
self.items = self
.items
.iter()
.enumerate()
.map(|(j, item_)| {
let mut item_ = item_.to_owned();
// get the index of the current AddHere item and give it to i
if item_.state == QueueState::AddHere {
i = j;
item_.state = QueueState::NoState;
}
item_
})
.collect::<Vec<QueueItem>>();
let empty = self.items.is_empty();
let len = items.len();
for item in items.into_iter().rev() {
self.items.insert(
i + if empty { 0 } else { 1 },
QueueItem {
item,
state: QueueState::NoState,
source,
by_human,
},
);
}
self.items[i + len - if empty { 1 } else { 0 }].state = QueueState::AddHere;
}
/// Add multiple Songs after the currently playing Song
pub fn add_multi_next(&mut self, items: Vec<Song>, source: PlayerLocation) {
use QueueState::*;
let empty = self.items.is_empty();
let add_here = (self.items.get(1).is_none()
|| !self.has_addhere() && self.items.get(1).is_some())
|| empty;
let len = items.len();
for item in items {
self.items.insert(
if empty { 0 } else { 1 },
QueueItem {
item,
state: NoState,
source,
by_human: true,
},
)
}
if add_here {
self.items[len - if empty { 1 } else { 0 }].state = QueueState::AddHere;
}
}
pub fn remove_item(&mut self, remove_index: usize) -> Result<QueueItem, QueueError> {
// dbg!(/*&remove_index, self.current_index(), &index,*/ &self.items[remove_index]);
if remove_index < self.items.len() {
// update the state of the next item to replace the item being removed
if self.items.get(remove_index + 1).is_some() {
self.items[remove_index + 1].state = self.items[remove_index].state;
}
Ok(self.items.remove(remove_index))
} else {
Err(QueueError::EmptyQueue)
}
}
pub fn insert(
&mut self,
index: usize,
new_item: Song,
source: PlayerLocation,
addhere: bool,
) -> Result<(), QueueError> {
if self.items.get_mut(index).is_none()
&& index > 0
&& self.items.get_mut(index - 1).is_none()
{
return Err(QueueError::OutOfBounds {
index,
len: self.items.len(),
});
}
if addhere {
let mut new_item = QueueItem::from_song(new_item, source);
for item in &mut self.items {
if item.state == QueueState::AddHere {
item.state = QueueState::NoState
}
}
new_item.state = QueueState::AddHere;
self.items.insert(index, new_item);
} else {
let new_item = QueueItem::from_song(new_item, source);
self.items.insert(index, new_item);
}
Ok(())
}
pub fn clear(&mut self) {
self.items.clear();
}
pub fn clear_except(&mut self, index: usize) -> Result<(), QueueError> {
use QueueState::*;
let empty = self.items.is_empty();
if !empty && index < self.items.len() {
let i = self.items[index].clone();
self.items.retain(|item| *item == i);
self.items[0].state = AddHere;
} else if empty {
return Err(QueueError::EmptyQueue);
} else {
return Err(QueueError::OutOfBounds {
index,
len: self.items.len(),
});
}
Ok(())
}
pub fn clear_played(&mut self) {
self.played.clear();
}
pub fn clear_all(&mut self) {
self.items.clear();
self.played.clear();
}
pub fn move_to(&mut self, index: usize) -> Result<(), QueueError> {
use QueueState::*;
let empty = self.items.is_empty();
let index = if !empty {
index
} else {
return Err(QueueError::EmptyQueue);
};
if !empty && dbg!(index < self.items.len()) {
let to_item = self.items[index].clone();
loop {
let empty = self.items.is_empty();
let item = self.items[0].item.to_owned();
if item != to_item.item && !empty {
if self.items[0].state == AddHere && self.items.get(1).is_some() {
self.items[1].state = AddHere;
}
let item = self.items.remove(0);
self.played.push(item);
// dbg!(&to_item.item, &self.items[ind].item);
} else if empty {
return Err(QueueError::EmptyQueue);
} else {
break;
}
}
} else {
return Err(QueueError::EmptyQueue);
}
Ok(())
}
pub fn swap(&mut self, a: usize, b: usize) {
self.items.swap(a, b)
}
pub fn move_item(&mut self, from: usize, to: usize) {
let item = self.items[from].to_owned();
if from != to {
self.items.remove(from);
}
self.items.insert(to, item);
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<&QueueItem, QueueError> {
if self.items.is_empty() {
if self.loop_ {
unimplemented!() // TODO: add function to loop the queue
} else {
return Err(QueueError::EmptyQueue);
}
}
if self.items[0].state == QueueState::AddHere || !self.has_addhere() {
self.items[0].state = QueueState::NoState;
if self.items.get_mut(1).is_some() {
self.items[1].state = QueueState::AddHere;
}
}
let item = self.items.remove(0);
self.played.push(item);
if self.items.is_empty() {
Err(QueueError::NoNext)
} else {
Ok(&self.items[0])
}
}
pub fn prev(&mut self) -> Result<&QueueItem, QueueError> {
if let Some(item) = self.played.pop() {
if item.state == QueueState::First && self.loop_ {
todo!()
}
self.items.insert(0, item);
Ok(&self.items[0])
} else {
Err(QueueError::EmptyPlayed)
}
}
pub fn now_playing(&self) -> Result<&QueueItem, QueueError> {
if !self.items.is_empty() {
Ok(&self.items[0])
} else {
Err(QueueError::EmptyQueue)
}
}
pub fn check_played(&mut self) {
while self.played.len() > 50 {
self.played.remove(0);
}
}
}
#[cfg(test)]
mod test_super {
#![allow(unused)]
use crate::{
config::tests::{new_config_lib, read_config_lib},
music_storage::library,
};
use super::*;
#[test]
fn move_test() {
let (_, library) = read_config_lib();
let mut q = Queue::default();
q.insert(0, library.library[2].to_owned(), PlayerLocation::File, true)
.inspect_err(|e| println!("{e}"));
q.insert(1, library.library[2].to_owned(), PlayerLocation::File, true)
.inspect_err(|e| println!("{e}"));
// q.next();
// q.clear();
q.dbg_items();
dbg!(&q.played.len());
// q.dbg_items();
impl IntoIterator for QueueAlbum {
type Item = AlbumTrack;
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.album.into_iter()
}
}