use std::time::Duration;
use config::MusicService;
use dioxus::prelude::*;
use reader::Track;
use crate::use_player_controller::PlayerController;
impl PlayerController {
pub(crate) fn spotify_access(&self) -> Option<String> {
let cfg = self.config.peek();
let server = cfg.server.as_ref()?;
if server.service != MusicService::Spotify {
return None;
}
let packed = server.access_token.clone()?;
let access = ::server::spotify::auth::unpack_token(&packed).0;
(!access.is_empty()).then_some(access)
}
fn ensure_spotify_host(&mut self) {
if self.spotify_host.peek().is_some() || *self.spotify_host_starting.peek() {
return;
}
let Some(access) = self.spotify_access() else {
return;
};
let browser = self.config.peek().spotify_browser.clone();
self.spotify_host_starting.set(true);
let mut host_sig = self.spotify_host;
let mut starting = self.spotify_host_starting;
let mut error = self.playback_error;
let mut external = self.external_active;
let mut playing = self.is_playing;
let mut pending = self.spotify_pending_uri;
spawn(async move {
match ::server::spotify::host::SpotifyHost::start(access, browser).await {
Ok(host) => host_sig.set(Some(host)),
Err(e) => {
tracing::warn!(error = %e, "spotify host failed to start");
error.set(Some(e));
if *external.peek() {
external.set(false);
playing.set(false);
pending.set(None);
}
}
}
starting.set(false);
});
}
pub(crate) fn spotify_browser_disconnected(&mut self) {
self.spotify_host.set(None);
self.spotify_device.set(None);
self.spotify_activated.set(false);
if self.spotify_device_override.peek().is_some() {
return;
}
self.spotify_pending_uri.set(None);
self.spotify_commanded.set(None);
if let Some(task) = self.spotify_start_task.take() {
task.cancel();
}
self.spotify_progress_anchor.set(None);
self.external_active.set(false);
self.is_playing.set(false);
}
pub fn spotify_access_token(&self) -> Option<String> {
self.spotify_access()
}
pub fn spotify_select_device(&mut self, device_id: Option<String>) {
self.spotify_device_chosen.set(true);
self.spotify_device_override.set(device_id.clone());
if !*self.external_active.peek() {
return;
}
let Some(access) = self.spotify_access() else {
return;
};
let Some(target) = device_id.or_else(|| self.spotify_device.peek().clone()) else {
return;
};
let play = *self.is_playing.peek();
spawn(async move {
if let Err(e) = ::server::spotify::api::transfer_playback(&access, &target, play).await
{
tracing::warn!(error = %e, "spotify device transfer failed");
}
});
}
pub fn spotify_adopt_external(&mut self, device_id: String) {
if *self.spotify_device_chosen.peek()
|| self.spotify_device_override.peek().is_some()
|| !self.config.peek().spotify_prefer_active_device
{
return;
}
self.spotify_device_override.set(Some(device_id));
self.external_active.set(true);
}
pub(crate) fn spotify_transport_pause(&mut self) {
if self.spotify_device_override.peek().is_some() {
if let Some(access) = self.spotify_access() {
spawn(async move {
let _ = ::server::spotify::api::player_pause(&access).await;
});
}
} else if let Some(host) = self.spotify_host.peek().clone() {
host.pause();
}
}
pub(crate) fn spotify_transport_resume(&mut self) {
if self.spotify_device_override.peek().is_some() {
if let Some(access) = self.spotify_access() {
spawn(async move {
let _ = ::server::spotify::api::player_resume(&access).await;
});
}
} else if let Some(host) = self.spotify_host.peek().clone() {
host.resume();
}
}
pub(crate) fn start_spotify_uri(&mut self, access: String, device: String, uri: String) {
if let Some(task) = self.spotify_start_task.take() {
task.cancel();
}
let mut error = self.playback_error;
let task = spawn(async move {
tokio::time::sleep(Duration::from_millis(125)).await;
if let Err(e) = ::server::spotify::api::start_playback(&access, &device, &[uri]).await {
tracing::warn!(error = %e, "spotify start_playback failed");
error.set(Some(e));
}
});
self.spotify_start_task.set(Some(task));
}
pub(crate) fn spotify_report_is_stale(&mut self, reported: Option<&str>) -> bool {
let Some((commanded, at)) = self.spotify_commanded.peek().clone() else {
return false;
};
if at.elapsed() > Duration::from_secs(3) || reported == Some(commanded.as_str()) {
self.spotify_commanded.set(None);
return false;
}
true
}
pub(super) fn spotify_play(&mut self, item_id: &str, track: &Track) {
let uri = format!("spotify:track:{item_id}");
self.spotify_commanded
.set(Some((item_id.to_string(), std::time::Instant::now())));
let override_device = self.spotify_device_override.peek().clone();
if let Some(device) = override_device {
if let Some(access) = self.spotify_access() {
self.spotify_pending_uri.set(None);
self.start_spotify_uri(access, device, uri);
}
return;
}
self.ensure_spotify_host();
if let Some(host) = self.spotify_host.peek().clone() {
let artwork = self.current_song_cover_url.peek().clone();
host.set_now_playing(track, &artwork);
}
if !*self.spotify_activated.peek() {
self.spotify_pending_uri.set(Some(uri));
return;
}
let sdk_device = self.spotify_device.peek().clone();
match (self.spotify_access(), sdk_device) {
(Some(access), Some(device)) => {
self.spotify_pending_uri.set(None);
self.start_spotify_uri(access, device, uri);
}
_ => self.spotify_pending_uri.set(Some(uri)),
}
}
pub(crate) fn stop_external_playback(&mut self) {
if !*self.external_active.peek() {
return;
}
self.spotify_pending_uri.set(None);
self.spotify_commanded.set(None);
if let Some(task) = self.spotify_start_task.take() {
task.cancel();
}
self.spotify_transport_pause();
self.external_active.set(false);
}
}