use std::{
collections::VecDeque,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
};
use lunar_lib::{database::Entry, iterator_ext::IteratorExtensions};
use rand::seq::SliceRandom;
use selene_core::library::track::Track;
use crate::{
LoopMode, ShuffleMode,
playlist::{Playable, PlayingTrack, Playlist, PlaylistRng},
};
impl Playlist {
pub(crate) fn new(looping: Arc<AtomicBool>) -> Self {
Self {
rng: PlaylistRng::new(),
queue: VecDeque::new(),
playlist: Vec::new(),
tracklist: Vec::new(),
tracklist_index: None,
shuffle_mode: ShuffleMode::None,
loop_mode: LoopMode::None,
looping,
}
}
pub(crate) fn set_shuffle_mode(&mut self, shuffle_mode: ShuffleMode) {
self.shuffle_mode = shuffle_mode;
self.rebuild_tracklist();
}
pub(crate) fn set_loop_mode(&mut self, loop_mode: LoopMode) {
self.loop_mode = loop_mode;
self.looping.store(
matches!(loop_mode, LoopMode::RepeatTrack),
Ordering::Relaxed,
);
}
}
impl Playlist {
#[must_use]
pub(crate) fn playlist(&self) -> &[Playable] {
&self.playlist
}
pub(crate) fn set_playlist(&mut self, playlist: impl IntoIterator<Item = Playable>) {
self.playlist = playlist.into_iter().collect();
self.rebuild_tracklist();
}
pub(crate) fn extend_playlist(&mut self, items: impl IntoIterator<Item = Playable>) {
let items: Vec<Playable> = items.into_iter().collect();
let mut rng = self.rng.current_rng();
let mut tracklist_items = items
.iter()
.flat_map(|p| p.flatten_shuffle(self.shuffle_mode, &mut rng))
.to_vec();
if matches!(self.shuffle_mode, ShuffleMode::Full) {
tracklist_items.shuffle(&mut rng);
}
self.playlist.extend(items);
self.tracklist.extend(tracklist_items);
self.rng = PlaylistRng::new();
}
}
impl Playlist {
pub(crate) fn queue(&self) -> &VecDeque<Arc<Entry<Track>>> {
&self.queue
}
pub(crate) fn set_queue<'a>(&mut self, items: impl IntoIterator<Item = &'a Playable>) {
self.queue = items.into_iter().flat_map(Playable::flatten).collect();
}
pub(crate) fn shuffle_queue(&mut self) {
let mut queue: Vec<Arc<Entry<Track>>> = std::mem::take(&mut self.queue).into();
queue.shuffle(&mut rand::rng());
self.queue = queue.into();
}
pub(crate) fn clear_queue(&mut self) {
self.queue.clear();
}
pub(crate) fn extend_queue<'a>(&mut self, with: impl IntoIterator<Item = &'a Playable>) {
self.queue
.extend(with.into_iter().flat_map(Playable::flatten));
}
}
impl Playlist {
#[must_use]
pub(crate) fn position(&self) -> Option<u32> {
self.tracklist_index
}
#[must_use]
pub(crate) fn shuffle_mode(&self) -> ShuffleMode {
self.shuffle_mode
}
#[must_use]
pub(crate) fn loop_mode(&self) -> LoopMode {
self.loop_mode
}
#[must_use]
pub(crate) fn tracklist(&self) -> &[Arc<Entry<Track>>] {
&self.tracklist
}
}
impl Playlist {
pub(crate) fn current(&mut self) -> Option<PlayingTrack> {
let index = self.position()?;
let track = PlayingTrack::from_tracklist(self.tracklist[index as usize].clone(), index);
Some(track)
}
fn peek_next_raw(&self) -> Option<(u32, bool)> {
if self.tracklist.is_empty() {
return None;
}
let len = self.tracklist.len() as u32;
let result = match self.loop_mode {
LoopMode::None | LoopMode::RepeatTrack => {
let next_i = self.tracklist_index.map_or(0, |i| i + 1);
if next_i >= len {
return None;
}
(next_i, false)
}
LoopMode::Loop => (self.tracklist_index.map_or(0, |i| (i + 1) % len), false),
LoopMode::LoopAndReshuffle => {
let next_i = self.tracklist_index.map_or(0, |i| i + 1);
(next_i % len, next_i >= len)
}
};
Some(result)
}
pub(crate) fn peek_next(&self) -> Option<PlayingTrack> {
if let Some(front) = self.queue.front().cloned() {
return Some(PlayingTrack::from_queue(front));
}
let (index, new_cycle) = self.peek_next_raw()?;
let track = if new_cycle {
let tracklist = self.build_tracklist(
&mut self
.rng
.find_cycle_rng(self.rng.current_cycle().wrapping_add(1)),
);
PlayingTrack::from_tracklist(tracklist[index as usize].clone(), index)
} else {
PlayingTrack::from_tracklist(self.tracklist[index as usize].clone(), index)
};
Some(track)
}
pub(crate) fn pop_next(&mut self) -> Option<PlayingTrack> {
if let Some(front) = self.queue.pop_front() {
return Some(PlayingTrack::from_queue(front));
}
let (index, new_cycle) = self.peek_next_raw()?;
let track = if new_cycle {
let mut rng = self.rng.increment();
let tracklist = self.build_tracklist(&mut rng);
let track = PlayingTrack::from_tracklist(tracklist[index as usize].clone(), index);
self.tracklist = tracklist;
track
} else {
PlayingTrack::from_tracklist(self.tracklist[index as usize].clone(), index)
};
self.tracklist_index = Some(self.tracklist_index.map_or(0, |i| i + 1));
Some(track)
}
pub(crate) fn tracklist_seek(&mut self, to: i32, increment: bool) -> Option<u32> {
let len = self.tracklist.len();
if len == 0 {
return None;
}
let i = if increment {
self.tracklist_index
.map_or(0, |i| (i as i32).saturating_add(to))
} else {
to.max(0)
};
self.tracklist_index = match self.loop_mode {
LoopMode::None | LoopMode::RepeatTrack => {
let i = i.clamp(0, len as i32) as u32;
if i >= len as u32 {
return None;
}
Some(i)
}
LoopMode::Loop => Some(i.rem_euclid(len as i32) as u32),
LoopMode::LoopAndReshuffle => {
let current_cycle = self.rng.current_cycle();
let cycles_delta = i.div_euclid(len as i32);
let new_cycle = current_cycle.wrapping_add_signed(i64::from(cycles_delta));
self.rng.set_cycle(new_cycle);
let mut rng = self.rng.find_cycle_rng(new_cycle);
self.shuffle_tracklist(&mut rng);
Some(i.rem_euclid(len as i32) as u32)
}
};
self.tracklist_index
}
}