1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
use crate::database;
use crate::errors::Error;
use lazy_static::lazy_static;
use rspotify::spotify::client::{ApiError, Spotify};
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: 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 {
Ok(mut items) => {
$index += CHUNK_SIZE;
if items.items.is_empty() {
break;
}
result.append(&mut 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;
}
},
}
}
result
}};
}
use rspotify::spotify::model::playlist::*;
use rspotify::spotify::model::track::*;
use rspotify::spotify::senum::TimeRange;
pub 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) {
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) {
println!("failed to load track to db: {:?}", e)
};
}
for playlist in playlists {
let tracks = get_items!(
PlaylistTrack,
index,
spotify.user_playlist_tracks(
spotify_uid.as_ref(),
&playlist.id,
None,
CHUNK_SIZE,
index,
None,
)
);
for track in tracks {
if let Err(e) = database::insert_track(db_uid, track.track, 1) {
println!("failed to load track to db: {:?}", e)
};
}
}
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)
}
|