[API-1] feat: search route
This commit is contained in:
parent
57588c7e13
commit
96a091f068
24 changed files with 388 additions and 127 deletions
|
@ -6,21 +6,26 @@ mod models;
|
|||
mod routes;
|
||||
mod schema;
|
||||
mod services;
|
||||
mod utils;
|
||||
|
||||
use crate::helpers::db;
|
||||
use actix_web::{web, App, HttpServer};
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
use actix_web::{web, App, HttpServer};
|
||||
|
||||
dotenvy::dotenv().expect("No .env file found");
|
||||
std::env::set_var("RUST_LOG", "debug");
|
||||
|
||||
// Register services.
|
||||
let _ = services::spotify::instance().await;
|
||||
|
||||
db::init();
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.service(web::scope("/playlists").service(routes::playlists::get_playlist))
|
||||
.service(routes::auth::login)
|
||||
.service(routes::auth::routes())
|
||||
.service(routes::me::routes())
|
||||
.service(routes::users::routes())
|
||||
.service(routes::search::routes())
|
||||
|
|
|
@ -9,6 +9,15 @@ pub struct ErrorResponse {
|
|||
pub status: StatusCode,
|
||||
}
|
||||
|
||||
impl ErrorResponse {
|
||||
pub fn new(message: &str, status: StatusCode) -> Self {
|
||||
ErrorResponse {
|
||||
message: message.to_string(),
|
||||
status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ErrorResponse {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
write!(f, "{}: {}", self.status, self.message)
|
||||
|
|
|
@ -19,23 +19,20 @@ pub fn get_user(req: HttpRequest) -> Result<Users, ErrorResponse> {
|
|||
}
|
||||
Err(e) => {
|
||||
return Err(match e.kind() {
|
||||
jsonwebtoken::errors::ErrorKind::ExpiredSignature => ErrorResponse {
|
||||
message: "Not Authorized".to_string(),
|
||||
status: StatusCode::UNAUTHORIZED,
|
||||
},
|
||||
_ => ErrorResponse {
|
||||
message: e.to_string(),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
},
|
||||
jsonwebtoken::errors::ErrorKind::ExpiredSignature => {
|
||||
ErrorResponse::new("Not Authorized", StatusCode::UNAUTHORIZED)
|
||||
}
|
||||
_ => ErrorResponse::new(
|
||||
e.to_string().as_str(),
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(ErrorResponse {
|
||||
message: "Not Authorized".to_string(),
|
||||
status: StatusCode::UNAUTHORIZED,
|
||||
});
|
||||
}
|
||||
None => Err(ErrorResponse::new(
|
||||
"Not Authorized",
|
||||
StatusCode::UNAUTHORIZED,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,10 @@ use serde::{Deserialize, Serialize};
|
|||
pub struct Artist {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub slug: String,
|
||||
|
||||
created_at: NaiveDateTime,
|
||||
updated_at: Option<NaiveDateTime>,
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
|
||||
pub spotify_id: Option<String>,
|
||||
pub tidal_id: Option<String>,
|
||||
|
@ -25,9 +26,10 @@ pub struct Artist {
|
|||
pub struct Artists {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub slug: String,
|
||||
|
||||
created_at: NaiveDateTime,
|
||||
updated_at: Option<NaiveDateTime>,
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
|
||||
pub spotify_id: Option<String>,
|
||||
pub tidal_id: Option<String>,
|
||||
|
@ -68,6 +70,7 @@ impl Artist {
|
|||
Artist {
|
||||
id: artist.id,
|
||||
name: artist.name,
|
||||
slug: artist.slug,
|
||||
|
||||
created_at: artist.created_at,
|
||||
updated_at: artist.updated_at,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::helpers::db;
|
||||
use crate::models::tracks::{Track, Tracks, TracksWithArtists};
|
||||
use crate::models::tracks::{Tracks, TracksWithArtists};
|
||||
use crate::models::user::Users;
|
||||
use crate::schema::playlists;
|
||||
use chrono::NaiveDateTime;
|
||||
|
@ -25,7 +25,7 @@ pub struct Playlist {
|
|||
pub public: bool,
|
||||
pub creator_id: String,
|
||||
|
||||
pub created_at: NaiveDateTime,
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ pub struct Playlists {
|
|||
pub public: bool,
|
||||
pub creator_id: String,
|
||||
|
||||
pub created_at: NaiveDateTime,
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ impl Playlists {
|
|||
let tracks = Tracks::find_by_playlist(&self.id)?;
|
||||
let tracks: Vec<TracksWithArtists> = tracks
|
||||
.into_iter()
|
||||
.map(Track::with_artists)
|
||||
.map(Tracks::with_artists)
|
||||
.collect::<Result<Vec<TracksWithArtists>, _>>()?;
|
||||
|
||||
Ok(tracks)
|
||||
|
|
|
@ -1,5 +1,42 @@
|
|||
pub struct Track {}
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub struct Artist {}
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Track {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub duration_ms: i32,
|
||||
pub artists: Vec<Artist>,
|
||||
pub album: Album,
|
||||
|
||||
pub struct Album {}
|
||||
pub external_ids: ExternalIds,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct ExternalIds {
|
||||
pub isrc: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Artist {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Album {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub artists: Vec<Artist>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct APISearchResponse {
|
||||
pub albums: APISearchItems<Album>,
|
||||
pub artists: APISearchItems<Artist>,
|
||||
pub tracks: APISearchItems<Track>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct APISearchItems<T> {
|
||||
pub items: Vec<T>,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::helpers::db;
|
||||
use crate::models::artists::Artists;
|
||||
use crate::models::{artists::Artists, spotify};
|
||||
use crate::schema::tracks;
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::result::Error;
|
||||
|
@ -10,28 +10,15 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::schema::playlists_tracks;
|
||||
|
||||
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||
#[derive(AsChangeset, Clone, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||
#[diesel(table_name = crate::schema::tracks)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Track {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub duration_ms: i32,
|
||||
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
|
||||
pub spotify_id: Option<String>,
|
||||
pub tidal_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
||||
pub struct Tracks {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub duration_ms: i32,
|
||||
|
||||
pub created_at: NaiveDateTime,
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
|
||||
pub spotify_id: Option<String>,
|
||||
|
@ -45,10 +32,10 @@ impl Tracks {
|
|||
Ok(playlist)
|
||||
}
|
||||
|
||||
pub fn create(track: Track) -> Result<Self, Error> {
|
||||
pub fn create(track: Tracks) -> Result<Self, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let playlist = diesel::insert_into(tracks::table)
|
||||
.values(Track::from(track))
|
||||
.values(Tracks::from(track))
|
||||
.get_result(conn)?;
|
||||
Ok(playlist)
|
||||
}
|
||||
|
@ -61,24 +48,30 @@ impl Tracks {
|
|||
|
||||
let tracks = tracks
|
||||
.into_iter()
|
||||
.map(|(playlist_id, track_id)| {
|
||||
println!("{}: {}", playlist_id, track_id);
|
||||
Tracks::find(track_id).unwrap()
|
||||
})
|
||||
.map(|(_, track_id)| Tracks::find(track_id).unwrap())
|
||||
.collect::<Vec<Tracks>>();
|
||||
|
||||
Ok(tracks)
|
||||
}
|
||||
|
||||
pub fn create_or_update(track: Tracks) -> Result<Self, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let playlist = diesel::insert_into(tracks::table)
|
||||
.values(track.clone())
|
||||
.on_conflict(tracks::id)
|
||||
.do_update()
|
||||
.set(track)
|
||||
.get_result(conn)?;
|
||||
Ok(playlist)
|
||||
}
|
||||
|
||||
pub fn get_artists(&self) -> Result<Vec<Artists>, Error> {
|
||||
let artists = Artists::find_by_track(&self.id)?;
|
||||
Ok(artists)
|
||||
}
|
||||
}
|
||||
|
||||
impl Track {
|
||||
fn from(track: Track) -> Track {
|
||||
Track {
|
||||
fn from(track: Tracks) -> Self {
|
||||
Tracks {
|
||||
id: track.id,
|
||||
title: track.title,
|
||||
duration_ms: track.duration_ms,
|
||||
|
@ -107,6 +100,20 @@ impl Track {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<spotify::Track> for Tracks {
|
||||
fn from(value: spotify::Track) -> Tracks {
|
||||
Tracks {
|
||||
id: value.external_ids.isrc,
|
||||
title: value.name,
|
||||
duration_ms: value.duration_ms,
|
||||
spotify_id: Some(value.id),
|
||||
tidal_id: None,
|
||||
created_at: None,
|
||||
updated_at: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct TracksWithArtists {
|
||||
pub id: String,
|
||||
|
|
|
@ -18,8 +18,8 @@ pub struct User {
|
|||
|
||||
pub password: String,
|
||||
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
pub created_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
||||
|
@ -30,8 +30,8 @@ pub struct Users {
|
|||
|
||||
pub password: String,
|
||||
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
pub created_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
impl Users {
|
||||
|
|
|
@ -2,15 +2,19 @@ use crate::helpers::jwt::get_encoding_key;
|
|||
use crate::middlewares::error::ErrorResponse;
|
||||
use crate::models::user::Users;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{post, web, HttpResponse};
|
||||
use actix_web::{post, web, HttpResponse, Scope};
|
||||
use chrono::{Days, Utc};
|
||||
use jsonwebtoken::{encode, Header};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn routes() -> Scope {
|
||||
web::scope("/auth").service(login)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct JWTClaims {
|
||||
pub user_id: String,
|
||||
pub exp: usize,
|
||||
pub exp: i64,
|
||||
}
|
||||
|
||||
#[post("/auth/login")]
|
||||
|
@ -18,7 +22,7 @@ async fn login(body: web::Json<LoginBody>) -> Result<HttpResponse, ErrorResponse
|
|||
#[derive(Deserialize, Serialize)]
|
||||
struct Response {
|
||||
access_token: String,
|
||||
exp: usize,
|
||||
exp: i64,
|
||||
}
|
||||
|
||||
let user = Users::find_by_email(&body.email);
|
||||
|
@ -32,28 +36,24 @@ async fn login(body: web::Json<LoginBody>) -> Result<HttpResponse, ErrorResponse
|
|||
|
||||
let claims = JWTClaims {
|
||||
user_id: user.id.to_string(),
|
||||
exp: exp as usize,
|
||||
exp,
|
||||
};
|
||||
let token = encode(&Header::default(), &claims, &get_encoding_key()).unwrap();
|
||||
|
||||
Ok(HttpResponse::Ok().json(Response {
|
||||
access_token: token,
|
||||
exp: exp as usize,
|
||||
exp,
|
||||
}))
|
||||
}
|
||||
Err(_e) => {
|
||||
return Err(ErrorResponse {
|
||||
message: "Invalid credentials.".to_string(),
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
})
|
||||
}
|
||||
Err(_e) => Err(ErrorResponse::new(
|
||||
"Invalid credentials",
|
||||
StatusCode::BAD_REQUEST,
|
||||
)),
|
||||
},
|
||||
Err(_err) => {
|
||||
return Err(ErrorResponse {
|
||||
message: "Invalid credentials.".to_string(),
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
})
|
||||
}
|
||||
Err(_err) => Err(ErrorResponse::new(
|
||||
"Invalid credentials",
|
||||
StatusCode::BAD_REQUEST,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
src/routes/import.rs
Normal file
12
src/routes/import.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use actix_web::{get, web, HttpResponse, Result, Scope};
|
||||
|
||||
use crate::middlewares::error::ErrorResponse;
|
||||
|
||||
pub fn routes() -> Scope {
|
||||
web::scope("/import")
|
||||
}
|
||||
|
||||
#[get("/spotify/{playlist_id}")]
|
||||
async fn import_spotify() -> Result<HttpResponse, ErrorResponse> {
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod auth;
|
||||
pub mod import;
|
||||
pub mod me;
|
||||
pub mod playlists;
|
||||
pub mod search;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::middlewares::error::ErrorResponse;
|
||||
use crate::services::spotify;
|
||||
use crate::models::spotify;
|
||||
use crate::services::spotify as Spotify;
|
||||
use crate::{middlewares::error::ErrorResponse, models::tracks::Tracks};
|
||||
use actix_web::{get, web, HttpResponse, Result, Scope};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -14,13 +15,31 @@ struct SearchQuery {
|
|||
|
||||
#[derive(Serialize)]
|
||||
struct SearchResponse {
|
||||
track: (),
|
||||
tracks: Vec<SearchTracks>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SearchTracks {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
impl From<spotify::Track> for SearchTracks {
|
||||
fn from(value: spotify::Track) -> SearchTracks {
|
||||
SearchTracks { id: value.id }
|
||||
}
|
||||
}
|
||||
|
||||
#[get("")]
|
||||
async fn search(query: web::Query<SearchQuery>) -> Result<HttpResponse, ErrorResponse> {
|
||||
let spotify = spotify::instance().await;
|
||||
let track = spotify.search(&query.q).await;
|
||||
let spotify = Spotify::instance().await;
|
||||
let search = spotify.search(&query.q).await.unwrap();
|
||||
|
||||
Ok(HttpResponse::Ok().json(SearchResponse { track }))
|
||||
Ok(HttpResponse::Ok().json(SearchResponse {
|
||||
tracks: search
|
||||
.tracks
|
||||
.items
|
||||
.into_iter()
|
||||
.map(SearchTracks::from)
|
||||
.collect::<Vec<SearchTracks>>(),
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -24,18 +24,11 @@ fn get_a_user(user_id: &str) -> Result<Users, ErrorResponse> {
|
|||
|
||||
match user {
|
||||
Ok(user) => Ok(user),
|
||||
Err(DBError::NotFound) => {
|
||||
return Err(ErrorResponse {
|
||||
message: "User not found".to_string(),
|
||||
status: StatusCode::NOT_FOUND,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
return Err(ErrorResponse {
|
||||
message: "Unknown error".to_string(),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})
|
||||
}
|
||||
Err(DBError::NotFound) => Err(ErrorResponse::new("User not found", StatusCode::NOT_FOUND)),
|
||||
_ => Err(ErrorResponse::new(
|
||||
"Unknown error",
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,10 @@ diesel::table! {
|
|||
id -> Varchar,
|
||||
#[max_length = 255]
|
||||
name -> Varchar,
|
||||
created_at -> Timestamp,
|
||||
slug -> Varchar,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
updated_at -> Nullable<Timestamp>,
|
||||
#[max_length = 21]
|
||||
#[max_length = 22]
|
||||
spotify_id -> Nullable<Varchar>,
|
||||
#[max_length = 10]
|
||||
tidal_id -> Nullable<Varchar>,
|
||||
|
@ -33,7 +34,7 @@ diesel::table! {
|
|||
public -> Bool,
|
||||
#[max_length = 24]
|
||||
creator_id -> Varchar,
|
||||
created_at -> Timestamp,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
updated_at -> Nullable<Timestamp>,
|
||||
}
|
||||
}
|
||||
|
@ -54,9 +55,9 @@ diesel::table! {
|
|||
#[max_length = 255]
|
||||
title -> Varchar,
|
||||
duration_ms -> Int4,
|
||||
created_at -> Timestamp,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
updated_at -> Nullable<Timestamp>,
|
||||
#[max_length = 21]
|
||||
#[max_length = 22]
|
||||
spotify_id -> Nullable<Varchar>,
|
||||
#[max_length = 10]
|
||||
tidal_id -> Nullable<Varchar>,
|
||||
|
@ -73,7 +74,7 @@ diesel::table! {
|
|||
email -> Varchar,
|
||||
password -> Text,
|
||||
updated_at -> Nullable<Timestamp>,
|
||||
created_at -> Timestamp,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ use async_trait::async_trait;
|
|||
pub trait Service: Send + Sync {
|
||||
fn get_token(&self) -> &String;
|
||||
|
||||
async fn fetch_token(&mut self) -> &String;
|
||||
async fn fetch_token(&mut self);
|
||||
// async fn get_track_by_isrc(&self, isrc: &str);
|
||||
|
||||
fn is_expired(&self) -> bool;
|
||||
}
|
||||
|
|
|
@ -1,37 +1,50 @@
|
|||
use crate::services::service::Service;
|
||||
use crate::{models::spotify::APISearchResponse, services::service::Service, utils::get_env};
|
||||
use actix_web::http::Method;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{Duration, Utc};
|
||||
use lazy_static::lazy_static;
|
||||
use reqwest::{Client, RequestBuilder};
|
||||
use serde::Deserialize;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SpotifyResponse {
|
||||
access_token: String,
|
||||
expires_in: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Spotify {
|
||||
token: String,
|
||||
expires_at: i64,
|
||||
}
|
||||
|
||||
pub async fn instance<'a>() -> Spotify {
|
||||
Spotify::new().await
|
||||
lazy_static! {
|
||||
static ref SPOTIFY_INSTANCE: Mutex<Spotify> = Mutex::new(Spotify::new());
|
||||
}
|
||||
|
||||
pub async fn instance() -> Spotify {
|
||||
let mut spotify = SPOTIFY_INSTANCE.lock().unwrap().clone();
|
||||
|
||||
// Fetch token if expired
|
||||
spotify.fetch_token().await;
|
||||
|
||||
spotify
|
||||
}
|
||||
|
||||
impl Spotify {
|
||||
pub async fn new() -> Spotify {
|
||||
let mut spotify = Spotify {
|
||||
pub fn new() -> Spotify {
|
||||
Spotify {
|
||||
token: String::new(),
|
||||
};
|
||||
spotify.fetch_token().await;
|
||||
spotify
|
||||
expires_at: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn api(&self, url: &str, method: Method) -> RequestBuilder {
|
||||
let client = Client::new();
|
||||
|
||||
client
|
||||
.request(method, &format!("https://api.spotify.com/v1{url}"))
|
||||
.request(method, format!("https://api.spotify.com/v1{url}"))
|
||||
.bearer_auth(self.get_token())
|
||||
}
|
||||
|
||||
|
@ -39,11 +52,33 @@ impl Spotify {
|
|||
// let spotify = &SPOTIFY;
|
||||
// }
|
||||
|
||||
pub async fn get_artist(self, spotify_id: &str) {}
|
||||
// pub async fn get_artist(self, spotify_id: &str) {}
|
||||
|
||||
pub async fn get_track_by_isrc(self, isrc: &str) {}
|
||||
// pub async fn get_track_by_isrc(self, isrc: &str) {}
|
||||
|
||||
pub async fn search(self, search: &str) {}
|
||||
pub async fn search(&self, search: &str) -> Result<APISearchResponse, ()> {
|
||||
let response = self
|
||||
.api("/search", Method::GET)
|
||||
.query(&[("q", search), ("type", "album,artist,track")])
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match response {
|
||||
Ok(res) => {
|
||||
let data = res.json::<APISearchResponse>().await;
|
||||
|
||||
match data {
|
||||
Ok(data) => Ok(data),
|
||||
Err(_) => {
|
||||
panic!("Invalid response (Spotify Search)")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Invalid response (Spotify Search)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -52,7 +87,15 @@ impl Service for Spotify {
|
|||
&self.token
|
||||
}
|
||||
|
||||
async fn fetch_token(&mut self) -> &String {
|
||||
fn is_expired(&self) -> bool {
|
||||
self.expires_at < chrono::Utc::now().timestamp()
|
||||
}
|
||||
|
||||
async fn fetch_token(&mut self) {
|
||||
if !self.is_expired() {
|
||||
return;
|
||||
}
|
||||
|
||||
let response = Client::new()
|
||||
.post("https://accounts.spotify.com/api/token")
|
||||
.basic_auth(
|
||||
|
@ -63,12 +106,22 @@ impl Service for Spotify {
|
|||
.send()
|
||||
.await;
|
||||
|
||||
println!("Hello");
|
||||
|
||||
match response {
|
||||
Ok(res) => {
|
||||
let data: Result<SpotifyResponse, _> = res.json().await;
|
||||
let data = res.json::<SpotifyResponse>().await;
|
||||
|
||||
self.token = data.unwrap().access_token.into();
|
||||
&self.token
|
||||
match data {
|
||||
Ok(data) => {
|
||||
self.token = data.access_token;
|
||||
self.expires_at =
|
||||
(Utc::now() + Duration::seconds(data.expires_in)).timestamp();
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Invalid response (Spotify Token)")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Invalid response (Spotify Token)")
|
||||
|
@ -76,7 +129,3 @@ impl Service for Spotify {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_env(key: &str) -> String {
|
||||
std::env::var(key).unwrap()
|
||||
}
|
||||
|
|
3
src/utils.rs
Normal file
3
src/utils.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub fn get_env(key: &str) -> String {
|
||||
std::env::var(key).unwrap()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue