polaris-mirror/src/main.rs
Tobias Schmitz bff49c22ec
Embedded artwork support ()
* Embedded artwork support for mp4 and id3 tags

* Embedded artwork support for flac tags.

* small fixes

* use first embedded artwork for directory

* added artwork tests

* updated Cargo.lock

* use first embedded artwork for missing artworks
2020-11-25 15:46:09 -08:00

248 lines
6.3 KiB
Rust

#![recursion_limit = "256"]
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_migrations;
#[cfg(feature = "profile-index")]
#[macro_use]
extern crate flamer;
#[cfg(unix)]
use log::error;
#[cfg(unix)]
use sd_notify::{self, NotifyState};
#[cfg(unix)]
use std::fs::File;
#[cfg(unix)]
use std::io::prelude::*;
#[cfg(unix)]
use unix_daemonize::{daemonize_redirect, ChdirMode};
use anyhow::*;
use getopts::Options;
use log::info;
use simplelog::{LevelFilter, SimpleLogger, TermLogger, TerminalMode};
use std::path::Path;
mod config;
mod db;
mod ddns;
mod index;
mod lastfm;
mod playlist;
mod service;
mod artwork;
mod thumbnails;
mod ui;
mod user;
mod utils;
mod vfs;
fn log_config() -> simplelog::Config {
simplelog::ConfigBuilder::new()
.set_location_level(LevelFilter::Error)
.build()
}
#[cfg(unix)]
fn daemonize(options: &getopts::Matches) -> Result<()> {
if options.opt_present("f") {
return Ok(());
}
let mut log_file = utils::get_data_root()?;
log_file.push("polaris.log");
let pid = match daemonize_redirect(Some(&log_file), Some(&log_file), ChdirMode::NoChdir) {
Ok(p) => p,
Err(e) => bail!("Daemonize error: {:#?}", e),
};
let mut pid_path = utils::get_data_root()?;
pid_path.push("polaris.pid");
let mut file = File::create(pid_path)?;
file.write_all(pid.to_string().as_bytes())?;
Ok(())
}
#[cfg(unix)]
fn init_log(log_level: LevelFilter, options: &getopts::Matches) -> Result<()> {
if options.opt_present("f") {
if let Err(e) = TermLogger::init(log_level, log_config(), TerminalMode::Stdout) {
println!("Error starting terminal logger: {}", e);
} else {
return Ok(());
}
}
if let Err(e) = SimpleLogger::init(log_level, log_config()) {
bail!("Error starting simple logger: {}", e);
}
Ok(())
}
#[cfg(windows)]
fn init_log(log_level: LevelFilter, _: &getopts::Matches) -> Result<()> {
if TermLogger::init(log_level, log_config(), TerminalMode::Stdout).is_err() {
if let Err(e) = SimpleLogger::init(log_level, log_config()) {
bail!("Error starting simple logger: {}", e);
}
};
Ok(())
}
#[cfg(unix)]
fn notify_ready() {
if let Ok(true) = sd_notify::booted() {
if let Err(e) = sd_notify::notify(true, &[NotifyState::Ready]) {
error!("Unable to send ready notification: {}", e);
}
}
}
#[cfg(not(unix))]
fn notify_ready() {}
fn main() -> Result<()> {
// Parse CLI options
let args: Vec<String> = std::env::args().collect();
let mut options = Options::new();
options.optopt("c", "config", "set the configuration file", "FILE");
options.optopt("p", "port", "set polaris to run on a custom port", "PORT");
options.optopt("d", "database", "set the path to index database", "FILE");
options.optopt("w", "web", "set the path to web client files", "DIRECTORY");
options.optopt("s", "swagger", "set the path to swagger files", "DIRECTORY");
options.optopt(
"l",
"log",
"set the log level to a value between 0 (off) and 3 (debug)",
"LEVEL",
);
#[cfg(unix)]
options.optflag(
"f",
"foreground",
"run polaris in the foreground instead of daemonizing",
);
options.optflag("h", "help", "print this help menu");
let matches = options.parse(&args[1..])?;
if matches.opt_present("h") {
let program = args[0].clone();
let brief = format!("Usage: {} [options]", program);
print!("{}", options.usage(&brief));
return Ok(());
}
let log_level = match matches.opt_str("l").as_ref().map(String::as_ref) {
Some("0") => LevelFilter::Off,
Some("1") => LevelFilter::Error,
Some("2") => LevelFilter::Info,
Some("3") => LevelFilter::Debug,
_ => LevelFilter::Info,
};
init_log(log_level, &matches)?;
#[cfg(unix)]
daemonize(&matches)?;
// Init DB
info!("Starting up database");
let db_path = matches.opt_str("d");
let mut default_db_path = utils::get_data_root()?;
default_db_path.push("db.sqlite");
let db_path = db_path
.map(|n| Path::new(n.as_str()).to_path_buf())
.unwrap_or(default_db_path);
let db = db::DB::new(&db_path)?;
// Parse config
info!("Parsing configuration");
let config_file_name = matches.opt_str("c");
let config_file_path = config_file_name.map(|p| Path::new(p.as_str()).to_path_buf());
if let Some(path) = config_file_path {
let config = config::parse_toml_file(&path)?;
info!("Applying configuration");
config::amend(&db, &config)?;
}
let config = config::read(&db)?;
let auth_secret = config::get_auth_secret(&db)?;
// Init index
info!("Initializing index");
let index = index::builder(db.clone()).periodic_updates(true).build();
// API mount target
let prefix_url = config.prefix_url.unwrap_or_else(|| "".to_string());
let api_url = format!("/{}api", &prefix_url);
info!("Mounting API on {}", api_url);
// Web client mount target
let web_dir_name = matches.opt_str("w");
let mut default_web_dir = utils::get_data_root()?;
default_web_dir.push("web");
let web_dir_path = web_dir_name
.map(|n| Path::new(n.as_str()).to_path_buf())
.unwrap_or(default_web_dir);
info!("Static files location is {}", web_dir_path.display());
let web_url = format!("/{}", &prefix_url);
info!("Mounting web client files on {}", web_url);
// Swagger files mount target
let swagger_dir_name = matches.opt_str("s");
let mut default_swagger_dir = utils::get_data_root()?;
default_swagger_dir.push("swagger");
let swagger_dir_path = swagger_dir_name
.map(|n| Path::new(n.as_str()).to_path_buf())
.unwrap_or(default_swagger_dir);
info!("Swagger files location is {}", swagger_dir_path.display());
let swagger_url = format!("/{}swagger", &prefix_url);
info!("Mounting swagger files on {}", swagger_url);
// Thumbnails manager
let mut thumbnails_path = utils::get_data_root()?;
thumbnails_path.push("thumbnails");
let thumbnails_manager = thumbnails::ThumbnailsManager::new(thumbnails_path.as_path());
// Start server
info!("Starting up server");
let port: u16 = matches
.opt_str("p")
.unwrap_or_else(|| "5050".to_owned())
.parse()
.with_context(|| "Invalid port number")?;
let db_server = db.clone();
std::thread::spawn(move || {
let _ = service::server::run(
port,
&auth_secret,
api_url,
web_url,
web_dir_path,
swagger_url,
swagger_dir_path,
db_server,
index,
thumbnails_manager,
);
});
// Start DDNS updates
let db_ddns = db.clone();
std::thread::spawn(move || {
ddns::run(&db_ddns);
});
// Send readiness notification
notify_ready();
// Run UI
ui::run();
info!("Shutting down server");
Ok(())
}