This commit is contained in:
21
.gitea/workflows/ci.yml
Normal file
21
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- run: cargo fmt --check
|
||||||
|
|
||||||
|
- run: cargo clippy --workspace -- -D warnings
|
||||||
|
|
||||||
|
- run: cargo build --workspace
|
||||||
|
|
||||||
|
- run: cargo test --workspace
|
||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2934,7 +2934,9 @@ dependencies = [
|
|||||||
"dirs",
|
"dirs",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
"tempfile",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
26
justfile
Normal file
26
justfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
default:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
build:
|
||||||
|
cargo build --workspace
|
||||||
|
|
||||||
|
test:
|
||||||
|
cargo test --workspace
|
||||||
|
|
||||||
|
lint:
|
||||||
|
cargo clippy --workspace -- -D warnings
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
cargo fmt
|
||||||
|
|
||||||
|
check: fmt lint test
|
||||||
|
|
||||||
|
run:
|
||||||
|
cargo run --bin shanty
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
cd shanty-web/frontend && trunk build
|
||||||
|
|
||||||
|
dev: frontend run
|
||||||
|
|
||||||
|
ci: check build
|
||||||
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
@@ -9,3 +9,7 @@ serde = { workspace = true }
|
|||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
dirs = "6"
|
dirs = "6"
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3"
|
||||||
|
tracing-subscriber = { workspace = true }
|
||||||
|
|||||||
@@ -164,15 +164,33 @@ fn default_organization_format() -> String {
|
|||||||
"{artist}/{album}/{track_number} - {title}.{ext}".to_string()
|
"{artist}/{album}/{track_number} - {title}.{ext}".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_port() -> u16 { 8085 }
|
fn default_port() -> u16 {
|
||||||
fn default_bind() -> String { "0.0.0.0".to_string() }
|
8085
|
||||||
fn default_confidence() -> f64 { 0.8 }
|
}
|
||||||
fn default_format() -> String { "opus".to_string() }
|
fn default_bind() -> String {
|
||||||
fn default_search_source() -> String { "ytmusic".to_string() }
|
"0.0.0.0".to_string()
|
||||||
fn default_true() -> bool { true }
|
}
|
||||||
fn default_rate_limit() -> u32 { 450 }
|
fn default_confidence() -> f64 {
|
||||||
fn default_rate_limit_auth() -> u32 { 1800 }
|
0.8
|
||||||
fn default_concurrency() -> usize { 4 }
|
}
|
||||||
|
fn default_format() -> String {
|
||||||
|
"opus".to_string()
|
||||||
|
}
|
||||||
|
fn default_search_source() -> String {
|
||||||
|
"ytmusic".to_string()
|
||||||
|
}
|
||||||
|
fn default_true() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn default_rate_limit() -> u32 {
|
||||||
|
450
|
||||||
|
}
|
||||||
|
fn default_rate_limit_auth() -> u32 {
|
||||||
|
1800
|
||||||
|
}
|
||||||
|
fn default_concurrency() -> usize {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
|
||||||
// --- Loading and Saving ---
|
// --- Loading and Saving ---
|
||||||
|
|
||||||
@@ -223,8 +241,8 @@ impl AppConfig {
|
|||||||
std::fs::create_dir_all(parent)
|
std::fs::create_dir_all(parent)
|
||||||
.map_err(|e| format!("failed to create config directory: {e}"))?;
|
.map_err(|e| format!("failed to create config directory: {e}"))?;
|
||||||
}
|
}
|
||||||
let yaml = serde_yaml::to_string(self)
|
let yaml =
|
||||||
.map_err(|e| format!("failed to serialize config: {e}"))?;
|
serde_yaml::to_string(self).map_err(|e| format!("failed to serialize config: {e}"))?;
|
||||||
std::fs::write(&config_path, yaml)
|
std::fs::write(&config_path, yaml)
|
||||||
.map_err(|e| format!("failed to write config to {}: {e}", config_path.display()))?;
|
.map_err(|e| format!("failed to write config to {}: {e}", config_path.display()))?;
|
||||||
tracing::info!(path = %config_path.display(), "config saved");
|
tracing::info!(path = %config_path.display(), "config saved");
|
||||||
@@ -241,10 +259,10 @@ impl AppConfig {
|
|||||||
if let Ok(v) = std::env::var("SHANTY_DOWNLOAD_PATH") {
|
if let Ok(v) = std::env::var("SHANTY_DOWNLOAD_PATH") {
|
||||||
config.download_path = PathBuf::from(v);
|
config.download_path = PathBuf::from(v);
|
||||||
}
|
}
|
||||||
if let Ok(v) = std::env::var("SHANTY_WEB_PORT") {
|
if let Ok(v) = std::env::var("SHANTY_WEB_PORT")
|
||||||
if let Ok(port) = v.parse() {
|
&& let Ok(port) = v.parse()
|
||||||
config.web.port = port;
|
{
|
||||||
}
|
config.web.port = port;
|
||||||
}
|
}
|
||||||
if let Ok(v) = std::env::var("SHANTY_WEB_BIND") {
|
if let Ok(v) = std::env::var("SHANTY_WEB_BIND") {
|
||||||
config.web.bind = v;
|
config.web.bind = v;
|
||||||
|
|||||||
55
shanty-config/tests/integration.rs
Normal file
55
shanty-config/tests/integration.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use shanty_config::AppConfig;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default_config() {
|
||||||
|
let config = AppConfig::default();
|
||||||
|
assert_eq!(config.web.port, 8085);
|
||||||
|
assert_eq!(config.web.bind, "0.0.0.0");
|
||||||
|
assert_eq!(config.tagging.confidence, 0.8);
|
||||||
|
assert!(config.tagging.write_tags);
|
||||||
|
assert!(!config.tagging.auto_tag);
|
||||||
|
assert_eq!(config.download.format, "opus");
|
||||||
|
assert_eq!(config.download.search_source, "ytmusic");
|
||||||
|
assert_eq!(config.download.rate_limit, 450);
|
||||||
|
assert_eq!(config.download.rate_limit_auth, 1800);
|
||||||
|
assert_eq!(config.indexing.concurrency, 4);
|
||||||
|
assert!(config.allowed_secondary_types.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_missing_file() {
|
||||||
|
// Loading from a nonexistent path should return defaults
|
||||||
|
let config = AppConfig::load(Some("/tmp/shanty-test-nonexistent-config.yaml"));
|
||||||
|
assert_eq!(config.web.port, 8085);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_save_and_load_roundtrip() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let path = dir.path().join("config.yaml");
|
||||||
|
let path_str = path.to_string_lossy().to_string();
|
||||||
|
|
||||||
|
let mut config = AppConfig::default();
|
||||||
|
config.web.port = 9999;
|
||||||
|
config.tagging.confidence = 0.95;
|
||||||
|
config.download.format = "flac".to_string();
|
||||||
|
config.indexing.concurrency = 8;
|
||||||
|
|
||||||
|
config.save(Some(&path_str)).unwrap();
|
||||||
|
|
||||||
|
let loaded = AppConfig::load(Some(&path_str));
|
||||||
|
assert_eq!(loaded.web.port, 9999);
|
||||||
|
assert_eq!(loaded.tagging.confidence, 0.95);
|
||||||
|
assert_eq!(loaded.download.format, "flac");
|
||||||
|
assert_eq!(loaded.indexing.concurrency, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_config_path_resolution() {
|
||||||
|
let path = AppConfig::config_path(Some("/custom/path.yaml"));
|
||||||
|
assert_eq!(path.to_string_lossy(), "/custom/path.yaml");
|
||||||
|
|
||||||
|
// Default path should end with config.yaml
|
||||||
|
let default = AppConfig::config_path(None);
|
||||||
|
assert!(default.to_string_lossy().ends_with("config.yaml"));
|
||||||
|
}
|
||||||
Submodule shanty-db updated: 37410ce216...c6452609d6
Submodule shanty-dl updated: a57df38eb1...2592651c9a
Submodule shanty-index updated: 1d1674e3a1...3494de1133
Submodule shanty-org updated: 3159ee51ad...a2152cbf8d
Submodule shanty-search updated: d09557d953...d358b79a6b
Submodule shanty-tag updated: 4400cbc1cb...5957d69e7d
Submodule shanty-watch updated: 3d01fa85c9...0b336789da
Submodule shanty-web updated: 1f36374394...93392db27c
@@ -1,5 +1,5 @@
|
|||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{App, HttpServer, web};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use tracing_actix_web::TracingLogger;
|
use tracing_actix_web::TracingLogger;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
@@ -79,9 +79,8 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
static_dir
|
static_dir
|
||||||
} else {
|
} else {
|
||||||
// Check next to shanty-web crate root (for development)
|
// Check next to shanty-web crate root (for development)
|
||||||
let dev_path = std::path::PathBuf::from(
|
let dev_path =
|
||||||
concat!(env!("CARGO_MANIFEST_DIR"), "/shanty-web/static"),
|
std::path::PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/shanty-web/static"));
|
||||||
);
|
|
||||||
if dev_path.is_dir() {
|
if dev_path.is_dir() {
|
||||||
dev_path
|
dev_path
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user