spotify_client/client/
spotify.rs1use anyhow::{anyhow, Result};
2use librespot_core::session::Session;
3use maybe_async::maybe_async;
4use rspotify::{
5 clients::{BaseClient, OAuthClient},
6 http::HttpClient,
7 sync::Mutex,
8 ClientResult, Config, Credentials, OAuth, Token,
9};
10use std::{fmt, sync::Arc};
11
12use crate::token;
13
14#[derive(Clone, Default)]
15pub struct Spotify {
17 creds: Credentials,
18 oauth: OAuth,
19 config: Config,
20 token: Arc<Mutex<Option<Token>>>,
21 client_id: String,
22 http: HttpClient,
23 pub(crate) session: Arc<tokio::sync::Mutex<Option<Session>>>,
26}
27
28impl fmt::Debug for Spotify {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 f.debug_struct("Spotify")
31 .field("creds", &self.creds)
32 .field("oauth", &self.oauth)
33 .field("config", &self.config)
34 .field("token", &self.token)
35 .field("client_id", &self.client_id)
36 .finish()
37 }
38}
39
40impl Spotify {
41 pub fn new(session: Session, client_id: String) -> Spotify {
43 Self {
44 creds: Credentials::default(),
45 oauth: OAuth::default(),
46 config: Config {
47 token_refreshing: true,
48 ..Default::default()
49 },
50 token: Arc::new(Mutex::new(None)),
51 http: HttpClient::default(),
52 session: Arc::new(tokio::sync::Mutex::new(Some(session))),
53 client_id,
54 }
55 }
56
57 pub async fn session(&self) -> Session {
58 self.session
59 .lock()
60 .await
61 .clone()
62 .expect("non-empty Spotify session")
63 }
64
65 pub async fn access_token(&self) -> Result<String> {
69 let should_update = match self.token.lock().await.unwrap().as_ref() {
70 Some(token) => token.is_expired(),
71 None => true,
72 };
73 if should_update {
74 self.refresh_token().await?;
75 }
76
77 match self.token.lock().await.unwrap().as_ref() {
78 Some(token) => Ok(token.access_token.clone()),
79 None => Err(anyhow!(
80 "failed to get the authentication token stored inside the client."
81 )),
82 }
83 }
84}
85
86#[maybe_async]
90impl BaseClient for Spotify {
91 fn get_http(&self) -> &HttpClient {
92 &self.http
93 }
94
95 fn get_token(&self) -> Arc<Mutex<Option<Token>>> {
96 Arc::clone(&self.token)
97 }
98
99 fn get_creds(&self) -> &Credentials {
100 &self.creds
101 }
102
103 fn get_config(&self) -> &Config {
104 &self.config
105 }
106
107 async fn refetch_token(&self) -> ClientResult<Option<Token>> {
108 let session = self.session().await;
109 let old_token = self.token.lock().await.unwrap().clone();
110
111 if session.is_invalid() {
112 tracing::error!("Failed to get a new token: invalid session");
113 return Ok(old_token);
114 }
115
116 match token::get_token(&session, &self.client_id).await {
117 Ok(token) => Ok(Some(token)),
118 Err(err) => {
119 tracing::error!("Failed to get a new token: {err:#}");
120 Ok(old_token)
121 }
122 }
123 }
124}
125
126#[maybe_async]
135impl OAuthClient for Spotify {
136 fn get_oauth(&self) -> &OAuth {
137 panic!("`OAuthClient::get_oauth` should never be called!")
138 }
139
140 async fn request_token(&self, _code: &str) -> ClientResult<()> {
141 panic!("`OAuthClient::request_token` should never be called!")
142 }
143}