Implement moving to the next track
This commit is contained in:
parent
07618676db
commit
2fd2079330
1 changed files with 89 additions and 13 deletions
102
src/queue.rs
102
src/queue.rs
|
@ -1,10 +1,15 @@
|
|||
//! Implements a queue for the bot to play songs in order
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use parking_lot::Mutex;
|
||||
use poise::async_trait;
|
||||
use songbird::{
|
||||
events::EventData,
|
||||
input::Input,
|
||||
tracks::{self, TrackHandle},
|
||||
Driver,
|
||||
Driver, Event, EventContext, EventHandler, TrackEvent,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -14,19 +19,69 @@ pub enum QueueError {
|
|||
EmptyQueue,
|
||||
}
|
||||
|
||||
/// Event handler for the queue
|
||||
struct QueueHandler {
|
||||
remote_lock: Arc<Mutex<QueueCore>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for QueueHandler {
|
||||
async fn act(&self, ctx: &EventContext<'_>) -> Option<Event> {
|
||||
let mut inner = self.remote_lock.lock();
|
||||
|
||||
// Due to possibility that users might remove, reorder,
|
||||
// or dequeue+stop tracks, we need to verify that the FIRST
|
||||
// track is the one who has ended.
|
||||
match ctx {
|
||||
EventContext::Track(ts) => {
|
||||
// This slice should have exactly one entry.
|
||||
// If the ended track has same id as the queue head, then
|
||||
// we can progress the queue.
|
||||
if inner.tracks.first()?.uuid() != ts.first()?.1.uuid() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
let _old = inner.tracks.remove(0);
|
||||
|
||||
// Keep going until we find one track which works, or we run out.
|
||||
while let Some(new) = inner.tracks.first() {
|
||||
if new.play().is_err() {
|
||||
// Discard files which cannot be used for whatever reason.
|
||||
inner.tracks.remove(0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner queue data behind a Mutex to allow the event handler to access it
|
||||
struct QueueCore {
|
||||
tracks: Vec<TrackHandle>,
|
||||
}
|
||||
|
||||
/// A queue of tracks to play
|
||||
pub struct Queue {
|
||||
queue: Vec<TrackHandle>,
|
||||
inner: Arc<Mutex<QueueCore>>,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self { queue: Vec::new() }
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(QueueCore { tracks: Vec::new() })),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resumes the current track
|
||||
pub fn resume(&mut self) -> Result<()> {
|
||||
let Some(track) = self.queue.first() else {
|
||||
let inner = self.inner.lock();
|
||||
let Some(track) = inner.tracks.first() else {
|
||||
return Err(QueueError::EmptyQueue.into());
|
||||
};
|
||||
track.play()?;
|
||||
|
@ -35,17 +90,19 @@ impl Queue {
|
|||
|
||||
/// Stops the current track and removes it from the queue
|
||||
pub fn stop(&mut self) -> Result<()> {
|
||||
let Some(track) = self.queue.first() else {
|
||||
let mut inner = self.inner.lock();
|
||||
let Some(track) = inner.tracks.first() else {
|
||||
return Err(QueueError::EmptyQueue.into());
|
||||
};
|
||||
track.stop()?;
|
||||
self.queue.remove(0);
|
||||
inner.tracks.remove(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pauses the current track
|
||||
pub fn pause(&mut self) -> Result<()> {
|
||||
let Some(track) = self.queue.first() else {
|
||||
let inner = self.inner.lock();
|
||||
let Some(track) = inner.tracks.first() else {
|
||||
return Err(QueueError::EmptyQueue.into());
|
||||
};
|
||||
track.pause()?;
|
||||
|
@ -54,9 +111,25 @@ impl Queue {
|
|||
|
||||
/// Adds a track to the end of the queue
|
||||
pub fn add_to_end(&mut self, source: Input, handler: &mut Driver) -> TrackHandle {
|
||||
let mut inner = self.inner.lock();
|
||||
let (mut track, handle) = tracks::create_player(source);
|
||||
track.pause();
|
||||
self.queue.push(handle.clone());
|
||||
let position = track.position();
|
||||
// Event to remove the track from the queue when it ends
|
||||
track
|
||||
.events
|
||||
.as_mut()
|
||||
.expect("Why is this even an Option?")
|
||||
.add_event(
|
||||
EventData::new(
|
||||
Event::Track(TrackEvent::End),
|
||||
QueueHandler {
|
||||
remote_lock: self.inner.clone(),
|
||||
},
|
||||
),
|
||||
position,
|
||||
);
|
||||
inner.tracks.push(handle.clone());
|
||||
handler.play(track);
|
||||
handle
|
||||
}
|
||||
|
@ -76,12 +149,13 @@ impl Queue {
|
|||
|
||||
/// Adds a track to play next
|
||||
pub fn add_next(&mut self, source: Input, handler: &mut Driver) -> TrackHandle {
|
||||
let mut inner = self.inner.lock();
|
||||
let (mut track, handle) = tracks::create_player(source);
|
||||
track.pause();
|
||||
if self.queue.is_empty() {
|
||||
self.queue.push(handle.clone());
|
||||
if inner.tracks.is_empty() {
|
||||
inner.tracks.push(handle.clone());
|
||||
} else {
|
||||
self.queue.insert(1, handle.clone());
|
||||
inner.tracks.insert(1, handle.clone());
|
||||
}
|
||||
handler.play(track);
|
||||
handle
|
||||
|
@ -89,7 +163,8 @@ impl Queue {
|
|||
|
||||
/// Clears the queue
|
||||
pub fn clear(&mut self) {
|
||||
for track in self.queue.drain(..) {
|
||||
let mut inner = self.inner.lock();
|
||||
for track in inner.tracks.drain(..) {
|
||||
let _ = track.stop();
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +172,7 @@ impl Queue {
|
|||
/// Returns whether the queue is empty
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.queue.is_empty()
|
||||
let inner = self.inner.lock();
|
||||
inner.tracks.is_empty()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue