Merge 5ca38939bd
into release
This commit is contained in:
commit
bb11c80ebd
14 changed files with 700 additions and 435 deletions
|
@ -1,6 +1,9 @@
|
|||
coverage:
|
||||
range: "0...100"
|
||||
status:
|
||||
patch:
|
||||
default:
|
||||
informational: true
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,5 +1,17 @@
|
|||
# Changelog
|
||||
|
||||
## Polaris 0.14.1
|
||||
|
||||
### Server
|
||||
|
||||
- Fixed compilation issue when using musl toolchains
|
||||
- Log messages that DDNS is not setup have been downgraded to debug level
|
||||
|
||||
### Web client
|
||||
|
||||
- Fixed a bug where non-ASCII files or directories were not always alphabetically sorted (thanks @dechamps)
|
||||
- Fixed a bug where after linking a last.fm account, clicking the account name would not link to the expected page
|
||||
|
||||
## Polaris 0.14.0
|
||||
|
||||
### General
|
||||
|
|
1038
Cargo.lock
generated
1038
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
28
Cargo.toml
28
Cargo.toml
|
@ -14,17 +14,20 @@ ui = ["native-windows-gui", "native-windows-derive"]
|
|||
actix-files = { version = "0.6" }
|
||||
actix-web = { version = "4" }
|
||||
actix-web-httpauth = { version = "0.8" }
|
||||
ape = "0.4.0"
|
||||
base64 = "0.13"
|
||||
ape = "0.5"
|
||||
base64 = "0.21"
|
||||
branca = "0.10.1"
|
||||
crossbeam-channel = "0.5"
|
||||
diesel_migrations = { version = "2.0", features = ["sqlite"] }
|
||||
futures-util = { version = "0.3" }
|
||||
getopts = "0.2.21"
|
||||
http = "0.2.8"
|
||||
id3 = { git = "https://github.com/polyfloyd/rust-id3.git", rev = "f3b5e3a" } # TODO update after 1.5.0 is released
|
||||
id3 = "1.7.0"
|
||||
lewton = "0.10.2"
|
||||
libsqlite3-sys = { version = "0.25", features = ["bundled", "bundled-windows"], optional = true }
|
||||
libsqlite3-sys = { version = "0.26", features = [
|
||||
"bundled",
|
||||
"bundled-windows",
|
||||
], optional = true }
|
||||
log = "0.4.17"
|
||||
metaflac = "0.2.5"
|
||||
mp3-duration = "0.1.10"
|
||||
|
@ -42,9 +45,8 @@ serde_derive = "1.0.147"
|
|||
serde_json = "1.0.87"
|
||||
simplelog = "0.12.0"
|
||||
thiserror = "1.0.37"
|
||||
tokio = "1.21"
|
||||
toml = "0.5"
|
||||
ureq = "1.5.5"
|
||||
toml = "0.7"
|
||||
ureq = "2.7"
|
||||
url = "2.3"
|
||||
|
||||
[dependencies.diesel]
|
||||
|
@ -58,11 +60,17 @@ default_features = false
|
|||
features = ["bmp", "gif", "jpeg", "png"]
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
native-windows-gui = {version = "1.0.13", default-features = false, features = ["cursor", "image-decoder", "message-window", "menu", "tray-notification"], optional = true }
|
||||
native-windows-derive = {version = "1.0.5", optional = true }
|
||||
native-windows-gui = { version = "1.0.13", default-features = false, features = [
|
||||
"cursor",
|
||||
"image-decoder",
|
||||
"message-window",
|
||||
"menu",
|
||||
"tray-notification",
|
||||
], optional = true }
|
||||
native-windows-derive = { version = "1.0.5", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
daemonize = "0.4.1"
|
||||
daemonize = "0.5"
|
||||
sd-notify = "0.4.1"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
|
|
|
@ -21,7 +21,9 @@ Password: `demo_password`
|
|||
- Support for `flac`, `mp3`, `mp4`, `mpc`, `ogg`, `opus`, `ape`, `wav` and `aiff` files
|
||||
- Easy to setup and administer, no configuration files needed
|
||||
- Dark mode and customizable color themes
|
||||
- Listen to your music on the go with [Polaris Android](https://github.com/agersant/polaris-android)
|
||||
- Listen to your music on the go:
|
||||
- Polaris Android ([Google Play Store](https://play.google.com/store/apps/details?id=agersant.polaris) · [F-Droid](https://f-droid.org/packages/agersant.polaris/) · [Repository](https://github.com/agersant/polaris-android))
|
||||
- Polarios ([App Store](https://apps.apple.com/app/polarios/id1662366309) · [Repository](https://gitlab.com/elise/Polarios))
|
||||
- [Last.fm](https://www.last.fm) scrobbling
|
||||
|
||||
## Tutorials
|
||||
|
|
BIN
res/branding/logo/social_media_preview.afdesign
Normal file
BIN
res/branding/logo/social_media_preview.afdesign
Normal file
Binary file not shown.
|
@ -1,5 +1,6 @@
|
|||
use base64::prelude::*;
|
||||
use diesel::prelude::*;
|
||||
use log::{error, info};
|
||||
use log::{debug, error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::thread;
|
||||
use std::time;
|
||||
|
@ -12,6 +13,8 @@ const DDNS_UPDATE_URL: &str = "https://ydns.io/api/v1/update/";
|
|||
pub enum Error {
|
||||
#[error("DDNS update query failed with HTTP status code `{0}`")]
|
||||
UpdateQueryFailed(u16),
|
||||
#[error("DDNS update query failed due to a transport error")]
|
||||
UpdateQueryTransport,
|
||||
#[error(transparent)]
|
||||
DatabaseConnection(#[from] db::Error),
|
||||
#[error(transparent)]
|
||||
|
@ -39,19 +42,23 @@ impl Manager {
|
|||
fn update_my_ip(&self) -> Result<(), Error> {
|
||||
let config = self.config()?;
|
||||
if config.host.is_empty() || config.username.is_empty() {
|
||||
info!("Skipping DDNS update because credentials are missing");
|
||||
debug!("Skipping DDNS update because credentials are missing");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let full_url = format!("{}?host={}", DDNS_UPDATE_URL, &config.host);
|
||||
let credentials = format!("{}:{}", &config.username, &config.password);
|
||||
let response = ureq::get(full_url.as_str())
|
||||
.auth(&config.username, &config.password)
|
||||
.set(
|
||||
"Authorization",
|
||||
&format!("Basic {}", BASE64_STANDARD_NO_PAD.encode(credentials)),
|
||||
)
|
||||
.call();
|
||||
|
||||
if response.ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::UpdateQueryFailed(response.status()))
|
||||
match response {
|
||||
Ok(_) => Ok(()),
|
||||
Err(ureq::Error::Status(code, _)) => Err(Error::UpdateQueryFailed(code)),
|
||||
Err(ureq::Error::Transport(_)) => Err(Error::UpdateQueryTransport),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,6 @@ impl From<id3::Tag> for SongTags {
|
|||
let track_number = tag.track();
|
||||
let year = tag
|
||||
.year()
|
||||
.map(|y| y as i32)
|
||||
.or_else(|| tag.date_released().map(|d| d.year))
|
||||
.or_else(|| tag.original_date_released().map(|d| d.year))
|
||||
.or_else(|| tag.date_recorded().map(|d| d.year));
|
||||
|
@ -316,9 +315,7 @@ fn read_flac(path: &Path) -> Result<SongTags, Error> {
|
|||
let year = vorbis.get("DATE").and_then(|d| d[0].parse::<i32>().ok());
|
||||
let mut streaminfo = tag.get_blocks(metaflac::BlockType::StreamInfo);
|
||||
let duration = match streaminfo.next() {
|
||||
Some(&metaflac::Block::StreamInfo(ref s)) => {
|
||||
Some((s.total_samples as u32 / s.sample_rate) as u32)
|
||||
}
|
||||
Some(metaflac::Block::StreamInfo(s)) => Some(s.total_samples as u32 / s.sample_rate),
|
||||
_ => None,
|
||||
};
|
||||
let has_artwork = tag.pictures().count() > 0;
|
||||
|
|
|
@ -34,7 +34,7 @@ fn update_removes_missing_content() {
|
|||
|
||||
let copy_options = fs_extra::dir::CopyOptions::new();
|
||||
fs_extra::dir::copy(
|
||||
&original_collection_dir,
|
||||
original_collection_dir,
|
||||
&builder.test_directory,
|
||||
©_options,
|
||||
)
|
||||
|
@ -55,7 +55,7 @@ fn update_removes_missing_content() {
|
|||
}
|
||||
|
||||
let khemmis_directory = test_collection_dir.join("Khemmis");
|
||||
std::fs::remove_dir_all(&khemmis_directory).unwrap();
|
||||
std::fs::remove_dir_all(khemmis_directory).unwrap();
|
||||
ctx.index.update().unwrap();
|
||||
{
|
||||
let mut connection = ctx.db.connect().unwrap();
|
||||
|
@ -124,7 +124,7 @@ fn can_flatten_directory() {
|
|||
.build();
|
||||
ctx.index.update().unwrap();
|
||||
let path: PathBuf = [TEST_MOUNT_NAME, "Tobokegao"].iter().collect();
|
||||
let songs = ctx.index.flatten(&path).unwrap();
|
||||
let songs = ctx.index.flatten(path).unwrap();
|
||||
assert_eq!(songs.len(), 8);
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ fn can_flatten_directory_with_shared_prefix() {
|
|||
.build();
|
||||
ctx.index.update().unwrap();
|
||||
let path: PathBuf = [TEST_MOUNT_NAME, "Tobokegao", "Picnic"].iter().collect(); // Prefix of '(Picnic Remixes)'
|
||||
let songs = ctx.index.flatten(&path).unwrap();
|
||||
let songs = ctx.index.flatten(path).unwrap();
|
||||
assert_eq!(songs.len(), 7);
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ impl Manager {
|
|||
|
||||
if let Some(sleep_duration) = new_settings.reindex_every_n_seconds {
|
||||
diesel::update(misc_settings::table)
|
||||
.set(misc_settings::index_sleep_duration_seconds.eq(sleep_duration as i32))
|
||||
.set(misc_settings::index_sleep_duration_seconds.eq(sleep_duration))
|
||||
.execute(&mut connection)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ pub struct ContextBuilder {
|
|||
impl ContextBuilder {
|
||||
pub fn new(test_name: String) -> Self {
|
||||
Self {
|
||||
test_directory: prepare_test_directory(&test_name),
|
||||
test_directory: prepare_test_directory(test_name),
|
||||
config: config::Config::default(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ pub enum Error {
|
|||
CliArgsParsing(getopts::Fail),
|
||||
#[cfg(unix)]
|
||||
#[error("Failed to turn polaris process into a daemon:\n\n{0}")]
|
||||
Daemonize(daemonize::DaemonizeError),
|
||||
Daemonize(daemonize::Error),
|
||||
#[error("Could not create log directory `{0}`:\n\n{1}")]
|
||||
LogDirectoryCreationError(PathBuf, std::io::Error),
|
||||
#[error("Could not create log file `{0}`:\n\n{1}")]
|
||||
|
|
|
@ -12,6 +12,7 @@ use actix_web::{
|
|||
FromRequest, HttpRequest, HttpResponse, Responder, ResponseError,
|
||||
};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
use base64::prelude::*;
|
||||
use futures_util::future::err;
|
||||
use percent_encoding::percent_decode_str;
|
||||
use std::future::Future;
|
||||
|
@ -531,7 +532,7 @@ async fn get_audio(
|
|||
})
|
||||
.await?;
|
||||
|
||||
let named_file = NamedFile::open(&audio_path).map_err(|_| APIError::AudioFileIOError)?;
|
||||
let named_file = NamedFile::open(audio_path).map_err(|_| APIError::AudioFileIOError)?;
|
||||
Ok(MediaFile::new(named_file))
|
||||
}
|
||||
|
||||
|
@ -555,8 +556,7 @@ async fn get_thumbnail(
|
|||
})
|
||||
.await?;
|
||||
|
||||
let named_file =
|
||||
NamedFile::open(&thumbnail_path).map_err(|_| APIError::ThumbnailFileIOError)?;
|
||||
let named_file = NamedFile::open(thumbnail_path).map_err(|_| APIError::ThumbnailFileIOError)?;
|
||||
|
||||
Ok(MediaFile::new(named_file))
|
||||
}
|
||||
|
@ -671,7 +671,8 @@ async fn lastfm_link(
|
|||
let base64_content = percent_decode_str(&payload.content).decode_utf8_lossy();
|
||||
|
||||
// Base64 decode
|
||||
let popup_content = base64::decode(base64_content.as_bytes())
|
||||
let popup_content = BASE64_STANDARD_NO_PAD
|
||||
.decode(base64_content.as_bytes())
|
||||
.map_err(|_| APIError::LastFMLinkContentBase64DecodeError)?;
|
||||
|
||||
// UTF-8 decode
|
||||
|
|
|
@ -162,6 +162,7 @@ impl From<ddns::Error> for APIError {
|
|||
ddns::Error::Database(e) => APIError::Database(e),
|
||||
ddns::Error::DatabaseConnection(e) => e.into(),
|
||||
ddns::Error::UpdateQueryFailed(s) => APIError::DdnsUpdateQueryFailed(s),
|
||||
ddns::Error::UpdateQueryTransport => APIError::DdnsUpdateQueryFailed(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue