Adds lastfm endpoints

This commit is contained in:
Antoine Gersant 2024-07-13 17:28:48 -07:00
parent 18858d8d1a
commit 274a1f2cf7
2 changed files with 93 additions and 2 deletions
src/server

View file

@ -44,6 +44,12 @@ impl FromRef<App> for app::index::Index {
}
}
impl FromRef<App> for app::lastfm::Manager {
fn from_ref(app: &App) -> Self {
app.lastfm_manager.clone()
}
}
impl FromRef<App> for app::user::Manager {
fn from_ref(app: &App) -> Self {
app.user_manager.clone()

View file

@ -1,12 +1,14 @@
use axum::{
extract::{Path, State},
extract::{Path, Query, State},
response::Html,
routing::{delete, get, post, put},
Json, Router,
};
use base64::{prelude::BASE64_STANDARD_NO_PAD, Engine};
use percent_encoding::percent_decode_str;
use crate::{
app::{config, ddns, index, settings, user, vfs, App},
app::{config, ddns, index, lastfm, settings, user, vfs, App},
server::{dto, error::APIError},
};
@ -39,6 +41,11 @@ pub fn router() -> Router<App> {
.route("/recent", get(get_recent))
.route("/search", get(get_search_root))
.route("/search/*query", get(get_search))
.route("/lastfm/now_playing/*path", put(put_lastfm_now_playing))
.route("/lastfm/scrobble/*path", post(post_lastfm_scrobble))
.route("/lastfm/link_token", get(get_lastfm_link_token))
.route("/lastfm/link", get(get_lastfm_link))
.route("/lastfm/link", delete(delete_lastfm_link))
// TODO figure out NormalizePathLayer and remove this
.route("/browse/", get(get_browse_root))
.route("/flatten/", get(get_flatten_root))
@ -303,3 +310,81 @@ async fn get_search(
let result = index.search(&query).await?;
Ok(Json(result))
}
async fn put_lastfm_now_playing(
auth: Auth,
State(lastfm_manager): State<lastfm::Manager>,
State(user_manager): State<user::Manager>,
Path(path): Path<String>,
) -> Result<(), APIError> {
if !user_manager.is_lastfm_linked(auth.get_username()).await {
return Err(APIError::LastFMAccountNotLinked);
}
let path = percent_decode_str(&path).decode_utf8_lossy();
lastfm_manager
.now_playing(auth.get_username(), std::path::Path::new(path.as_ref()))
.await?;
Ok(())
}
async fn post_lastfm_scrobble(
auth: Auth,
State(lastfm_manager): State<lastfm::Manager>,
State(user_manager): State<user::Manager>,
Path(path): Path<String>,
) -> Result<(), APIError> {
if !user_manager.is_lastfm_linked(auth.get_username()).await {
return Err(APIError::LastFMAccountNotLinked);
}
let path = percent_decode_str(&path).decode_utf8_lossy();
lastfm_manager
.scrobble(auth.get_username(), std::path::Path::new(path.as_ref()))
.await?;
Ok(())
}
async fn get_lastfm_link_token(
auth: Auth,
State(lastfm_manager): State<lastfm::Manager>,
) -> Result<Json<dto::LastFMLinkToken>, APIError> {
let user::AuthToken(value) = lastfm_manager.generate_link_token(auth.get_username())?;
Ok(Json(dto::LastFMLinkToken { value }))
}
async fn get_lastfm_link(
State(lastfm_manager): State<lastfm::Manager>,
State(user_manager): State<user::Manager>,
Query(payload): Query<dto::LastFMLink>,
) -> Result<Html<String>, APIError> {
let auth_token = user::AuthToken(payload.auth_token.clone());
let authorization = user_manager
.authenticate(&auth_token, user::AuthorizationScope::LastFMLink)
.await?;
let lastfm_token = &payload.token;
lastfm_manager
.link(&authorization.username, lastfm_token)
.await?;
// Percent decode
let base64_content = percent_decode_str(&payload.content).decode_utf8_lossy();
// Base64 decode
let popup_content = BASE64_STANDARD_NO_PAD
.decode(base64_content.as_bytes())
.map_err(|_| APIError::LastFMLinkContentBase64DecodeError)?;
// UTF-8 decode
let popup_content_string = std::str::from_utf8(&popup_content)
.map_err(|_| APIError::LastFMLinkContentEncodingError)
.map(|s| s.to_owned())?;
Ok(Html(popup_content_string))
}
async fn delete_lastfm_link(
auth: Auth,
State(lastfm_manager): State<lastfm::Manager>,
) -> Result<(), APIError> {
lastfm_manager.unlink(auth.get_username()).await?;
Ok(())
}