mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
updated Kushi, mode changes to the moved location field to wrapper structs rather than as part of the queue
This commit is contained in:
parent
67f2385c9d
commit
e97075401d
4 changed files with 28 additions and 411 deletions
|
@ -48,4 +48,4 @@ tempfile = "3.10.1"
|
||||||
listenbrainz = "0.7.0"
|
listenbrainz = "0.7.0"
|
||||||
discord-rpc-client = "0.4.0"
|
discord-rpc-client = "0.4.0"
|
||||||
nestify = "0.3.3"
|
nestify = "0.3.3"
|
||||||
kushi = "0.1.2"
|
kushi = "0.1.3"
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub mod music_storage {
|
||||||
pub mod music_controller {
|
pub mod music_controller {
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
pub mod connections;
|
pub mod connections;
|
||||||
// pub mod queue;
|
pub mod queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod music_player {
|
pub mod music_player {
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
|
|
||||||
use crossbeam_channel;
|
use crossbeam_channel;
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
use kushi::error::QueueError;
|
use kushi::QueueError;
|
||||||
use kushi::Location;
|
|
||||||
use kushi::{Queue, QueueItemType};
|
use kushi::{Queue, QueueItemType};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
@ -18,13 +17,15 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::config::ConfigError;
|
use crate::config::ConfigError;
|
||||||
use crate::music_player::player::{Player, PlayerCommand, PlayerError};
|
use crate::music_player::player::{Player, PlayerCommand, PlayerError};
|
||||||
use crate::music_storage::library::{Album, Song};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config, music_storage::library::MusicLibrary,
|
config::Config, music_storage::library::MusicLibrary,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::queue::{QueueAlbum, QueueSong};
|
||||||
|
|
||||||
|
|
||||||
pub struct Controller<P: Player + Send + Sync> {
|
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 config: Arc<RwLock<Config>>,
|
||||||
pub library: MusicLibrary,
|
pub library: MusicLibrary,
|
||||||
pub player: Arc<Mutex<P>>,
|
pub player: Arc<Mutex<P>>,
|
||||||
|
@ -51,8 +52,6 @@ pub enum PlayerLocation {
|
||||||
Custom,
|
Custom,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Location for PlayerLocation {}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct MailMan<T: Send, U: Send> {
|
pub(super) struct MailMan<T: Send, U: Send> {
|
||||||
pub tx: Sender<T>,
|
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 config_ = Arc::new(RwLock::from(config));
|
||||||
let library = MusicLibrary::init(config_.clone(), uuid)?;
|
let library = MusicLibrary::init(config_.clone(), uuid)?;
|
||||||
|
|
||||||
let queue: Queue<Song, Album, PlayerLocation> = Queue {
|
let queue: Queue<QueueSong, QueueAlbum> = Queue {
|
||||||
items: Vec::new(),
|
items: Vec::new(),
|
||||||
played: Vec::new(),
|
played: Vec::new(),
|
||||||
loop_: false,
|
loop_: false,
|
||||||
|
@ -135,7 +134,7 @@ impl<P: Player + Send + Sync + Sized + 'static> Controller<P> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.enqueue_next(&{
|
.enqueue_next(&{
|
||||||
match uri.item {
|
match uri.item {
|
||||||
QueueItemType::Single(song) => song.primary_uri().unwrap().0.clone(),
|
QueueItemType::Single(song) => song.song.primary_uri().unwrap().0.clone(),
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -152,9 +151,9 @@ impl<P: Player + Send + Sync + Sized + 'static> Controller<P> {
|
||||||
Ok(controller)
|
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();
|
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 {
|
mod test_super {
|
||||||
use std::{thread::sleep, time::Duration};
|
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;
|
use super::Controller;
|
||||||
|
|
||||||
|
@ -177,7 +176,7 @@ mod test_super {
|
||||||
{
|
{
|
||||||
let mut queue = controller.queue.write().unwrap();
|
let mut queue = controller.queue.write().unwrap();
|
||||||
for x in config.1.library {
|
for x in config.1.library {
|
||||||
queue.add_item(x, Some(PlayerLocation::Library), true);
|
queue.add_item(QueueSong { song: x, location: PlayerLocation::Library }, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,407 +1,25 @@
|
||||||
use crate::music_storage::library::Song;
|
use std::vec::IntoIter;
|
||||||
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 crate::music_storage::library::{Album, AlbumTrack, Song};
|
||||||
|
|
||||||
|
use super::controller::PlayerLocation;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[non_exhaustive]
|
pub struct QueueSong {
|
||||||
pub struct QueueItem {
|
pub song: Song,
|
||||||
pub(super) item: Song,
|
pub location: PlayerLocation,
|
||||||
pub(super) state: QueueState,
|
|
||||||
pub(super) source: PlayerLocation,
|
|
||||||
pub(super) by_human: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QueueItem {
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
fn from_song(song: Song, source: PlayerLocation) -> Self {
|
pub struct QueueAlbum {
|
||||||
QueueItem {
|
pub album: Album,
|
||||||
item: song,
|
pub location: PlayerLocation,
|
||||||
state: QueueState::NoState,
|
|
||||||
source,
|
|
||||||
by_human: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
impl IntoIterator for QueueAlbum {
|
||||||
pub struct Queue {
|
type Item = AlbumTrack;
|
||||||
pub items: Vec<QueueItem>,
|
type IntoIter = IntoIter<Self::Item>;
|
||||||
pub played: Vec<QueueItem>,
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
pub loop_: bool,
|
self.album.into_iter()
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue