feat: improve inevitable communication between frontend & backend

This commit is contained in:
talwat 2024-09-25 14:39:26 +02:00
parent 6e457241d2
commit e0d13792e2
2 changed files with 42 additions and 18 deletions

View File

@ -1,5 +1,3 @@
use std::process::exit;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
mod scrape; mod scrape;

View File

@ -1,10 +1,12 @@
use std::{collections::VecDeque, sync::Arc, time::Duration}; use std::{collections::VecDeque, sync::Arc};
use reqwest::Client; use reqwest::Client;
use rodio::{source::SineWave, Decoder, OutputStream, OutputStreamHandle, Sink, Source}; use rodio::{Decoder, OutputStream, Sink};
use tokio::{ use tokio::{
sync::RwLock, select, sync::{
task, time::sleep, mpsc::{self, Receiver},
RwLock,
}, task
}; };
/// The amount of songs to buffer up. /// The amount of songs to buffer up.
@ -12,6 +14,11 @@ const BUFFER_SIZE: usize = 5;
use crate::tracks::Track; use crate::tracks::Track;
/// Handles communication between the frontend & audio player.
pub enum Messages {
Skip,
}
/// Main struct responsible for queuing up tracks. /// Main struct responsible for queuing up tracks.
/// ///
/// Internally tracks are stored in an [Arc], /// Internally tracks are stored in an [Arc],
@ -21,9 +28,6 @@ pub struct Queue {
tracks: Arc<RwLock<VecDeque<Track>>>, tracks: Arc<RwLock<VecDeque<Track>>>,
} }
unsafe impl Send for Queue {}
unsafe impl Sync for Queue {}
impl Queue { impl Queue {
pub async fn new() -> Self { pub async fn new() -> Self {
Self { Self {
@ -57,28 +61,48 @@ impl Queue {
Ok(track) Ok(track)
} }
pub async fn play(self, sink: Sink) -> eyre::Result<()> { /// This is the main "audio server".
let client = Client::builder().build()?; ///
/// `rx` is used to communicate with it, for example when to
/// skip tracks or pause.
pub async fn play(
self,
sink: Sink,
client: Client,
mut rx: Receiver<Messages>
) -> eyre::Result<()> {
let sink = Arc::new(sink); let sink = Arc::new(sink);
loop { loop {
sink.stop(); let clone = sink.clone();
let msg = select! {
Some(x) = rx.recv() => x,
let track = self.next(&client).await?; // This future will finish only at the end of the current track.
sink.append(Decoder::new(track.data)?); Ok(()) = task::spawn_blocking(move || clone.sleep_until_end()) => Messages::Skip,
};
let sink = sink.clone(); match msg {
task::spawn_blocking(move || sink.sleep_until_end()).await?; Messages::Skip => {
sink.stop();
let track = self.next(&client).await?;
sink.append(Decoder::new(track.data)?);
}
}
} }
} }
} }
pub async fn play() -> eyre::Result<()> { pub async fn play() -> eyre::Result<()> {
let queue = Queue::new().await; let queue = Queue::new().await;
let (stream, handle) = OutputStream::try_default()?; let (tx, rx) = mpsc::channel(8);
let (_stream, handle) = OutputStream::try_default()?;
let sink = Sink::try_new(&handle)?; let sink = Sink::try_new(&handle)?;
let client = Client::builder().build()?;
let audio = task::spawn(queue.clone().play(sink)); let audio = task::spawn(queue.clone().play(sink, client.clone(), rx));
tx.send(Messages::Skip).await?; // This is responsible for the initial track being played.
crossterm::terminal::enable_raw_mode()?; crossterm::terminal::enable_raw_mode()?;
@ -88,6 +112,8 @@ pub async fn play() -> eyre::Result<()> {
crossterm::event::KeyCode::Char(x) => { crossterm::event::KeyCode::Char(x) => {
if x == 'q' { if x == 'q' {
break 'a; break 'a;
} else if x == 's' {
tx.send(Messages::Skip).await?;
} }
} }
_ => (), _ => (),