mirror of
https://github.com/agersant/polaris
synced 2024-12-02 09:39:09 +00:00
commit
4d13c96dcc
20 changed files with 1080 additions and 935 deletions
1740
Cargo.lock
generated
1740
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
20
Cargo.toml
20
Cargo.toml
|
@ -11,28 +11,28 @@ ui = []
|
|||
ape = "0.2.0"
|
||||
app_dirs = "1.1.1"
|
||||
base64 = "0.10.0"
|
||||
diesel = { version = "1.3.3", features = ["sqlite"] }
|
||||
diesel_migrations = { version = "1.3.0", features = ["sqlite"] }
|
||||
diesel = { version = "1.4", features = ["sqlite"] }
|
||||
diesel_migrations = { version = "1.4", features = ["sqlite"] }
|
||||
error-chain = "0.12.0"
|
||||
getopts = "0.2.15"
|
||||
id3 = "0.2.3"
|
||||
image = "0.20.0"
|
||||
id3 = "0.3"
|
||||
image = "0.22"
|
||||
rustfm-scrobble = { git = "https://github.com/agersant/rustfm-scrobble" }
|
||||
lewton = "0.9.1"
|
||||
log = "0.4.5"
|
||||
metaflac = "0.1.8"
|
||||
mp3-duration = "0.1.0"
|
||||
rand = "0.5.5"
|
||||
regex = "1.0.5"
|
||||
rand = "0.7"
|
||||
regex = "1.2"
|
||||
ring = "0.13.5"
|
||||
reqwest = "0.9.2"
|
||||
rocket = "0.4.0"
|
||||
rocket = "0.4.2"
|
||||
rust-crypto = "0.2.36"
|
||||
serde = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
simplelog = "0.5.2"
|
||||
toml = "0.4.5"
|
||||
simplelog = "0.6"
|
||||
toml = "0.5"
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
version = "0.4.0"
|
||||
|
|
73
src/api.rs
73
src/api.rs
|
@ -1,8 +1,10 @@
|
|||
use error_chain::bail;
|
||||
use rocket::http::{Cookie, Cookies, RawStr, Status};
|
||||
use rocket::request::{self, FromParam, FromRequest, Request};
|
||||
use rocket::response::content::Html;
|
||||
use rocket::{Outcome, State};
|
||||
use rocket::{delete, get, post, put, routes, Outcome, State};
|
||||
use rocket_contrib::json::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::File;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
|
@ -71,7 +73,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Auth {
|
|||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, ()> {
|
||||
let mut cookies = request.guard::<Cookies>().unwrap();
|
||||
let mut cookies = request.guard::<Cookies<'_>>().unwrap();
|
||||
if let Some(u) = cookies.get_private(COOKIE_SESSION) {
|
||||
return Outcome::Success(Auth {
|
||||
username: u.value().to_string(),
|
||||
|
@ -85,7 +87,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Auth {
|
|||
password: Some(password),
|
||||
}) = Basic::from_str(auth_header_string.trim_start_matches("Basic "))
|
||||
{
|
||||
let db = match request.guard::<State<Arc<DB>>>() {
|
||||
let db = match request.guard::<State<'_, Arc<DB>>>() {
|
||||
Outcome::Success(d) => d,
|
||||
_ => return Outcome::Failure((Status::InternalServerError, ())),
|
||||
};
|
||||
|
@ -107,7 +109,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for AdminRights {
|
|||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, ()> {
|
||||
let db = request.guard::<State<Arc<DB>>>()?;
|
||||
let db = request.guard::<State<'_, Arc<DB>>>()?;
|
||||
|
||||
match user::count::<DB>(&db) {
|
||||
Err(_) => return Outcome::Failure((Status::InternalServerError, ())),
|
||||
|
@ -166,7 +168,7 @@ pub struct InitialSetup {
|
|||
}
|
||||
|
||||
#[get("/initial_setup")]
|
||||
fn initial_setup(db: State<Arc<DB>>) -> Result<Json<InitialSetup>, errors::Error> {
|
||||
fn initial_setup(db: State<'_, Arc<DB>>) -> Result<Json<InitialSetup>, errors::Error> {
|
||||
let initial_setup = InitialSetup {
|
||||
has_any_users: user::count::<DB>(&db)? > 0,
|
||||
};
|
||||
|
@ -175,7 +177,7 @@ fn initial_setup(db: State<Arc<DB>>) -> Result<Json<InitialSetup>, errors::Error
|
|||
|
||||
#[get("/settings")]
|
||||
fn get_settings(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
_admin_rights: AdminRights,
|
||||
) -> Result<Json<Config>, errors::Error> {
|
||||
let config = config::read::<DB>(&db)?;
|
||||
|
@ -184,7 +186,7 @@ fn get_settings(
|
|||
|
||||
#[put("/settings", data = "<config>")]
|
||||
fn put_settings(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
_admin_rights: AdminRights,
|
||||
config: Json<Config>,
|
||||
) -> Result<(), errors::Error> {
|
||||
|
@ -193,14 +195,14 @@ fn put_settings(
|
|||
}
|
||||
|
||||
#[get("/preferences")]
|
||||
fn get_preferences(db: State<Arc<DB>>, auth: Auth) -> Result<Json<Preferences>, errors::Error> {
|
||||
fn get_preferences(db: State<'_, Arc<DB>>, auth: Auth) -> Result<Json<Preferences>, errors::Error> {
|
||||
let preferences = config::read_preferences::<DB>(&db, &auth.username)?;
|
||||
Ok(Json(preferences))
|
||||
}
|
||||
|
||||
#[put("/preferences", data = "<preferences>")]
|
||||
fn put_preferences(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
auth: Auth,
|
||||
preferences: Json<Preferences>,
|
||||
) -> Result<(), errors::Error> {
|
||||
|
@ -210,7 +212,7 @@ fn put_preferences(
|
|||
|
||||
#[post("/trigger_index")]
|
||||
fn trigger_index(
|
||||
command_sender: State<Arc<index::CommandSender>>,
|
||||
command_sender: State<'_, Arc<index::CommandSender>>,
|
||||
_admin_rights: AdminRights,
|
||||
) -> Result<(), errors::Error> {
|
||||
command_sender.trigger_reindex()?;
|
||||
|
@ -230,9 +232,9 @@ struct AuthOutput {
|
|||
|
||||
#[post("/auth", data = "<credentials>")]
|
||||
fn auth(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
credentials: Json<AuthCredentials>,
|
||||
mut cookies: Cookies,
|
||||
mut cookies: Cookies<'_>,
|
||||
) -> Result<Json<AuthOutput>, errors::Error> {
|
||||
if !user::auth::<DB>(&db, &credentials.username, &credentials.password)? {
|
||||
bail!(errors::ErrorKind::IncorrectCredentials)
|
||||
|
@ -248,7 +250,7 @@ fn auth(
|
|||
|
||||
#[get("/browse")]
|
||||
fn browse_root(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
) -> Result<Json<Vec<index::CollectionFile>>, errors::Error> {
|
||||
let result = index::browse(db.deref().deref(), &PathBuf::new())?;
|
||||
|
@ -257,7 +259,7 @@ fn browse_root(
|
|||
|
||||
#[get("/browse/<path>")]
|
||||
fn browse(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
path: VFSPathBuf,
|
||||
) -> Result<Json<Vec<index::CollectionFile>>, errors::Error> {
|
||||
|
@ -266,14 +268,17 @@ fn browse(
|
|||
}
|
||||
|
||||
#[get("/flatten")]
|
||||
fn flatten_root(db: State<Arc<DB>>, _auth: Auth) -> Result<Json<Vec<index::Song>>, errors::Error> {
|
||||
fn flatten_root(
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
) -> Result<Json<Vec<index::Song>>, errors::Error> {
|
||||
let result = index::flatten(db.deref().deref(), &PathBuf::new())?;
|
||||
Ok(Json(result))
|
||||
}
|
||||
|
||||
#[get("/flatten/<path>")]
|
||||
fn flatten(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
path: VFSPathBuf,
|
||||
) -> Result<Json<Vec<index::Song>>, errors::Error> {
|
||||
|
@ -282,20 +287,26 @@ fn flatten(
|
|||
}
|
||||
|
||||
#[get("/random")]
|
||||
fn random(db: State<Arc<DB>>, _auth: Auth) -> Result<Json<Vec<index::Directory>>, errors::Error> {
|
||||
fn random(
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
) -> Result<Json<Vec<index::Directory>>, errors::Error> {
|
||||
let result = index::get_random_albums(db.deref().deref(), 20)?;
|
||||
Ok(Json(result))
|
||||
}
|
||||
|
||||
#[get("/recent")]
|
||||
fn recent(db: State<Arc<DB>>, _auth: Auth) -> Result<Json<Vec<index::Directory>>, errors::Error> {
|
||||
fn recent(
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
) -> Result<Json<Vec<index::Directory>>, errors::Error> {
|
||||
let result = index::get_recent_albums(db.deref().deref(), 20)?;
|
||||
Ok(Json(result))
|
||||
}
|
||||
|
||||
#[get("/search")]
|
||||
fn search_root(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
) -> Result<Json<Vec<index::CollectionFile>>, errors::Error> {
|
||||
let result = index::search(db.deref().deref(), "")?;
|
||||
|
@ -304,7 +315,7 @@ fn search_root(
|
|||
|
||||
#[get("/search/<query>")]
|
||||
fn search(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
query: String,
|
||||
) -> Result<Json<Vec<index::CollectionFile>>, errors::Error> {
|
||||
|
@ -314,7 +325,7 @@ fn search(
|
|||
|
||||
#[get("/serve/<path>")]
|
||||
fn serve(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
_auth: Auth,
|
||||
path: VFSPathBuf,
|
||||
) -> Result<serve::RangeResponder<File>, errors::Error> {
|
||||
|
@ -339,7 +350,7 @@ pub struct ListPlaylistsEntry {
|
|||
|
||||
#[get("/playlists")]
|
||||
fn list_playlists(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
auth: Auth,
|
||||
) -> Result<Json<Vec<ListPlaylistsEntry>>, errors::Error> {
|
||||
let playlist_names = playlist::list_playlists(&auth.username, db.deref().deref())?;
|
||||
|
@ -358,7 +369,7 @@ pub struct SavePlaylistInput {
|
|||
|
||||
#[put("/playlist/<name>", data = "<playlist>")]
|
||||
fn save_playlist(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
auth: Auth,
|
||||
name: String,
|
||||
playlist: Json<SavePlaylistInput>,
|
||||
|
@ -369,7 +380,7 @@ fn save_playlist(
|
|||
|
||||
#[get("/playlist/<name>")]
|
||||
fn read_playlist(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
auth: Auth,
|
||||
name: String,
|
||||
) -> Result<Json<Vec<index::Song>>, errors::Error> {
|
||||
|
@ -378,14 +389,14 @@ fn read_playlist(
|
|||
}
|
||||
|
||||
#[delete("/playlist/<name>")]
|
||||
fn delete_playlist(db: State<Arc<DB>>, auth: Auth, name: String) -> Result<(), errors::Error> {
|
||||
fn delete_playlist(db: State<'_, Arc<DB>>, auth: Auth, name: String) -> Result<(), errors::Error> {
|
||||
playlist::delete_playlist(&name, &auth.username, db.deref().deref())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[put("/lastfm/now_playing/<path>")]
|
||||
fn lastfm_now_playing(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
auth: Auth,
|
||||
path: VFSPathBuf,
|
||||
) -> Result<(), errors::Error> {
|
||||
|
@ -394,14 +405,18 @@ fn lastfm_now_playing(
|
|||
}
|
||||
|
||||
#[post("/lastfm/scrobble/<path>")]
|
||||
fn lastfm_scrobble(db: State<Arc<DB>>, auth: Auth, path: VFSPathBuf) -> Result<(), errors::Error> {
|
||||
fn lastfm_scrobble(
|
||||
db: State<'_, Arc<DB>>,
|
||||
auth: Auth,
|
||||
path: VFSPathBuf,
|
||||
) -> Result<(), errors::Error> {
|
||||
lastfm::scrobble(db.deref().deref(), &auth.username, &path.into() as &PathBuf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[get("/lastfm/link?<token>&<content>")]
|
||||
fn lastfm_link(
|
||||
db: State<Arc<DB>>,
|
||||
db: State<'_, Arc<DB>>,
|
||||
auth: Auth,
|
||||
token: String,
|
||||
content: String,
|
||||
|
@ -430,7 +445,7 @@ fn lastfm_link(
|
|||
}
|
||||
|
||||
#[delete("/lastfm/link")]
|
||||
fn lastfm_unlink(db: State<Arc<DB>>, auth: Auth) -> Result<(), errors::Error> {
|
||||
fn lastfm_unlink(db: State<'_, Arc<DB>>, auth: Auth) -> Result<(), errors::Error> {
|
||||
lastfm::unlink(db.deref().deref(), &auth.username)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use core::ops::Deref;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
use error_chain::bail;
|
||||
use log::info;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::path;
|
||||
|
|
|
@ -2,6 +2,8 @@ use core::ops::Deref;
|
|||
use diesel::prelude::*;
|
||||
use diesel::sqlite::SqliteConnection;
|
||||
use diesel_migrations;
|
||||
use error_chain::bail;
|
||||
use log::info;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
@ -17,7 +19,7 @@ const DB_MIGRATIONS_PATH: &str = "migrations";
|
|||
embed_migrations!("migrations");
|
||||
|
||||
pub trait ConnectionSource {
|
||||
fn get_connection(&self) -> MutexGuard<SqliteConnection>;
|
||||
fn get_connection(&self) -> MutexGuard<'_, SqliteConnection>;
|
||||
fn get_connection_mutex(&self) -> Arc<Mutex<SqliteConnection>>;
|
||||
}
|
||||
|
||||
|
@ -75,7 +77,7 @@ impl DB {
|
|||
}
|
||||
|
||||
impl ConnectionSource for DB {
|
||||
fn get_connection(&self) -> MutexGuard<SqliteConnection> {
|
||||
fn get_connection(&self) -> MutexGuard<'_, SqliteConnection> {
|
||||
self.connection.lock().unwrap()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use core::ops::Deref;
|
||||
use diesel::prelude::*;
|
||||
use log::{error, info};
|
||||
use reqwest;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
|
|
|
@ -2,6 +2,7 @@ use ape;
|
|||
use core;
|
||||
use diesel;
|
||||
use diesel_migrations;
|
||||
use error_chain::error_chain;
|
||||
use getopts;
|
||||
use id3;
|
||||
use image;
|
||||
|
@ -44,7 +45,7 @@ error_chain! {
|
|||
}
|
||||
|
||||
impl<'r> rocket::response::Responder<'r> for Error {
|
||||
fn respond_to(self, _: &rocket::request::Request) -> rocket::response::Result<'r> {
|
||||
fn respond_to(self, _: &rocket::request::Request<'_>) -> rocket::response::Result<'r> {
|
||||
let mut build = rocket::response::Response::build();
|
||||
build
|
||||
.status(match self.0 {
|
||||
|
|
|
@ -4,7 +4,10 @@ use diesel::dsl::sql;
|
|||
use diesel::prelude::*;
|
||||
use diesel::sql_types;
|
||||
use diesel::sqlite::SqliteConnection;
|
||||
use error_chain::bail;
|
||||
use log::{error, info};
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
#[cfg(test)]
|
||||
|
@ -169,7 +172,7 @@ impl<'conn> IndexBuilder<'conn> {
|
|||
fn new(
|
||||
connection: &Mutex<SqliteConnection>,
|
||||
album_art_pattern: Regex,
|
||||
) -> Result<IndexBuilder, errors::Error> {
|
||||
) -> Result<IndexBuilder<'_>, errors::Error> {
|
||||
let mut new_songs = Vec::new();
|
||||
let mut new_directories = Vec::new();
|
||||
new_songs.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use rustfm_scrobble::{Scrobble, Scrobbler};
|
||||
use serde::Deserialize;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::db::ConnectionSource;
|
||||
|
|
69
src/main.rs
69
src/main.rs
|
@ -2,47 +2,10 @@
|
|||
#![feature(proc_macro_hygiene, decl_macro)]
|
||||
#![allow(proc_macro_derive_resolution_fallback)]
|
||||
|
||||
extern crate ape;
|
||||
extern crate app_dirs;
|
||||
extern crate base64;
|
||||
extern crate core;
|
||||
extern crate crypto;
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
#[macro_use]
|
||||
extern crate diesel_migrations;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate getopts;
|
||||
extern crate id3;
|
||||
extern crate image;
|
||||
extern crate lewton;
|
||||
extern crate metaflac;
|
||||
extern crate mp3_duration;
|
||||
extern crate rand;
|
||||
extern crate regex;
|
||||
extern crate reqwest;
|
||||
extern crate ring;
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
extern crate rocket_contrib;
|
||||
extern crate rustfm_scrobble;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate toml;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate simplelog;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate uuid;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate unix_daemonize;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::fs::File;
|
||||
|
@ -51,10 +14,12 @@ use std::io::prelude::*;
|
|||
#[cfg(unix)]
|
||||
use unix_daemonize::{daemonize_redirect, ChdirMode};
|
||||
|
||||
use core::ops::Deref;
|
||||
use crate::errors::*;
|
||||
use core::ops::Deref;
|
||||
use error_chain::bail;
|
||||
use getopts::Options;
|
||||
use simplelog::{Level, LevelFilter, SimpleLogger, TermLogger};
|
||||
use log::info;
|
||||
use simplelog::{Level, LevelFilter, SimpleLogger, TermLogger, TerminalMode};
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -81,13 +46,15 @@ mod utils;
|
|||
mod vfs;
|
||||
mod web;
|
||||
|
||||
static LOG_CONFIG: simplelog::Config = simplelog::Config {
|
||||
time: Some(Level::Error),
|
||||
level: Some(Level::Error),
|
||||
target: Some(Level::Error),
|
||||
location: Some(Level::Error),
|
||||
time_format: None,
|
||||
};
|
||||
fn log_config() -> simplelog::Config {
|
||||
simplelog::Config {
|
||||
time: Some(Level::Error),
|
||||
level: Some(Level::Error),
|
||||
target: Some(Level::Error),
|
||||
location: Some(Level::Error),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(ref e) = run() {
|
||||
|
@ -123,15 +90,16 @@ fn daemonize(options: &getopts::Matches) -> Result<()> {
|
|||
|
||||
#[cfg(unix)]
|
||||
fn init_log(log_level: LevelFilter, options: &getopts::Matches) -> Result<()> {
|
||||
let config = log_config();
|
||||
if options.opt_present("f") {
|
||||
if let Err(e) = TermLogger::init(log_level, LOG_CONFIG) {
|
||||
if let Err(e) = TermLogger::init(log_level, config, TerminalMode::Stdout) {
|
||||
println!("Error starting terminal logger: {}", e);
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = SimpleLogger::init(log_level, LOG_CONFIG) {
|
||||
if let Err(e) = SimpleLogger::init(log_level, config) {
|
||||
bail!("Error starting simple logger: {}", e);
|
||||
}
|
||||
Ok(())
|
||||
|
@ -139,8 +107,9 @@ fn init_log(log_level: LevelFilter, options: &getopts::Matches) -> Result<()> {
|
|||
|
||||
#[cfg(windows)]
|
||||
fn init_log(log_level: LevelFilter, _: &getopts::Matches) -> Result<()> {
|
||||
if TermLogger::init(log_level, LOG_CONFIG).is_err() {
|
||||
if let Err(e) = SimpleLogger::init(log_level, LOG_CONFIG) {
|
||||
let config = log_config();
|
||||
if TermLogger::init(log_level, config, TerminalMode::Stdout).is_err() {
|
||||
if let Err(e) = SimpleLogger::init(log_level, config) {
|
||||
bail!("Error starting simple logger: {}", e);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ape;
|
||||
use error_chain::bail;
|
||||
use id3;
|
||||
use lewton::inside_ogg::OggStreamReader;
|
||||
use metaflac;
|
||||
|
|
38
src/serve.rs
38
src/serve.rs
|
@ -1,8 +1,9 @@
|
|||
use log::warn;
|
||||
use rocket;
|
||||
use rocket::http::hyper::header::*;
|
||||
use rocket::http::Status;
|
||||
use rocket::Response;
|
||||
use rocket::response::{self, Responder};
|
||||
use rocket::Response;
|
||||
use std::cmp;
|
||||
use std::convert::From;
|
||||
use std::fs::File;
|
||||
|
@ -44,7 +45,11 @@ impl<'r, R: Responder<'r>> RangeResponder<R> {
|
|||
RangeResponder { original }
|
||||
}
|
||||
|
||||
fn ignore_range(self, request: &rocket::request::Request, file_length: Option<u64>) -> response::Result<'r> {
|
||||
fn ignore_range(
|
||||
self,
|
||||
request: &rocket::request::Request<'_>,
|
||||
file_length: Option<u64>,
|
||||
) -> response::Result<'r> {
|
||||
let mut response = self.original.respond_to(request)?;
|
||||
if let Some(content_length) = file_length {
|
||||
response.set_header(ContentLength(content_length));
|
||||
|
@ -55,8 +60,8 @@ impl<'r, R: Responder<'r>> RangeResponder<R> {
|
|||
|
||||
fn reject_range(self, file_length: Option<u64>) -> response::Result<'r> {
|
||||
let mut response = Response::build()
|
||||
.status(Status::RangeNotSatisfiable)
|
||||
.finalize();
|
||||
.status(Status::RangeNotSatisfiable)
|
||||
.finalize();
|
||||
if file_length.is_some() {
|
||||
let content_range = ContentRange(ContentRangeSpec::Bytes {
|
||||
range: None,
|
||||
|
@ -99,8 +104,7 @@ fn truncate_range(range: &PartialFileRange, file_length: &Option<u64>) -> Option
|
|||
}
|
||||
|
||||
impl<'r> Responder<'r> for RangeResponder<File> {
|
||||
fn respond_to(mut self, request: &rocket::request::Request) -> response::Result<'r> {
|
||||
|
||||
fn respond_to(mut self, request: &rocket::request::Request<'_>) -> response::Result<'r> {
|
||||
let metadata: Option<_> = self.original.metadata().ok();
|
||||
let file_length: Option<u64> = metadata.map(|m| m.len());
|
||||
|
||||
|
@ -113,9 +117,12 @@ impl<'r> Responder<'r> for RangeResponder<File> {
|
|||
let vec_range = match Range::from_str(range_header) {
|
||||
Ok(Range::Bytes(v)) => v,
|
||||
_ => {
|
||||
warn!("Ignoring range header that could not be parse {:?}, file length is {:?}", range_header, file_length);
|
||||
warn!(
|
||||
"Ignoring range header that could not be parsed {:?}, file length is {:?}",
|
||||
range_header, file_length
|
||||
);
|
||||
return self.ignore_range(request, file_length);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let partial_file_range = match vec_range.into_iter().next() {
|
||||
|
@ -138,15 +145,18 @@ impl<'r> Responder<'r> for RangeResponder<File> {
|
|||
}
|
||||
let partial_original = self.original.take(content_len);
|
||||
let response = Response::build()
|
||||
.status(Status::PartialContent)
|
||||
.header(ContentLength(content_len))
|
||||
.header(content_range)
|
||||
.streamed_body(partial_original)
|
||||
.finalize();
|
||||
.status(Status::PartialContent)
|
||||
.header(ContentLength(content_len))
|
||||
.header(content_range)
|
||||
.streamed_body(partial_original)
|
||||
.finalize();
|
||||
|
||||
Ok(response)
|
||||
} else {
|
||||
warn!("Rejecting unsatisfiable range header {:?}, file length is {:?}", &partial_file_range, &file_length);
|
||||
warn!(
|
||||
"Rejecting unsatisfiable range header {:?}, file length is {:?}",
|
||||
&partial_file_range, &file_length
|
||||
);
|
||||
self.reject_range(file_length)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
use rocket::http::uri::Origin;
|
||||
use rocket::response::NamedFile;
|
||||
use rocket::response::Redirect;
|
||||
use rocket::State;
|
||||
use rocket::{get, routes, State};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::server::StaticDirs;
|
||||
|
||||
pub fn get_routes() -> Vec<rocket::Route> {
|
||||
routes![
|
||||
index,
|
||||
files,
|
||||
]
|
||||
routes![index, files,]
|
||||
}
|
||||
|
||||
#[get("/", rank = 9)]
|
||||
fn index(origin: &Origin) -> Redirect {
|
||||
fn index(origin: &Origin<'_>) -> Redirect {
|
||||
let mut new_path = origin.path().to_owned();
|
||||
if !new_path.ends_with("/") {
|
||||
new_path.push_str("/");
|
||||
|
@ -26,27 +23,30 @@ fn index(origin: &Origin) -> Redirect {
|
|||
}
|
||||
|
||||
#[get("/<file..>", rank = 9)]
|
||||
fn files(static_dirs: State<Arc<StaticDirs>>, file: PathBuf) -> Option<NamedFile> {
|
||||
fn files(static_dirs: State<'_, Arc<StaticDirs>>, file: PathBuf) -> Option<NamedFile> {
|
||||
let path = static_dirs.swagger_dir_path.clone().join(file.clone());
|
||||
NamedFile::open(path).ok()
|
||||
NamedFile::open(path).ok()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_index_redirect() {
|
||||
use rocket::http::Status;
|
||||
use crate::test::get_test_environment;
|
||||
use rocket::http::Status;
|
||||
|
||||
let env = get_test_environment("swagger_index_redirect.sqlite");
|
||||
let client = &env.client;
|
||||
let response = client.get("/swagger").dispatch();
|
||||
assert_eq!(response.status(), Status::PermanentRedirect);
|
||||
assert_eq!(response.headers().get_one("Location"), Some("/swagger/index.html"));
|
||||
assert_eq!(
|
||||
response.headers().get_one("Location"),
|
||||
Some("/swagger/index.html")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_index() {
|
||||
use rocket::http::Status;
|
||||
use crate::test::get_test_environment;
|
||||
use rocket::http::Status;
|
||||
|
||||
let env = get_test_environment("swagger_index.sqlite");
|
||||
let client = &env.client;
|
||||
|
|
|
@ -49,9 +49,7 @@ pub fn get_thumbnail(real_path: &Path, max_dimension: u32) -> Result<PathBuf> {
|
|||
let scaled_image =
|
||||
source_image.resize(out_dimension, out_dimension, FilterType::Lanczos3);
|
||||
let (scaled_width, scaled_height) = scaled_image.dimensions();
|
||||
let background = image::Rgb {
|
||||
data: [255 as u8, 255 as u8, 255 as u8],
|
||||
};
|
||||
let background = image::Rgb([255, 255 as u8, 255 as u8]);
|
||||
final_image = DynamicImage::ImageRgb8(ImageBuffer::from_pixel(
|
||||
out_dimension,
|
||||
out_dimension,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use log::info;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use log::info;
|
||||
use std;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use core::ops::Deref;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
use error_chain::bail;
|
||||
use rand;
|
||||
use ring::{digest, pbkdf2};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use app_dirs::{app_root, AppDataType, AppInfo};
|
||||
use error_chain::bail;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
@ -54,10 +55,7 @@ pub fn get_audio_format(path: &Path) -> Option<AudioFormat> {
|
|||
|
||||
#[test]
|
||||
fn test_get_audio_format() {
|
||||
assert_eq!(
|
||||
get_audio_format(Path::new("animals/🐷/my🐖file.jpg")),
|
||||
None
|
||||
);
|
||||
assert_eq!(get_audio_format(Path::new("animals/🐷/my🐖file.jpg")), None);
|
||||
assert_eq!(
|
||||
get_audio_format(Path::new("animals/🐷/my🐖file.flac")),
|
||||
Some(AudioFormat::FLAC)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use core::ops::Deref;
|
||||
use diesel::prelude::*;
|
||||
use error_chain::bail;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
|
17
src/web.rs
17
src/web.rs
|
@ -1,5 +1,5 @@
|
|||
use rocket::response::NamedFile;
|
||||
use rocket::State;
|
||||
use rocket::{get, routes, State};
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -7,29 +7,26 @@ use std::sync::Arc;
|
|||
use crate::server::StaticDirs;
|
||||
|
||||
pub fn get_routes() -> Vec<rocket::Route> {
|
||||
routes![
|
||||
index,
|
||||
files,
|
||||
]
|
||||
routes![index, files,]
|
||||
}
|
||||
|
||||
#[get("/", rank = 10)]
|
||||
fn index(static_dirs: State<Arc<StaticDirs>>) -> io::Result<NamedFile> {
|
||||
fn index(static_dirs: State<'_, Arc<StaticDirs>>) -> io::Result<NamedFile> {
|
||||
let mut path = static_dirs.web_dir_path.clone();
|
||||
path.push("index.html");
|
||||
NamedFile::open(path)
|
||||
NamedFile::open(path)
|
||||
}
|
||||
|
||||
#[get("/<file..>", rank = 10)]
|
||||
fn files(static_dirs: State<Arc<StaticDirs>>, file: PathBuf) -> Option<NamedFile> {
|
||||
fn files(static_dirs: State<'_, Arc<StaticDirs>>, file: PathBuf) -> Option<NamedFile> {
|
||||
let path = static_dirs.web_dir_path.clone().join(file.clone());
|
||||
NamedFile::open(path).ok()
|
||||
NamedFile::open(path).ok()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_index() {
|
||||
use rocket::http::Status;
|
||||
use crate::test::get_test_environment;
|
||||
use rocket::http::Status;
|
||||
|
||||
let env = get_test_environment("web_index.sqlite");
|
||||
let client = &env.client;
|
||||
|
|
Loading…
Reference in a new issue