diff --git a/Cargo.lock b/Cargo.lock index 6716706..17bfa9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,60 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "axum" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -662,6 +716,87 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -935,6 +1070,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -951,6 +1092,12 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.8.7" @@ -1231,6 +1378,7 @@ name = "rate_music" version = "0.1.0" dependencies = [ "anyhow", + "axum", "crossterm", "mpris", "ratatui", @@ -1350,6 +1498,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1760,6 +1918,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "synom" version = "0.11.3" @@ -1896,6 +2060,34 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.41" diff --git a/Cargo.toml b/Cargo.toml index dae5297..dafedc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,12 +7,13 @@ edition = "2021" [dependencies] anyhow = "1.0.81" -crossterm = "0.28.0" +crossterm = "0.28.1" mpris = "2.0.1" ratatui = "0.29" sqlx = { version = "0.8.3", features = ["sqlite", "runtime-tokio"] } tokio = { version = "1.36.0", features = ["rt", "macros", "rt-multi-thread"] } tui-textarea = "0.7.0" +axum = "0.8.3" [profile.optimize] inherits = "release" diff --git a/src/database.rs b/src/database.rs index d92323c..2c0d542 100644 --- a/src/database.rs +++ b/src/database.rs @@ -3,6 +3,7 @@ use std::i64; use anyhow::Result; use sqlx::{Row, SqlitePool}; +#[derive(Clone)] pub struct Database { pool: SqlitePool, } diff --git a/src/http_server.rs b/src/http_server.rs new file mode 100644 index 0000000..394a617 --- /dev/null +++ b/src/http_server.rs @@ -0,0 +1,63 @@ +use axum::{ + extract::{Path, State}, + http::StatusCode, + response::{IntoResponse, Response}, + routing::get, + Router, +}; +use tokio::sync::broadcast::Sender; + +use crate::database::Database; + +#[derive(Clone)] +struct SharedState { + database: Database, + mpris_sender: Sender<(String, String)>, +} + +pub async fn http_serve(database: &Database, mpris_producer: Sender<(String, String)>) { + let database = database.clone(); + let shared_state = SharedState { + database, + mpris_sender: mpris_producer, + }; + + let app = Router::new() + .route("/", get(root)) + .route("/{length}", get(add_rating)) + .with_state(shared_state); + + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + axum::serve(listener, app).await.unwrap(); +} + +async fn root() -> impl IntoResponse { + (StatusCode::OK, "Use /{user_id}/{rating}") +} + +async fn add_rating( + Path((user_id, rating)): Path<(i64, i64)>, + State(shared): State, +) -> Response { + // Get the current interpret and track from the broadcast channel + match shared.mpris_sender.subscribe().recv().await { + Ok((interpret, track)) => { + // write to db + match shared + .database + .user_add_rating(user_id, &interpret, &track, rating) + .await + { + Ok(_) => (StatusCode::OK, "Done.").into_response(), + Err(e) => { + println!("HTTP error: {e}"); + (StatusCode::BAD_REQUEST, e.to_string()).into_response() + } + } + } + Err(e) => { + println!("mpris error: {e}"); + (StatusCode::BAD_REQUEST, e.to_string()).into_response() + } + } +} diff --git a/src/main.rs b/src/main.rs index 5a700d8..31729a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ mod database; +mod http_server; mod player; mod userinterface; -use std::env::args; -use tokio; +use std::{env::args, sync::Arc}; + +use tokio::sync::broadcast; #[tokio::main] async fn main() { @@ -11,9 +13,11 @@ async fn main() { let player = player::MprisPlayer::new().expect("Could not create player"); // Initialize the database - let db = database::Database::new() - .await - .expect("Could not create database"); + let db = Arc::new( + database::Database::new() + .await + .expect("Could not create database"), + ); // Create the tables on an empty database db.create_tables().await.unwrap(); @@ -57,6 +61,27 @@ async fn main() { return; } + // Instantiate the mpris channel + let (mpris_tx, _) = broadcast::channel(32); + + let mpris_tx_http = mpris_tx.clone(); + tokio::spawn(async move { + let player = player::MprisPlayer::new().expect("Could not create player"); + loop { + if let Ok((interpret, track)) = player.get_interpret_and_track() { + if let Err(e) = mpris_tx.send((interpret, track)) { + println!("Error sending interpret and track: {e}"); + } + } + } + }); + + // Create the HTTP backend + let db_http = db.clone(); + tokio::spawn(async move { + http_server::http_serve(&db_http, mpris_tx_http).await; + }); + loop { let (usernumber, userrating) = userinterface::get_user_rating(&db) .await @@ -78,3 +103,7 @@ async fn main() { } } } + +fn mpris_player_producer() { + // +}