summaryrefslogtreecommitdiff
path: root/src/spotify.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/spotify.rs')
-rw-r--r--src/spotify.rs172
1 files changed, 172 insertions, 0 deletions
diff --git a/src/spotify.rs b/src/spotify.rs
new file mode 100644
index 0000000..b9a1147
--- /dev/null
+++ b/src/spotify.rs
@@ -0,0 +1,172 @@
+use crate::database;
+use crate::errors::Error;
+use lazy_static::lazy_static;
+use rspotify::spotify::client::{ApiError, Spotify};
+use rspotify::spotify::model::playlist::FullPlaylist;
+use rspotify::spotify::oauth2::{SpotifyClientCredentials, SpotifyOAuth};
+use rspotify::spotify::util::process_token;
+use std::collections::HashMap;
+use std::sync::{Arc, Mutex};
+
+lazy_static! {
+ static ref CACHE: Arc<Mutex<HashMap<String, SpotifyOAuth>>> =
+ Arc::new(Mutex::new(HashMap::new()));
+}
+
+static CHUNK_SIZE: i32 = 50;
+
+macro_rules! get_items {
+ ($func_name:ident, $spotify_call:stmt, $t: ty) => {
+ fn $func_name(spotify_uid: &str, spotify: &mut Spotify) -> Result<Vec<$t>, Error> {
+ let mut index = 0;
+ let mut result = Vec::new();
+ loop {
+ match $spotify_call {
+ Ok(items) => {
+ index += CHUNK_SIZE;
+ if items.items.is_empty() {
+ break;
+ }
+ result.append(items.items);
+ }
+ Err(e) => match e.downcast::<ApiError>() {
+ Ok(ApiError::RateLimited(x)) => std::thread::sleep(
+ std::time::Duration::from_secs(x.unwrap_or(5) as u64),
+ ),
+
+ cause => {
+ println!("Error: {:?}", cause);
+ break;
+ }
+ },
+ }
+ }
+ Ok((result))
+ }
+ };
+}
+
+get_items!(
+ playlists,
+ spotify.user_playlists(spotify_uid.as_ref(), Some(CHUNK_SIZE), Some(playlist_index)),
+ FullPlaylist
+);
+fn load_playlist(spotify_uid: &str, spotify: &mut Spotify) -> Result<Vec<FullPlaylist>, Error> {
+ let mut playlist_index = 0;
+ loop {
+ match spotify.user_playlists(spotify_uid.as_ref(), Some(CHUNK_SIZE), Some(playlist_index)) {
+ Ok(playlists) => {
+ playlist_index += chunk_size;
+ if playlists.items.is_empty() {
+ break;
+ }
+ for playlist in playlists.items {}
+ }
+ Err(e) => match e.downcast::<ApiError>() {
+ Ok(ApiError::RateLimited(x)) => {
+ std::thread::sleep(std::time::Duration::from_secs(x.unwrap_or(5) as u64))
+ }
+
+ cause => {
+ println!("Error: {:?}", cause);
+ break;
+ }
+ },
+ }
+ }
+ Ok(())
+}
+
+pub fn load_profile(db_uid: i32, spotify_uid: &str, spotify: Spotify) -> Result<(), Error> {
+ let mut playlist_index = 0;
+ loop {
+ match spotify.user_playlists(spotify_uid.as_ref(), Some(chunk_size), Some(playlist_index)) {
+ Ok(playlists) => {
+ playlist_index += chunk_size;
+ if playlists.items.is_empty() {
+ break;
+ }
+ for playlist in playlists.items {
+ println!("playlist: {:?}", playlist.name);
+ let mut track_index = 0;
+
+ loop {
+ match spotify.user_playlist_tracks(
+ spotify_uid.as_ref(),
+ &playlist.id,
+ None,
+ Some(chunk_size),
+ Some(track_index),
+ None,
+ ) {
+ Ok(tracks) => {
+ track_index += chunk_size;
+ if tracks.items.is_empty() {
+ break;
+ }
+ for track in tracks.items {
+ if let Err(e) = insert_track(uid, track.track) {
+ println!("failed to load track to db: {:?}", e)
+ };
+ }
+ }
+ Err(e) => match e.downcast::<ApiError>() {
+ Ok(ApiError::RateLimited(x)) => std::thread::sleep(
+ std::time::Duration::from_secs(x.unwrap_or(5) as u64),
+ ),
+
+ cause => {
+ println!("Error: {:?}", cause);
+ break;
+ }
+ },
+ }
+ }
+ }
+ }
+ Err(e) => match e.downcast::<ApiError>() {
+ Ok(ApiError::RateLimited(x)) => {
+ std::thread::sleep(std::time::Duration::from_secs(x.unwrap_or(5) as u64))
+ }
+
+ cause => {
+ println!("Error: {:?}", cause);
+ break;
+ }
+ },
+ }
+ }
+ Ok(())
+}
+
+pub fn auth_user(name: &str, url: String) -> Result<(String, Spotify), Error> {
+ let mut guard = CACHE.lock()?;
+ let mut oauth = guard.remove(name)?;
+ println!("auth: {:?} url: {}", oauth, url);
+ let token_info = process_token(&mut oauth, &mut ("?code=".to_owned() + url.as_ref()));
+ let client_credential = SpotifyClientCredentials::default()
+ .token_info(token_info?)
+ .build();
+
+ let spotify = Spotify::default()
+ .client_credentials_manager(client_credential)
+ .build();
+ let user_id = spotify
+ .current_user()
+ .map_err(|e| format!("failed to load currentuser {:?}", e))?
+ .id;
+ Ok((user_id, spotify))
+}
+
+#[get("/token/<name>")]
+pub fn token(name: String) -> Result<String, Error> {
+ let state = rspotify::spotify::util::generate_random_string(16);
+ let oauth = SpotifyOAuth::default();
+ let oauth = oauth
+ .scope("playlist-read-private, playlist-read-collaborative, user-read-private, user-follow-read, user-library-read")
+ .build();
+ let auth_url = oauth.get_authorize_url(Some(&state), None);
+ let mut guard = CACHE.lock()?;
+ guard.insert(name, oauth);
+ Ok(auth_url)
+}