Hello there I'm back
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
pub mod stream;
|
||||
|
||||
use librespot::{
|
||||
connect::spirc::Spirc,
|
||||
core::{config::ConnectConfig, session::Session},
|
||||
discovery::Credentials,
|
||||
playback::{
|
||||
config::{Bitrate, PlayerConfig, VolumeCtrl},
|
||||
mixer::{self, MixerConfig},
|
||||
player::{Player as SpotifyPlayer, PlayerEvent},
|
||||
},
|
||||
};
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
|
||||
use crate::{
|
||||
audio::{SinkEvent, StreamSink},
|
||||
librespot_ext::discovery::CredentialsExt,
|
||||
utils,
|
||||
};
|
||||
|
||||
use self::stream::Stream;
|
||||
|
||||
pub struct Player {
|
||||
stream: Stream,
|
||||
session: Option<Session>,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn create() -> Self {
|
||||
Self {
|
||||
stream: Stream::new(),
|
||||
session: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(
|
||||
&mut self,
|
||||
token: &str,
|
||||
device_name: &str,
|
||||
) -> Result<
|
||||
(
|
||||
Spirc,
|
||||
(UnboundedReceiver<PlayerEvent>, UnboundedReceiver<SinkEvent>),
|
||||
),
|
||||
Box<dyn std::error::Error>,
|
||||
> {
|
||||
let username = utils::spotify::get_username(token).await?;
|
||||
|
||||
let player_config = PlayerConfig {
|
||||
bitrate: Bitrate::Bitrate96,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let credentials = Credentials::with_token(username, token);
|
||||
|
||||
// Shutdown old session (cannot be done in the stop function)
|
||||
if let Some(session) = self.session.take() {
|
||||
session.shutdown()
|
||||
}
|
||||
|
||||
// Connect the session
|
||||
let (session, _) = Session::connect(Default::default(), credentials, None, false).await?;
|
||||
self.session = Some(session.clone());
|
||||
|
||||
let mixer = (mixer::find(Some("softvol")).expect("to exist"))(MixerConfig {
|
||||
volume_ctrl: VolumeCtrl::Linear,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let stream = self.get_stream();
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let (player, receiver) = SpotifyPlayer::new(
|
||||
player_config,
|
||||
session.clone(),
|
||||
mixer.get_soft_volume(),
|
||||
move || Box::new(StreamSink::new(stream, tx)),
|
||||
);
|
||||
|
||||
let (spirc, spirc_task) = Spirc::new(
|
||||
ConnectConfig {
|
||||
name: device_name.into(),
|
||||
// 50%
|
||||
initial_volume: Some(65535 / 2),
|
||||
..Default::default()
|
||||
},
|
||||
session.clone(),
|
||||
player,
|
||||
mixer,
|
||||
);
|
||||
|
||||
tokio::spawn(spirc_task);
|
||||
|
||||
Ok((spirc, (receiver, rx)))
|
||||
}
|
||||
|
||||
pub fn get_stream(&self) -> Stream {
|
||||
self.stream.clone()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
use std::{
|
||||
io::{Read, Seek, Write},
|
||||
sync::{Arc, Condvar, Mutex},
|
||||
};
|
||||
|
||||
use songbird::input::reader::MediaSource;
|
||||
|
||||
const MAX_SIZE: usize = 1 * 1024 * 1024;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Stream {
|
||||
inner: Arc<(Mutex<Vec<u8>>, Condvar)>,
|
||||
}
|
||||
|
||||
impl Stream {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: Arc::new((Mutex::new(Vec::new()), Condvar::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Stream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let (mutex, condvar) = &*self.inner;
|
||||
let mut buffer = mutex.lock().expect("Mutex was poisoned");
|
||||
|
||||
log::trace!("Read!");
|
||||
|
||||
while buffer.is_empty() {
|
||||
buffer = condvar.wait(buffer).expect("Mutex was poisoned");
|
||||
}
|
||||
|
||||
let max_read = usize::min(buf.len(), buffer.len());
|
||||
buf[0..max_read].copy_from_slice(&buffer[0..max_read]);
|
||||
buffer.drain(0..max_read);
|
||||
|
||||
Ok(max_read)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Stream {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let (mutex, condvar) = &*self.inner;
|
||||
let mut buffer = mutex.lock().expect("Mutex was poisoned");
|
||||
|
||||
while buffer.len() + buf.len() > MAX_SIZE {
|
||||
buffer = condvar.wait(buffer).unwrap();
|
||||
}
|
||||
|
||||
buffer.extend_from_slice(buf);
|
||||
condvar.notify_all();
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for Stream {
|
||||
fn seek(&mut self, _: std::io::SeekFrom) -> std::io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MediaSource for Stream {
|
||||
fn byte_len(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
fn is_seekable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user