Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

use channel to send messages

use channel to send messages

use channel to send messages

use channel to send messages

use channel to send messages

use channel to send messages

use channel to send messages

+223 -65
+58 -25
crates/graphql/src/schema/playback.rs
··· 1 + use std::sync::{mpsc::Sender, Arc, Mutex}; 2 + 1 3 use async_graphql::*; 2 - use rockbox_sys as rb; 4 + use rockbox_sys::{self as rb, events::RockboxCommand}; 3 5 4 6 use crate::schema::objects::track::Track; 5 7 ··· 28 30 29 31 #[Object] 30 32 impl PlaybackMutation { 31 - async fn play(&self, _ctx: &Context<'_>, elapsed: i64, offset: i64) -> String { 32 - rb::playback::play(elapsed, offset); 33 - "play".to_string() 33 + async fn play(&self, ctx: &Context<'_>, elapsed: i64, offset: i64) -> Result<String, Error> { 34 + let cmd = ctx.data::<Arc<Mutex<Sender<RockboxCommand>>>>().unwrap(); 35 + cmd.lock() 36 + .unwrap() 37 + .send(RockboxCommand::Play(elapsed, offset))?; 38 + Ok("play".to_string()) 34 39 } 35 40 36 - async fn pause(&self) -> String { 37 - rb::playback::pause(); 38 - "pause".to_string() 41 + async fn pause(&self, ctx: &Context<'_>) -> Result<String, Error> { 42 + ctx.data::<Arc<Mutex<Sender<RockboxCommand>>>>() 43 + .unwrap() 44 + .lock() 45 + .unwrap() 46 + .send(RockboxCommand::Pause)?; 47 + Ok("pause".to_string()) 39 48 } 40 49 41 - async fn resume(&self) -> String { 42 - rb::playback::resume(); 43 - "resume".to_string() 50 + async fn resume(&self, ctx: &Context<'_>) -> Result<String, Error> { 51 + ctx.data::<Arc<Mutex<Sender<RockboxCommand>>>>() 52 + .unwrap() 53 + .lock() 54 + .unwrap() 55 + .send(RockboxCommand::Resume)?; 56 + Ok("resume".to_string()) 44 57 } 45 58 46 - async fn next(&self) -> String { 47 - rb::playback::next(); 48 - "next".to_string() 59 + async fn next(&self, ctx: &Context<'_>) -> Result<String, Error> { 60 + ctx.data::<Arc<Mutex<Sender<RockboxCommand>>>>() 61 + .unwrap() 62 + .lock() 63 + .unwrap() 64 + .send(RockboxCommand::Next)?; 65 + Ok("next".to_string()) 49 66 } 50 67 51 - async fn previous(&self) -> String { 52 - rb::playback::prev(); 53 - "previous".to_string() 68 + async fn previous(&self, ctx: &Context<'_>) -> Result<String, Error> { 69 + ctx.data::<Arc<Mutex<Sender<RockboxCommand>>>>() 70 + .unwrap() 71 + .lock() 72 + .unwrap() 73 + .send(RockboxCommand::Prev)?; 74 + Ok("previous".to_string()) 54 75 } 55 76 56 - async fn fast_forward_rewind(&self, _ctx: &Context<'_>, new_time: i32) -> String { 57 - rb::playback::ff_rewind(new_time); 58 - "fast_forward_rewind".to_string() 77 + async fn fast_forward_rewind(&self, ctx: &Context<'_>, new_time: i32) -> Result<String, Error> { 78 + ctx.data::<Arc<Mutex<Sender<RockboxCommand>>>>() 79 + .unwrap() 80 + .lock() 81 + .unwrap() 82 + .send(RockboxCommand::FfRewind(new_time))?; 83 + Ok("fast_forward_rewind".to_string()) 59 84 } 60 85 61 - async fn flush_and_reload_tracks(&self) -> String { 62 - rb::playback::flush_and_reload_tracks(); 63 - "flush_and_reload_tracks".to_string() 86 + async fn flush_and_reload_tracks(&self, ctx: &Context<'_>) -> Result<String, Error> { 87 + ctx.data::<Arc<Mutex<Sender<RockboxCommand>>>>() 88 + .unwrap() 89 + .lock() 90 + .unwrap() 91 + .send(RockboxCommand::FlushAndReloadTracks)?; 92 + Ok("flush_and_reload_tracks".to_string()) 64 93 } 65 94 66 - async fn hard_stop(&self) -> String { 67 - rb::playback::hard_stop(); 68 - "hard_stop".to_string() 95 + async fn hard_stop(&self, ctx: &Context<'_>) -> Result<String, Error> { 96 + ctx.data::<Arc<Mutex<Sender<RockboxCommand>>>>() 97 + .unwrap() 98 + .lock() 99 + .unwrap() 100 + .send(RockboxCommand::Stop)?; 101 + Ok("hard_stop".to_string()) 69 102 } 70 103 }
+5 -1
crates/graphql/src/server.rs
··· 1 + use std::sync::{mpsc::Sender, Arc, Mutex}; 2 + 1 3 use actix_cors::Cors; 2 4 use actix_web::{ 3 5 http::header::HOST, ··· 7 9 use async_graphql::{http::GraphiQLSource, EmptySubscription, Schema}; 8 10 use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse}; 9 11 use owo_colors::OwoColorize; 12 + use rockbox_sys::events::RockboxCommand; 10 13 11 14 use crate::{ 12 15 schema::{Mutation, Query}, ··· 43 46 )) 44 47 } 45 48 46 - pub async fn start() -> std::io::Result<()> { 49 + pub async fn start(cmd_tx: Arc<Mutex<Sender<RockboxCommand>>>) -> std::io::Result<()> { 47 50 let schema = Schema::build( 48 51 Query::default(), 49 52 Mutation::default(), 50 53 EmptySubscription::default(), 51 54 ) 55 + .data(cmd_tx) 52 56 .finish(); 53 57 let graphql_port = std::env::var("ROCKBOX_GRAPHQL_PORT").unwrap_or("6062".to_string()); 54 58 let addr = format!("{}:{}", "0.0.0.0", graphql_port);
+52 -12
crates/rpc/src/playback.rs
··· 1 + use std::sync::{mpsc::Sender, Arc, Mutex}; 2 + 1 3 use crate::api::rockbox::v1alpha1::{playback_service_server::PlaybackService, *}; 2 - use rockbox_sys as rb; 3 - use tokio::task; 4 + use rockbox_sys::{self as rb, events::RockboxCommand}; 4 5 5 - #[derive(Default)] 6 - pub struct Playback; 6 + pub struct Playback { 7 + cmd_tx: Arc<Mutex<Sender<RockboxCommand>>>, 8 + } 9 + 10 + impl Playback { 11 + pub fn new(cmd_tx: Arc<Mutex<Sender<RockboxCommand>>>) -> Self { 12 + Self { cmd_tx } 13 + } 14 + } 7 15 8 16 #[tonic::async_trait] 9 17 impl PlaybackService for Playback { ··· 12 20 request: tonic::Request<PlayRequest>, 13 21 ) -> Result<tonic::Response<PlayResponse>, tonic::Status> { 14 22 let params = request.into_inner(); 15 - rb::playback::play(params.elapsed, params.offset); 23 + self.cmd_tx 24 + .lock() 25 + .unwrap() 26 + .send(RockboxCommand::Play(params.elapsed, params.offset)) 27 + .map_err(|_| tonic::Status::internal("Failed to send command"))?; 16 28 Ok(tonic::Response::new(PlayResponse::default())) 17 29 } 18 30 ··· 20 32 &self, 21 33 request: tonic::Request<PauseRequest>, 22 34 ) -> Result<tonic::Response<PauseResponse>, tonic::Status> { 23 - rb::playback::pause(); 35 + self.cmd_tx 36 + .lock() 37 + .unwrap() 38 + .send(RockboxCommand::Pause) 39 + .map_err(|_| tonic::Status::internal("Failed to send command"))?; 24 40 Ok(tonic::Response::new(PauseResponse::default())) 25 41 } 26 42 ··· 28 44 &self, 29 45 request: tonic::Request<ResumeRequest>, 30 46 ) -> Result<tonic::Response<ResumeResponse>, tonic::Status> { 31 - rb::playback::resume(); 47 + self.cmd_tx 48 + .lock() 49 + .unwrap() 50 + .send(RockboxCommand::Resume) 51 + .map_err(|_| tonic::Status::internal("Failed to send command"))?; 32 52 Ok(tonic::Response::new(ResumeResponse::default())) 33 53 } 34 54 ··· 36 56 &self, 37 57 request: tonic::Request<NextRequest>, 38 58 ) -> Result<tonic::Response<NextResponse>, tonic::Status> { 39 - rb::playback::next(); 59 + self.cmd_tx 60 + .lock() 61 + .unwrap() 62 + .send(RockboxCommand::Next) 63 + .map_err(|_| tonic::Status::internal("Failed to send command"))?; 40 64 Ok(tonic::Response::new(NextResponse::default())) 41 65 } 42 66 ··· 44 68 &self, 45 69 request: tonic::Request<PreviousRequest>, 46 70 ) -> Result<tonic::Response<PreviousResponse>, tonic::Status> { 47 - rb::playback::prev(); 71 + self.cmd_tx 72 + .lock() 73 + .unwrap() 74 + .send(RockboxCommand::Prev) 75 + .map_err(|_| tonic::Status::internal("Failed to send command"))?; 48 76 Ok(tonic::Response::new(PreviousResponse::default())) 49 77 } 50 78 ··· 53 81 request: tonic::Request<FastForwardRewindRequest>, 54 82 ) -> Result<tonic::Response<FastForwardRewindResponse>, tonic::Status> { 55 83 let params = request.into_inner(); 56 - rb::playback::ff_rewind(params.new_time); 84 + self.cmd_tx 85 + .lock() 86 + .unwrap() 87 + .send(RockboxCommand::FfRewind(params.new_time)) 88 + .map_err(|_| tonic::Status::internal("Failed to send command"))?; 57 89 Ok(tonic::Response::new(FastForwardRewindResponse::default())) 58 90 } 59 91 ··· 77 109 &self, 78 110 request: tonic::Request<FlushAndReloadTracksRequest>, 79 111 ) -> Result<tonic::Response<FlushAndReloadTracksResponse>, tonic::Status> { 80 - rb::playback::flush_and_reload_tracks(); 112 + self.cmd_tx 113 + .lock() 114 + .unwrap() 115 + .send(RockboxCommand::FlushAndReloadTracks) 116 + .map_err(|_| tonic::Status::internal("Failed to send command"))?; 81 117 Ok(tonic::Response::new(FlushAndReloadTracksResponse::default())) 82 118 } 83 119 ··· 93 129 &self, 94 130 request: tonic::Request<HardStopRequest>, 95 131 ) -> Result<tonic::Response<HardStopResponse>, tonic::Status> { 96 - rb::playback::hard_stop(); 132 + self.cmd_tx 133 + .lock() 134 + .unwrap() 135 + .send(RockboxCommand::Stop) 136 + .map_err(|_| tonic::Status::internal("Failed to send command"))?; 97 137 Ok(tonic::Response::new(HardStopResponse::default())) 98 138 } 99 139 }
+13 -2
crates/rpc/src/playlist.rs
··· 1 + use std::sync::{mpsc::Sender, Arc, Mutex}; 2 + 3 + use rockbox_sys::events::RockboxCommand; 4 + 1 5 use crate::api::rockbox::v1alpha1::{playlist_service_server::PlaylistService, *}; 2 6 3 - #[derive(Default)] 4 - pub struct Playlist; 7 + pub struct Playlist { 8 + cmd_tx: Arc<Mutex<Sender<RockboxCommand>>>, 9 + } 10 + 11 + impl Playlist { 12 + pub fn new(cmd_tx: Arc<Mutex<Sender<RockboxCommand>>>) -> Self { 13 + Self { cmd_tx } 14 + } 15 + } 5 16 6 17 #[tonic::async_trait] 7 18 impl PlaylistService for Playlist {
+8 -3
crates/rpc/src/server.rs
··· 1 1 use std::net::SocketAddr; 2 + use std::sync::mpsc::Sender; 3 + use std::sync::{Arc, Mutex}; 2 4 3 5 use crate::api::rockbox::v1alpha1::browse_service_server::BrowseServiceServer; 4 6 use crate::api::rockbox::v1alpha1::playback_service_server::PlaybackServiceServer; ··· 10 12 use crate::playlist::Playlist; 11 13 use crate::sound::Sound; 12 14 use owo_colors::OwoColorize; 15 + use rockbox_sys::events::RockboxCommand; 13 16 use tonic::transport::Server; 14 17 15 - pub async fn start() -> Result<(), Box<dyn std::error::Error>> { 18 + pub async fn start( 19 + cmd_tx: Arc<Mutex<Sender<RockboxCommand>>>, 20 + ) -> Result<(), Box<dyn std::error::Error>> { 16 21 let rockbox_port: u16 = std::env::var("ROCKBOX_PORT") 17 22 .unwrap_or_else(|_| "6061".to_string()) 18 23 .parse() ··· 36 41 .build_v1alpha()?, 37 42 ) 38 43 .add_service(tonic_web::enable(PlaylistServiceServer::new( 39 - Playlist::default(), 44 + Playlist::new(cmd_tx.clone()), 40 45 ))) 41 46 .add_service(tonic_web::enable(PlaybackServiceServer::new( 42 - Playback::default(), 47 + Playback::new(cmd_tx.clone()), 43 48 ))) 44 49 .add_service(tonic_web::enable(BrowseServiceServer::new( 45 50 Browse::default(),
+44 -6
crates/server/src/lib.rs
··· 1 1 use owo_colors::OwoColorize; 2 - use rockbox_sys as rb; 3 - use std::thread; 2 + use rockbox_sys::{self as rb, events::RockboxCommand}; 3 + use std::{ 4 + sync::{Arc, Mutex}, 5 + thread, 6 + }; 4 7 5 8 #[no_mangle] 6 9 pub extern "C" fn start_server() { ··· 16 19 // Start the server 17 20 println!("{}", BANNER.yellow()); 18 21 22 + let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<RockboxCommand>(); 23 + let cmd_tx = Arc::new(Mutex::new(cmd_tx)); 24 + 19 25 thread::spawn(move || { 20 - rb::playback::pause(); 26 + while let Ok(event) = cmd_rx.recv() { 27 + match event { 28 + RockboxCommand::Play(elapsed, offset) => { 29 + rb::playback::play(elapsed, offset); 30 + } 31 + RockboxCommand::Pause => { 32 + rb::playback::pause(); 33 + } 34 + RockboxCommand::Resume => { 35 + rb::playback::resume(); 36 + } 37 + RockboxCommand::Next => { 38 + rb::playback::next(); 39 + } 40 + RockboxCommand::Prev => { 41 + rb::playback::prev(); 42 + } 43 + RockboxCommand::FfRewind(newtime) => { 44 + rb::playback::ff_rewind(newtime); 45 + } 46 + RockboxCommand::FlushAndReloadTracks => { 47 + rb::playback::flush_and_reload_tracks(); 48 + } 49 + RockboxCommand::Stop => { 50 + rb::playback::hard_stop(); 51 + } 52 + } 53 + } 54 + }); 55 + 56 + let cloned_cmd_tx = cmd_tx.clone(); 57 + 58 + thread::spawn(move || { 21 59 let runtime = tokio::runtime::Builder::new_current_thread() 22 60 .enable_all() 23 61 .build() 24 62 .unwrap(); 25 - match runtime.block_on(rockbox_rpc::server::start()) { 63 + match runtime.block_on(rockbox_rpc::server::start(cmd_tx.clone())) { 26 64 Ok(_) => {} 27 65 Err(e) => { 28 66 eprintln!("Error starting server: {}", e); ··· 30 68 } 31 69 }); 32 70 33 - thread::spawn(|| { 71 + thread::spawn(move || { 34 72 let runtime = tokio::runtime::Builder::new_current_thread() 35 73 .enable_all() 36 74 .build() 37 75 .unwrap(); 38 - match runtime.block_on(rockbox_graphql::server::start()) { 76 + match runtime.block_on(rockbox_graphql::server::start(cloned_cmd_tx.clone())) { 39 77 Ok(_) => {} 40 78 Err(e) => { 41 79 eprintln!("Error starting server: {}", e);
+10
crates/sys/src/events.rs
··· 1 + pub enum RockboxCommand { 2 + Pause, 3 + Play(i64, i64), 4 + Resume, 5 + Next, 6 + Prev, 7 + FfRewind(i32), 8 + FlushAndReloadTracks, 9 + Stop, 10 + }
+9 -8
crates/sys/src/lib.rs
··· 2 2 3 3 pub mod browse; 4 4 pub mod dir; 5 + pub mod events; 5 6 pub mod file; 6 7 pub mod menu; 7 8 pub mod metadata; ··· 604 605 605 606 extern "C" { 606 607 // Playback control 607 - fn audio_pause(); 608 - fn audio_play(elapsed: c_long, offset: c_long); 609 - fn audio_resume(); 610 - fn audio_next(); 611 - fn audio_prev(); 612 - fn audio_ff_rewind(newtime: c_int); 608 + fn audio_pause() -> c_void; 609 + fn audio_play(elapsed: c_long, offset: c_long) -> c_void; 610 + fn audio_resume() -> c_void; 611 + fn audio_next() -> c_void; 612 + fn audio_prev() -> c_void; 613 + fn audio_ff_rewind(newtime: c_int) -> c_void; 613 614 fn audio_next_track() -> *mut Mp3Entry; 614 615 fn audio_status() -> c_int; 615 616 fn audio_current_track() -> *mut Mp3Entry; 616 - fn audio_flush_and_reload_tracks(); 617 + fn audio_flush_and_reload_tracks() -> c_void; 617 618 fn audio_get_file_pos() -> c_int; 618 - fn audio_hard_stop(); 619 + fn audio_hard_stop() -> c_void; 619 620 620 621 // Playlist control 621 622 fn playlist_get_current() -> *mut PlaylistInfo;
+24 -8
crates/sys/src/playback.rs
··· 1 1 use crate::Mp3Entry; 2 2 3 3 pub fn pause() { 4 - unsafe { crate::audio_pause() } 4 + unsafe { 5 + crate::audio_pause(); 6 + } 5 7 } 6 8 7 9 pub fn play(elapsed: i64, offset: i64) { 8 - unsafe { crate::audio_play(elapsed, offset) } 10 + unsafe { 11 + crate::audio_play(elapsed, offset); 12 + } 9 13 } 10 14 11 15 pub fn resume() { 12 - unsafe { crate::audio_resume() } 16 + unsafe { 17 + crate::audio_resume(); 18 + } 13 19 } 14 20 15 21 pub fn next() { 16 - unsafe { crate::audio_next() } 22 + unsafe { 23 + crate::audio_next(); 24 + } 17 25 } 18 26 19 27 pub fn prev() { 20 - unsafe { crate::audio_prev() } 28 + unsafe { 29 + crate::audio_prev(); 30 + } 21 31 } 22 32 23 33 pub fn ff_rewind(newtime: i32) { 24 - unsafe { crate::audio_ff_rewind(newtime) } 34 + unsafe { 35 + crate::audio_ff_rewind(newtime); 36 + } 25 37 } 26 38 27 39 pub fn next_track() -> Mp3Entry { ··· 39 51 } 40 52 41 53 pub fn flush_and_reload_tracks() { 42 - unsafe { crate::audio_flush_and_reload_tracks() } 54 + unsafe { 55 + crate::audio_flush_and_reload_tracks(); 56 + } 43 57 } 44 58 45 59 pub fn get_file_pos() -> i32 { ··· 47 61 } 48 62 49 63 pub fn hard_stop() { 50 - unsafe { crate::audio_hard_stop() } 64 + unsafe { 65 + crate::audio_hard_stop(); 66 + } 51 67 }