diff --git a/Cargo.lock b/Cargo.lock index 50236f6..f5d8086 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,9 +772,11 @@ dependencies = [ "configparser", "lofty", "md5", + "mime_guess", "r2d2", "r2d2_sqlite", "rusqlite", + "rust-embed", "serde", "serde_json", ] @@ -1055,6 +1057,42 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "actix-web", + "mime_guess", + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1076,6 +1114,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scheduled-thread-pool" version = "0.2.7" @@ -1152,6 +1199,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1374,12 +1432,31 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 4c23fa0..e75fda6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,10 @@ base64 = "0.21.4" configparser = "3.0.3" lofty = "0.15.0" md5 = "0.7.0" +mime_guess = "2.0.5" r2d2 = "0.8.10" r2d2_sqlite = "0.22.0" rusqlite = { version = "0.29.0", features = ["bundled"] } +rust-embed = { version = "8.5.0", features = ["actix"] } serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.105" diff --git a/README.md b/README.md index fce1cbc..3388e15 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,18 @@ Loklang is a local audio streaming service that can be used to access audio files on the host from any device on the network. +## Configuration + +Create a file named `config.ini` in the same directory as the `loklang.exe` file and add any supported config options to that file. +```ini +[config] +rootdir = "path/to/music" +``` + +### Currently Supported Options + +- `rootdir`: Root directory for the music files + ## Development 1. Clone this repo diff --git a/config.example.ini b/config.example.ini new file mode 100644 index 0000000..3a8d780 --- /dev/null +++ b/config.example.ini @@ -0,0 +1,2 @@ +[config] +rootdir = D:\Music\Test \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 710971b..56ea06f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,24 +3,23 @@ mod metadata; mod routes; mod utils; -use actix_web::{ - web::{scope, Data}, - App, HttpServer, -}; -use actix_files::Files; +use actix_web::{web::Data, App, HttpServer}; use configparser::ini::Ini; use r2d2::Pool; use r2d2_sqlite::{self, SqliteConnectionManager}; -use routes::{ - get_albums, get_artists, get_picture, get_song_by_id, get_songs, get_songs_by_album, - get_songs_by_artist, get_stream_by_id, -}; +use routes::register; -use std::{fs::create_dir_all, collections::HashMap}; use std::path::PathBuf; +use std::{collections::HashMap, fs::create_dir_all}; + +use rust_embed::Embed; + +#[derive(Embed)] +#[folder = "views/"] +pub struct Asset; async fn setup_db(pool: &Pool, config: &HashMap>) { db::create_tables(pool).await; @@ -64,17 +63,9 @@ async fn main() -> std::io::Result<()> { println!("Started server at PORT 8000!"); HttpServer::new(move || { - App::new().app_data(Data::new(pool.clone())).service( - scope("/api/v1") - .service(get_songs) - .service(get_song_by_id) - .service(get_albums) - .service(get_songs_by_album) - .service(get_artists) - .service(get_songs_by_artist) - .service(get_stream_by_id) - .service(get_picture), - ).service(Files::new("/", "./views").index_file("index.html")) + App::new() + .app_data(Data::new(pool.clone())) + .configure(register) }) .bind(("0.0.0.0", 8000))? .run() diff --git a/src/routes.rs b/src/routes/api.rs similarity index 90% rename from src/routes.rs rename to src/routes/api.rs index af77403..09087ad 100644 --- a/src/routes.rs +++ b/src/routes/api.rs @@ -17,7 +17,6 @@ use crate::{ type DataDB = Data>; -// GET songs #[get("/songs")] pub async fn get_songs(db: DataDB) -> HttpResponse { let res = handle_song_action(&db, SongActions::GetAllSongs) @@ -108,3 +107,15 @@ pub async fn get_picture(data: Path, db: DataDB) -> HttpResponse { HttpResponse::Ok().body(res[0].clone()) } } + +pub fn register(config: &mut actix_web::web::ServiceConfig) { + config + .service(get_songs) + .service(get_song_by_id) + .service(get_albums) + .service(get_songs_by_album) + .service(get_artists) + .service(get_songs_by_artist) + .service(get_stream_by_id) + .service(get_picture); +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs new file mode 100644 index 0000000..1e497c8 --- /dev/null +++ b/src/routes/mod.rs @@ -0,0 +1,12 @@ +pub mod api; +pub mod views; + +use actix_web::web::{scope, ServiceConfig}; + +use api::register as register_api; +use views::register as register_views; + +pub fn register(config: &mut ServiceConfig) { + config.service(scope("/api/v1").configure(register_api)); + config.service(scope("").configure(register_views)); +} diff --git a/src/routes/views.rs b/src/routes/views.rs new file mode 100644 index 0000000..6e584e8 --- /dev/null +++ b/src/routes/views.rs @@ -0,0 +1,21 @@ +use crate::utils::static_resolver::handle_embedded_file; +use actix_web::{ + get, + web::{Path, ServiceConfig}, + HttpResponse, Responder, +}; + +// View Routes +#[get("/")] +pub async fn index() -> HttpResponse { + handle_embedded_file("index.html") +} + +#[get("/dist/{_:.*}")] +pub async fn dist(path: Path) -> impl Responder { + handle_embedded_file(path.as_str()) +} + +pub fn register(config: &mut ServiceConfig) { + config.service(index).service(dist); +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 8cef5ef..290755f 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,5 @@ +pub mod static_resolver; + use std::{fs, path::PathBuf}; use crate::metadata; diff --git a/src/utils/static_resolver.rs b/src/utils/static_resolver.rs new file mode 100644 index 0000000..c07ab80 --- /dev/null +++ b/src/utils/static_resolver.rs @@ -0,0 +1,12 @@ +use crate::Asset; +use actix_web::HttpResponse; +use mime_guess::from_path; + +pub fn handle_embedded_file(path: &str) -> HttpResponse { + match Asset::get(path) { + Some(content) => HttpResponse::Ok() + .content_type(from_path(path).first_or_octet_stream().as_ref()) + .body(content.data.into_owned()), + None => HttpResponse::NotFound().body("404 Not Found"), + } +} \ No newline at end of file diff --git a/views/index.html b/views/index.html index f66e3d3..3c427b0 100644 --- a/views/index.html +++ b/views/index.html @@ -5,7 +5,7 @@ Loklang - +