mirror of
https://github.com/agersant/polaris
synced 2024-12-11 13:56:24 +00:00
Implemented last fm endpoints
This commit is contained in:
parent
4af2c0f09e
commit
c25dc8155f
2 changed files with 98 additions and 20 deletions
|
@ -249,7 +249,7 @@ fn get_endpoints(db: &Arc<DB>, index_channel: &Arc<index::CommandSender>) -> Mou
|
||||||
lastfm_router.get(
|
lastfm_router.get(
|
||||||
"/scrobble",
|
"/scrobble",
|
||||||
move |request: &mut Request| self::lastfm_scrobble(request, scrobble_db.deref()),
|
move |request: &mut Request| self::lastfm_scrobble(request, scrobble_db.deref()),
|
||||||
"auth",
|
"scrobble",
|
||||||
);
|
);
|
||||||
|
|
||||||
auth_api_mount.mount("/lastfm/", lastfm_router);
|
auth_api_mount.mount("/lastfm/", lastfm_router);
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
use rocket::http::{Cookie, Cookies, RawStr, Status};
|
use rocket::http::{Cookie, Cookies, RawStr, Status};
|
||||||
use rocket::request::{self, FromParam, FromRequest, Request};
|
use rocket::request::{self, FromParam, FromRequest, Request};
|
||||||
|
use rocket::response::content::Html;
|
||||||
use rocket::{Outcome, State};
|
use rocket::{Outcome, State};
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use config::{self, Config, Preferences};
|
use config::{self, Config, Preferences};
|
||||||
use db::DB;
|
use db::DB;
|
||||||
use errors;
|
use errors;
|
||||||
use index;
|
use index;
|
||||||
|
use lastfm;
|
||||||
use playlist;
|
use playlist;
|
||||||
use serve;
|
use serve;
|
||||||
use thumbnails;
|
use thumbnails;
|
||||||
|
@ -45,6 +48,10 @@ pub fn get_routes() -> Vec<rocket::Route> {
|
||||||
save_playlist,
|
save_playlist,
|
||||||
read_playlist,
|
read_playlist,
|
||||||
delete_playlist,
|
delete_playlist,
|
||||||
|
lastfm_link,
|
||||||
|
lastfm_unlink,
|
||||||
|
lastfm_now_playing,
|
||||||
|
lastfm_scrobble,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,18 +104,18 @@ struct VFSPathBuf {
|
||||||
impl<'r> FromParam<'r> for VFSPathBuf {
|
impl<'r> FromParam<'r> for VFSPathBuf {
|
||||||
type Error = &'r RawStr;
|
type Error = &'r RawStr;
|
||||||
|
|
||||||
fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
|
fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
|
||||||
let decoded_path = param.percent_decode_lossy();
|
let decoded_path = param.percent_decode_lossy();
|
||||||
Ok(VFSPathBuf{
|
Ok(VFSPathBuf {
|
||||||
path_buf: PathBuf::from(decoded_path.into_owned())
|
path_buf: PathBuf::from(decoded_path.into_owned()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<VFSPathBuf> for PathBuf {
|
impl From<VFSPathBuf> for PathBuf {
|
||||||
fn from(vfs_path_buf: VFSPathBuf) -> Self {
|
fn from(vfs_path_buf: VFSPathBuf) -> Self {
|
||||||
vfs_path_buf.path_buf.clone()
|
vfs_path_buf.path_buf.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -198,10 +205,11 @@ fn auth(
|
||||||
mut cookies: Cookies,
|
mut cookies: Cookies,
|
||||||
) -> Result<Json<AuthOutput>, errors::Error> {
|
) -> Result<Json<AuthOutput>, errors::Error> {
|
||||||
user::auth::<DB>(&db, &credentials.username, &credentials.password)?;
|
user::auth::<DB>(&db, &credentials.username, &credentials.password)?;
|
||||||
cookies.add_private(Cookie::new(
|
cookies.add_private(
|
||||||
SESSION_FIELD_USERNAME,
|
Cookie::build(SESSION_FIELD_USERNAME, credentials.username.clone())
|
||||||
credentials.username.clone(),
|
.same_site(rocket::http::SameSite::Lax)
|
||||||
));
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
let auth_output = AuthOutput {
|
let auth_output = AuthOutput {
|
||||||
admin: user::is_admin::<DB>(&db, &credentials.username)?,
|
admin: user::is_admin::<DB>(&db, &credentials.username)?,
|
||||||
|
@ -257,19 +265,30 @@ fn recent(db: State<DB>, _auth: Auth) -> Result<Json<Vec<index::Directory>>, err
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/search")]
|
#[get("/search")]
|
||||||
fn search_root(db: State<DB>, _auth: Auth) -> Result<Json<Vec<index::CollectionFile>>, errors::Error> {
|
fn search_root(
|
||||||
|
db: State<DB>,
|
||||||
|
_auth: Auth,
|
||||||
|
) -> Result<Json<Vec<index::CollectionFile>>, errors::Error> {
|
||||||
let result = index::search(db.deref(), "")?;
|
let result = index::search(db.deref(), "")?;
|
||||||
Ok(Json(result))
|
Ok(Json(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/search/<query>")]
|
#[get("/search/<query>")]
|
||||||
fn search(db: State<DB>, _auth: Auth, query: String) -> Result<Json<Vec<index::CollectionFile>>, errors::Error> {
|
fn search(
|
||||||
|
db: State<DB>,
|
||||||
|
_auth: Auth,
|
||||||
|
query: String,
|
||||||
|
) -> Result<Json<Vec<index::CollectionFile>>, errors::Error> {
|
||||||
let result = index::search(db.deref(), &query)?;
|
let result = index::search(db.deref(), &query)?;
|
||||||
Ok(Json(result))
|
Ok(Json(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/serve/<path>")]
|
#[get("/serve/<path>")]
|
||||||
fn serve(db: State<DB>, _auth: Auth, path: VFSPathBuf) -> Result<serve::RangeResponder<File>, errors::Error> {
|
fn serve(
|
||||||
|
db: State<DB>,
|
||||||
|
_auth: Auth,
|
||||||
|
path: VFSPathBuf,
|
||||||
|
) -> Result<serve::RangeResponder<File>, errors::Error> {
|
||||||
let db: &DB = db.deref();
|
let db: &DB = db.deref();
|
||||||
let vfs = db.get_vfs()?;
|
let vfs = db.get_vfs()?;
|
||||||
let real_path = vfs.virtual_to_real(&path.into() as &PathBuf)?;
|
let real_path = vfs.virtual_to_real(&path.into() as &PathBuf)?;
|
||||||
|
@ -290,8 +309,10 @@ struct ListPlaylistsEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/playlists")]
|
#[get("/playlists")]
|
||||||
fn list_playlists(db: State<DB>, auth: Auth) -> Result<Json<Vec<ListPlaylistsEntry>>, errors::Error> {
|
fn list_playlists(
|
||||||
|
db: State<DB>,
|
||||||
|
auth: Auth,
|
||||||
|
) -> Result<Json<Vec<ListPlaylistsEntry>>, errors::Error> {
|
||||||
let playlist_names = playlist::list_playlists(&auth.username, db.deref())?;
|
let playlist_names = playlist::list_playlists(&auth.username, db.deref())?;
|
||||||
let playlists: Vec<ListPlaylistsEntry> = playlist_names
|
let playlists: Vec<ListPlaylistsEntry> = playlist_names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -307,13 +328,22 @@ struct SavePlaylistInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/playlist/<name>", data = "<playlist>")]
|
#[put("/playlist/<name>", data = "<playlist>")]
|
||||||
fn save_playlist(db: State<DB>, auth: Auth, name: String, playlist: Json<SavePlaylistInput>) -> Result<(), errors::Error> {
|
fn save_playlist(
|
||||||
|
db: State<DB>,
|
||||||
|
auth: Auth,
|
||||||
|
name: String,
|
||||||
|
playlist: Json<SavePlaylistInput>,
|
||||||
|
) -> Result<(), errors::Error> {
|
||||||
playlist::save_playlist(&name, &auth.username, &playlist.tracks, db.deref())?;
|
playlist::save_playlist(&name, &auth.username, &playlist.tracks, db.deref())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/playlist/<name>")]
|
#[get("/playlist/<name>")]
|
||||||
fn read_playlist(db: State<DB>, auth: Auth, name: String) -> Result<Json<Vec<index::Song>>, errors::Error> {
|
fn read_playlist(
|
||||||
|
db: State<DB>,
|
||||||
|
auth: Auth,
|
||||||
|
name: String,
|
||||||
|
) -> Result<Json<Vec<index::Song>>, errors::Error> {
|
||||||
let songs = playlist::read_playlist(&name, &auth.username, db.deref())?;
|
let songs = playlist::read_playlist(&name, &auth.username, db.deref())?;
|
||||||
Ok(Json(songs))
|
Ok(Json(songs))
|
||||||
}
|
}
|
||||||
|
@ -323,3 +353,51 @@ fn delete_playlist(db: State<DB>, auth: Auth, name: String) -> Result<(), errors
|
||||||
playlist::delete_playlist(&name, &auth.username, db.deref())?;
|
playlist::delete_playlist(&name, &auth.username, db.deref())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[put("/lastfm/now_playing/<path>")]
|
||||||
|
fn lastfm_now_playing(db: State<DB>, auth: Auth, path: VFSPathBuf) -> Result<(), errors::Error> {
|
||||||
|
lastfm::now_playing(db.deref(), &auth.username, &path.into() as &PathBuf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/lastfm/scrobble/<path>")]
|
||||||
|
fn lastfm_scrobble(db: State<DB>, auth: Auth, path: VFSPathBuf) -> Result<(), errors::Error> {
|
||||||
|
lastfm::scrobble(db.deref(), &auth.username, &path.into() as &PathBuf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/lastfm/link?<token>&<content>")]
|
||||||
|
fn lastfm_link(
|
||||||
|
db: State<DB>,
|
||||||
|
auth: Auth,
|
||||||
|
token: String,
|
||||||
|
content: String,
|
||||||
|
) -> Result<Html<String>, errors::Error> {
|
||||||
|
lastfm::link(db.deref(), &auth.username, &token)?;
|
||||||
|
|
||||||
|
// Percent decode
|
||||||
|
let base64_content = match RawStr::from_str(&content).percent_decode() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return Err(errors::Error::from(errors::ErrorKind::EncodingError).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Base64 decode
|
||||||
|
let popup_content = match base64::decode(base64_content.as_bytes()) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return Err(errors::Error::from(errors::ErrorKind::EncodingError).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// UTF-8 decode
|
||||||
|
let popup_content_string = match str::from_utf8(&popup_content) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return Err(errors::Error::from(errors::ErrorKind::EncodingError).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Html(popup_content_string.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[delete("/lastfm/link")]
|
||||||
|
fn lastfm_unlink(db: State<DB>, auth: Auth) -> Result<(), errors::Error> {
|
||||||
|
lastfm::unlink(db.deref(), &auth.username)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue