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>> = 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, 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::() { 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, 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::() { 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::() { 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::() { 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/")] pub fn token(name: String) -> Result { 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) }