use crate::database; use crate::errors::Error; use lazy_static::lazy_static; use rand::{Rng, RngCore}; use rspotify::client::{ClientError, Spotify}; use rspotify::model::enums::TimeRange; use rspotify::model::playlist::*; use rspotify::model::track::*; use std::collections::HashMap; use std::sync::{Arc, Mutex}; lazy_static! { static ref CACHE: Arc>> = Arc::new(Mutex::new(HashMap::new())); } static CHUNK_SIZE: u32 = 50; macro_rules! get_items { ($t: ty, $index:ident, $spotify_call:expr) => {{ $index = 0; let mut result: Vec<$t> = Vec::new(); loop { match $spotify_call.await { Ok(mut page) => { $index += CHUNK_SIZE; if page.items.is_empty() { break; } result.append(&mut page.items); } Err(e) => match e { ClientError::RateLimited(x) => { std::thread::sleep(std::time::Duration::from_secs(x.unwrap_or(5) as u64)) } cause => { println!("Error: {:?}", cause); break; } }, } } result }}; } pub async fn load_profile(db_uid: i32, spotify_uid: &str, spotify: Spotify) -> Result<(), Error> { let mut index; let playlists = get_items!( SimplifiedPlaylist, index, spotify.current_user_playlists(CHUNK_SIZE, index) ); let library = get_items!( SavedTrack, index, spotify.current_user_saved_tracks(CHUNK_SIZE, index) ); let top_tracks = get_items!( FullTrack, index, spotify.current_user_top_tracks(CHUNK_SIZE, index, TimeRange::MediumTerm) ); for track in library { if let Err(e) = database::insert_track(db_uid, track.track, 5).await { println!("failed to load track to db: {:?}", e) }; } for (pos, track) in top_tracks.iter().enumerate() { let weight = ((50.0 - pos as f64) / 50.0 * 10.0).floor() as i32; if let Err(e) = database::insert_track(db_uid, track.clone(), 5 + weight).await { println!("failed to load track to db: {:?}", e) }; } let playlists = playlists.iter().map(|x| x.clone()); for playlist in playlists { let tracks = get_items!( PlaylistItem, index, spotify.playlist_tracks( //spotify_uid.as_ref(), &playlist.id, None, CHUNK_SIZE, index, None, ) ); let tracks: Vec = tracks.iter().map(|x| x.track.clone()).flatten().collect(); for track in tracks { if let Err(e) = database::insert_track(db_uid, track, 1).await { println!("failed to load track to db: {:?}", e) }; } } Ok(()) } use rspotify::client::SpotifyBuilder; use rspotify::oauth2::{CredentialsBuilder, OAuthBuilder}; /// Generate `length` random chars fn generate_random_uuid(length: usize) -> String { let alphanum: &[u8] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".as_bytes(); let mut buf = vec![0u8; length]; rand::thread_rng().fill_bytes(buf.as_mut_slice()); let range = alphanum.len(); buf.iter() .map(|byte| alphanum[*byte as usize % range] as char) .collect() } pub async fn auth_user(name: &str, code: &str) -> Result<(String, Spotify), Error> { let (user, mut spotify) = { let mut guard = (*CACHE).lock()?; guard.remove(name)? }; println!("auth: {:?} url: {}", name, code); spotify.request_user_token(code).await?; //let token_info = process_token(&mut oauth, &mut token_string); /*let client_credential = SpotifyClientCredentials::default() .token_info(token_info.await?) .build(); let spotify = Spotify::default() .client_credentials_manager(client_credential) .build();*/ /*let user_id = spotify .current_user() .await .map_err(|e| format!("failed to load currentuser {:?}", e))? .id;*/ Ok((user, spotify)) } pub fn token(name: String, token: String) -> Result { let scope = "playlist-read-private playlist-read-collaborative user-read-private user-follow-read user-library-read"; let oauth = OAuthBuilder::from_env() .scope(scope.split_whitespace().map(|x| x.to_owned()).collect()) .build() .unwrap(); let creds = CredentialsBuilder::from_env().build().unwrap(); let spotify = SpotifyBuilder::default() .credentials(creds) .oauth(oauth) .build() .unwrap(); let auth_url = spotify.get_authorize_url(false).unwrap(); //let token = spotify.token.as_ref().unwrap(); /*let state = rspotify::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(token, (name, spotify)); Ok(auth_url) }