summaryrefslogtreecommitdiff
path: root/src/spotify.rs
blob: 596dec35bc14fd8ccd47a33ad9960c52b046dc01 (plain)
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)
}