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.

Add PlayMusicDirectory RPC and client/server support

Implement PlayMusicDirectory across protobuf, Rust server/client, and
the macOS client. Server loads ~/Music, supports shuffle, recurse and
start position, posts the playlist to Rockbox and optionally shuffles
before starting playback.

+769 -42
+78 -2
cli/src/api/rockbox.v1alpha1.rs
··· 1811 1811 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1812 1812 pub struct PlayResponse {} 1813 1813 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1814 - pub struct PauseRequest {} 1815 - #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1816 1814 pub struct PlayOrPauseRequest {} 1817 1815 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1818 1816 pub struct PlayOrPauseResponse {} 1817 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1818 + pub struct PauseRequest {} 1819 1819 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1820 1820 pub struct PauseResponse {} 1821 1821 #[derive(Clone, Copy, PartialEq, ::prost::Message)] ··· 2008 2008 pub position: ::core::option::Option<i32>, 2009 2009 } 2010 2010 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2011 + pub struct PlayMusicDirectoryRequest { 2012 + #[prost(bool, optional, tag = "1")] 2013 + pub shuffle: ::core::option::Option<bool>, 2014 + #[prost(bool, optional, tag = "2")] 2015 + pub recurse: ::core::option::Option<bool>, 2016 + #[prost(int32, optional, tag = "3")] 2017 + pub position: ::core::option::Option<i32>, 2018 + } 2019 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2011 2020 pub struct PlayDirectoryResponse {} 2021 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2022 + pub struct PlayMusicDirectoryResponse {} 2012 2023 #[derive(Clone, PartialEq, ::prost::Message)] 2013 2024 pub struct PlayTrackRequest { 2014 2025 #[prost(string, tag = "1")] ··· 2434 2445 )); 2435 2446 self.inner.unary(req, path, codec).await 2436 2447 } 2448 + pub async fn play_music_directory( 2449 + &mut self, 2450 + request: impl tonic::IntoRequest<super::PlayMusicDirectoryRequest>, 2451 + ) -> std::result::Result<tonic::Response<super::PlayMusicDirectoryResponse>, tonic::Status> 2452 + { 2453 + self.inner.ready().await.map_err(|e| { 2454 + tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 2455 + })?; 2456 + let codec = tonic::codec::ProstCodec::default(); 2457 + let path = http::uri::PathAndQuery::from_static( 2458 + "/rockbox.v1alpha1.PlaybackService/PlayMusicDirectory", 2459 + ); 2460 + let mut req = request.into_request(); 2461 + req.extensions_mut().insert(GrpcMethod::new( 2462 + "rockbox.v1alpha1.PlaybackService", 2463 + "PlayMusicDirectory", 2464 + )); 2465 + self.inner.unary(req, path, codec).await 2466 + } 2437 2467 pub async fn play_track( 2438 2468 &mut self, 2439 2469 request: impl tonic::IntoRequest<super::PlayTrackRequest>, ··· 2635 2665 &self, 2636 2666 request: tonic::Request<super::PlayDirectoryRequest>, 2637 2667 ) -> std::result::Result<tonic::Response<super::PlayDirectoryResponse>, tonic::Status>; 2668 + async fn play_music_directory( 2669 + &self, 2670 + request: tonic::Request<super::PlayMusicDirectoryRequest>, 2671 + ) -> std::result::Result<tonic::Response<super::PlayMusicDirectoryResponse>, tonic::Status>; 2638 2672 async fn play_track( 2639 2673 &self, 2640 2674 request: tonic::Request<super::PlayTrackRequest>, ··· 3415 3449 let inner = self.inner.clone(); 3416 3450 let fut = async move { 3417 3451 let method = PlayDirectorySvc(inner); 3452 + let codec = tonic::codec::ProstCodec::default(); 3453 + let mut grpc = tonic::server::Grpc::new(codec) 3454 + .apply_compression_config( 3455 + accept_compression_encodings, 3456 + send_compression_encodings, 3457 + ) 3458 + .apply_max_message_size_config( 3459 + max_decoding_message_size, 3460 + max_encoding_message_size, 3461 + ); 3462 + let res = grpc.unary(method, req).await; 3463 + Ok(res) 3464 + }; 3465 + Box::pin(fut) 3466 + } 3467 + "/rockbox.v1alpha1.PlaybackService/PlayMusicDirectory" => { 3468 + #[allow(non_camel_case_types)] 3469 + struct PlayMusicDirectorySvc<T: PlaybackService>(pub Arc<T>); 3470 + impl<T: PlaybackService> 3471 + tonic::server::UnaryService<super::PlayMusicDirectoryRequest> 3472 + for PlayMusicDirectorySvc<T> 3473 + { 3474 + type Response = super::PlayMusicDirectoryResponse; 3475 + type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 3476 + fn call( 3477 + &mut self, 3478 + request: tonic::Request<super::PlayMusicDirectoryRequest>, 3479 + ) -> Self::Future { 3480 + let inner = Arc::clone(&self.0); 3481 + let fut = async move { 3482 + <T as PlaybackService>::play_music_directory(&inner, request).await 3483 + }; 3484 + Box::pin(fut) 3485 + } 3486 + } 3487 + let accept_compression_encodings = self.accept_compression_encodings; 3488 + let send_compression_encodings = self.send_compression_encodings; 3489 + let max_decoding_message_size = self.max_decoding_message_size; 3490 + let max_encoding_message_size = self.max_encoding_message_size; 3491 + let inner = self.inner.clone(); 3492 + let fut = async move { 3493 + let method = PlayMusicDirectorySvc(inner); 3418 3494 let codec = tonic::codec::ProstCodec::default(); 3419 3495 let mut grpc = tonic::server::Grpc::new(codec) 3420 3496 .apply_compression_config(
cli/src/api/rockbox_descriptor.bin

This is a binary file and will not be displayed.

+78 -2
crates/controls/src/api/rockbox.v1alpha1.rs
··· 1811 1811 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1812 1812 pub struct PlayResponse {} 1813 1813 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1814 - pub struct PauseRequest {} 1815 - #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1816 1814 pub struct PlayOrPauseRequest {} 1817 1815 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1818 1816 pub struct PlayOrPauseResponse {} 1817 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1818 + pub struct PauseRequest {} 1819 1819 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1820 1820 pub struct PauseResponse {} 1821 1821 #[derive(Clone, Copy, PartialEq, ::prost::Message)] ··· 2008 2008 pub position: ::core::option::Option<i32>, 2009 2009 } 2010 2010 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2011 + pub struct PlayMusicDirectoryRequest { 2012 + #[prost(bool, optional, tag = "1")] 2013 + pub shuffle: ::core::option::Option<bool>, 2014 + #[prost(bool, optional, tag = "2")] 2015 + pub recurse: ::core::option::Option<bool>, 2016 + #[prost(int32, optional, tag = "3")] 2017 + pub position: ::core::option::Option<i32>, 2018 + } 2019 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2011 2020 pub struct PlayDirectoryResponse {} 2021 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2022 + pub struct PlayMusicDirectoryResponse {} 2012 2023 #[derive(Clone, PartialEq, ::prost::Message)] 2013 2024 pub struct PlayTrackRequest { 2014 2025 #[prost(string, tag = "1")] ··· 2434 2445 )); 2435 2446 self.inner.unary(req, path, codec).await 2436 2447 } 2448 + pub async fn play_music_directory( 2449 + &mut self, 2450 + request: impl tonic::IntoRequest<super::PlayMusicDirectoryRequest>, 2451 + ) -> std::result::Result<tonic::Response<super::PlayMusicDirectoryResponse>, tonic::Status> 2452 + { 2453 + self.inner.ready().await.map_err(|e| { 2454 + tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 2455 + })?; 2456 + let codec = tonic::codec::ProstCodec::default(); 2457 + let path = http::uri::PathAndQuery::from_static( 2458 + "/rockbox.v1alpha1.PlaybackService/PlayMusicDirectory", 2459 + ); 2460 + let mut req = request.into_request(); 2461 + req.extensions_mut().insert(GrpcMethod::new( 2462 + "rockbox.v1alpha1.PlaybackService", 2463 + "PlayMusicDirectory", 2464 + )); 2465 + self.inner.unary(req, path, codec).await 2466 + } 2437 2467 pub async fn play_track( 2438 2468 &mut self, 2439 2469 request: impl tonic::IntoRequest<super::PlayTrackRequest>, ··· 2635 2665 &self, 2636 2666 request: tonic::Request<super::PlayDirectoryRequest>, 2637 2667 ) -> std::result::Result<tonic::Response<super::PlayDirectoryResponse>, tonic::Status>; 2668 + async fn play_music_directory( 2669 + &self, 2670 + request: tonic::Request<super::PlayMusicDirectoryRequest>, 2671 + ) -> std::result::Result<tonic::Response<super::PlayMusicDirectoryResponse>, tonic::Status>; 2638 2672 async fn play_track( 2639 2673 &self, 2640 2674 request: tonic::Request<super::PlayTrackRequest>, ··· 3415 3449 let inner = self.inner.clone(); 3416 3450 let fut = async move { 3417 3451 let method = PlayDirectorySvc(inner); 3452 + let codec = tonic::codec::ProstCodec::default(); 3453 + let mut grpc = tonic::server::Grpc::new(codec) 3454 + .apply_compression_config( 3455 + accept_compression_encodings, 3456 + send_compression_encodings, 3457 + ) 3458 + .apply_max_message_size_config( 3459 + max_decoding_message_size, 3460 + max_encoding_message_size, 3461 + ); 3462 + let res = grpc.unary(method, req).await; 3463 + Ok(res) 3464 + }; 3465 + Box::pin(fut) 3466 + } 3467 + "/rockbox.v1alpha1.PlaybackService/PlayMusicDirectory" => { 3468 + #[allow(non_camel_case_types)] 3469 + struct PlayMusicDirectorySvc<T: PlaybackService>(pub Arc<T>); 3470 + impl<T: PlaybackService> 3471 + tonic::server::UnaryService<super::PlayMusicDirectoryRequest> 3472 + for PlayMusicDirectorySvc<T> 3473 + { 3474 + type Response = super::PlayMusicDirectoryResponse; 3475 + type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 3476 + fn call( 3477 + &mut self, 3478 + request: tonic::Request<super::PlayMusicDirectoryRequest>, 3479 + ) -> Self::Future { 3480 + let inner = Arc::clone(&self.0); 3481 + let fut = async move { 3482 + <T as PlaybackService>::play_music_directory(&inner, request).await 3483 + }; 3484 + Box::pin(fut) 3485 + } 3486 + } 3487 + let accept_compression_encodings = self.accept_compression_encodings; 3488 + let send_compression_encodings = self.send_compression_encodings; 3489 + let max_decoding_message_size = self.max_decoding_message_size; 3490 + let max_encoding_message_size = self.max_encoding_message_size; 3491 + let inner = self.inner.clone(); 3492 + let fut = async move { 3493 + let method = PlayMusicDirectorySvc(inner); 3418 3494 let codec = tonic::codec::ProstCodec::default(); 3419 3495 let mut grpc = tonic::server::Grpc::new(codec) 3420 3496 .apply_compression_config(
crates/controls/src/api/rockbox_descriptor.bin

This is a binary file and will not be displayed.

+78 -2
crates/rocksky/src/api/rockbox.v1alpha1.rs
··· 1811 1811 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1812 1812 pub struct PlayResponse {} 1813 1813 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1814 - pub struct PauseRequest {} 1815 - #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1816 1814 pub struct PlayOrPauseRequest {} 1817 1815 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1818 1816 pub struct PlayOrPauseResponse {} 1817 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1818 + pub struct PauseRequest {} 1819 1819 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 1820 1820 pub struct PauseResponse {} 1821 1821 #[derive(Clone, Copy, PartialEq, ::prost::Message)] ··· 2008 2008 pub position: ::core::option::Option<i32>, 2009 2009 } 2010 2010 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2011 + pub struct PlayMusicDirectoryRequest { 2012 + #[prost(bool, optional, tag = "1")] 2013 + pub shuffle: ::core::option::Option<bool>, 2014 + #[prost(bool, optional, tag = "2")] 2015 + pub recurse: ::core::option::Option<bool>, 2016 + #[prost(int32, optional, tag = "3")] 2017 + pub position: ::core::option::Option<i32>, 2018 + } 2019 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2011 2020 pub struct PlayDirectoryResponse {} 2021 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2022 + pub struct PlayMusicDirectoryResponse {} 2012 2023 #[derive(Clone, PartialEq, ::prost::Message)] 2013 2024 pub struct PlayTrackRequest { 2014 2025 #[prost(string, tag = "1")] ··· 2434 2445 )); 2435 2446 self.inner.unary(req, path, codec).await 2436 2447 } 2448 + pub async fn play_music_directory( 2449 + &mut self, 2450 + request: impl tonic::IntoRequest<super::PlayMusicDirectoryRequest>, 2451 + ) -> std::result::Result<tonic::Response<super::PlayMusicDirectoryResponse>, tonic::Status> 2452 + { 2453 + self.inner.ready().await.map_err(|e| { 2454 + tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 2455 + })?; 2456 + let codec = tonic::codec::ProstCodec::default(); 2457 + let path = http::uri::PathAndQuery::from_static( 2458 + "/rockbox.v1alpha1.PlaybackService/PlayMusicDirectory", 2459 + ); 2460 + let mut req = request.into_request(); 2461 + req.extensions_mut().insert(GrpcMethod::new( 2462 + "rockbox.v1alpha1.PlaybackService", 2463 + "PlayMusicDirectory", 2464 + )); 2465 + self.inner.unary(req, path, codec).await 2466 + } 2437 2467 pub async fn play_track( 2438 2468 &mut self, 2439 2469 request: impl tonic::IntoRequest<super::PlayTrackRequest>, ··· 2635 2665 &self, 2636 2666 request: tonic::Request<super::PlayDirectoryRequest>, 2637 2667 ) -> std::result::Result<tonic::Response<super::PlayDirectoryResponse>, tonic::Status>; 2668 + async fn play_music_directory( 2669 + &self, 2670 + request: tonic::Request<super::PlayMusicDirectoryRequest>, 2671 + ) -> std::result::Result<tonic::Response<super::PlayMusicDirectoryResponse>, tonic::Status>; 2638 2672 async fn play_track( 2639 2673 &self, 2640 2674 request: tonic::Request<super::PlayTrackRequest>, ··· 3415 3449 let inner = self.inner.clone(); 3416 3450 let fut = async move { 3417 3451 let method = PlayDirectorySvc(inner); 3452 + let codec = tonic::codec::ProstCodec::default(); 3453 + let mut grpc = tonic::server::Grpc::new(codec) 3454 + .apply_compression_config( 3455 + accept_compression_encodings, 3456 + send_compression_encodings, 3457 + ) 3458 + .apply_max_message_size_config( 3459 + max_decoding_message_size, 3460 + max_encoding_message_size, 3461 + ); 3462 + let res = grpc.unary(method, req).await; 3463 + Ok(res) 3464 + }; 3465 + Box::pin(fut) 3466 + } 3467 + "/rockbox.v1alpha1.PlaybackService/PlayMusicDirectory" => { 3468 + #[allow(non_camel_case_types)] 3469 + struct PlayMusicDirectorySvc<T: PlaybackService>(pub Arc<T>); 3470 + impl<T: PlaybackService> 3471 + tonic::server::UnaryService<super::PlayMusicDirectoryRequest> 3472 + for PlayMusicDirectorySvc<T> 3473 + { 3474 + type Response = super::PlayMusicDirectoryResponse; 3475 + type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 3476 + fn call( 3477 + &mut self, 3478 + request: tonic::Request<super::PlayMusicDirectoryRequest>, 3479 + ) -> Self::Future { 3480 + let inner = Arc::clone(&self.0); 3481 + let fut = async move { 3482 + <T as PlaybackService>::play_music_directory(&inner, request).await 3483 + }; 3484 + Box::pin(fut) 3485 + } 3486 + } 3487 + let accept_compression_encodings = self.accept_compression_encodings; 3488 + let send_compression_encodings = self.send_compression_encodings; 3489 + let max_decoding_message_size = self.max_decoding_message_size; 3490 + let max_encoding_message_size = self.max_encoding_message_size; 3491 + let inner = self.inner.clone(); 3492 + let fut = async move { 3493 + let method = PlayMusicDirectorySvc(inner); 3418 3494 let codec = tonic::codec::ProstCodec::default(); 3419 3495 let mut grpc = tonic::server::Grpc::new(codec) 3420 3496 .apply_compression_config(
crates/rocksky/src/api/rockbox_descriptor.bin

This is a binary file and will not be displayed.

+11 -1
crates/rpc/proto/rockbox/v1alpha1/playback.proto
··· 9 9 10 10 message PlayResponse {} 11 11 12 - message PauseRequest {} 13 12 14 13 message PlayOrPauseRequest {} 15 14 16 15 message PlayOrPauseResponse {} 16 + 17 + message PauseRequest {} 17 18 18 19 message PauseResponse {} 19 20 ··· 145 146 optional int32 position = 4; 146 147 } 147 148 149 + message PlayMusicDirectoryRequest { 150 + optional bool shuffle = 1; 151 + optional bool recurse = 2; 152 + optional int32 position = 3; 153 + } 154 + 148 155 message PlayDirectoryResponse {} 156 + 157 + message PlayMusicDirectoryResponse {} 149 158 150 159 message PlayTrackRequest { 151 160 string path = 1; ··· 193 202 rpc PlayArtistTracks(PlayArtistTracksRequest) returns (PlayArtistTracksResponse) {} 194 203 rpc PlayPlaylist(PlayPlaylistRequest) returns (PlayPlaylistResponse) {} 195 204 rpc PlayDirectory(PlayDirectoryRequest) returns (PlayDirectoryResponse) {} 205 + rpc PlayMusicDirectory(PlayMusicDirectoryRequest) returns (PlayMusicDirectoryResponse) {} 196 206 rpc PlayTrack(PlayTrackRequest) returns (PlayTrackResponse) {} 197 207 rpc PlayLikedTracks(PlayLikedTracksRequest) returns (PlayLikedTracksResponse) {} 198 208 rpc PlayAllTracks(PlayAllTracksRequest) returns (PlayAllTracksResponse) {}
+78 -2
crates/rpc/src/api/rockbox.v1alpha1.rs
··· 2338 2338 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2339 2339 pub struct PlayResponse {} 2340 2340 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2341 - pub struct PauseRequest {} 2342 - #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2343 2341 pub struct PlayOrPauseRequest {} 2344 2342 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2345 2343 pub struct PlayOrPauseResponse {} 2344 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2345 + pub struct PauseRequest {} 2346 2346 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2347 2347 pub struct PauseResponse {} 2348 2348 #[derive(Clone, Copy, PartialEq, ::prost::Message)] ··· 2535 2535 pub position: ::core::option::Option<i32>, 2536 2536 } 2537 2537 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2538 + pub struct PlayMusicDirectoryRequest { 2539 + #[prost(bool, optional, tag = "1")] 2540 + pub shuffle: ::core::option::Option<bool>, 2541 + #[prost(bool, optional, tag = "2")] 2542 + pub recurse: ::core::option::Option<bool>, 2543 + #[prost(int32, optional, tag = "3")] 2544 + pub position: ::core::option::Option<i32>, 2545 + } 2546 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2538 2547 pub struct PlayDirectoryResponse {} 2548 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 2549 + pub struct PlayMusicDirectoryResponse {} 2539 2550 #[derive(Clone, PartialEq, ::prost::Message)] 2540 2551 pub struct PlayTrackRequest { 2541 2552 #[prost(string, tag = "1")] ··· 2961 2972 )); 2962 2973 self.inner.unary(req, path, codec).await 2963 2974 } 2975 + pub async fn play_music_directory( 2976 + &mut self, 2977 + request: impl tonic::IntoRequest<super::PlayMusicDirectoryRequest>, 2978 + ) -> std::result::Result<tonic::Response<super::PlayMusicDirectoryResponse>, tonic::Status> 2979 + { 2980 + self.inner.ready().await.map_err(|e| { 2981 + tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 2982 + })?; 2983 + let codec = tonic::codec::ProstCodec::default(); 2984 + let path = http::uri::PathAndQuery::from_static( 2985 + "/rockbox.v1alpha1.PlaybackService/PlayMusicDirectory", 2986 + ); 2987 + let mut req = request.into_request(); 2988 + req.extensions_mut().insert(GrpcMethod::new( 2989 + "rockbox.v1alpha1.PlaybackService", 2990 + "PlayMusicDirectory", 2991 + )); 2992 + self.inner.unary(req, path, codec).await 2993 + } 2964 2994 pub async fn play_track( 2965 2995 &mut self, 2966 2996 request: impl tonic::IntoRequest<super::PlayTrackRequest>, ··· 3162 3192 &self, 3163 3193 request: tonic::Request<super::PlayDirectoryRequest>, 3164 3194 ) -> std::result::Result<tonic::Response<super::PlayDirectoryResponse>, tonic::Status>; 3195 + async fn play_music_directory( 3196 + &self, 3197 + request: tonic::Request<super::PlayMusicDirectoryRequest>, 3198 + ) -> std::result::Result<tonic::Response<super::PlayMusicDirectoryResponse>, tonic::Status>; 3165 3199 async fn play_track( 3166 3200 &self, 3167 3201 request: tonic::Request<super::PlayTrackRequest>, ··· 3942 3976 let inner = self.inner.clone(); 3943 3977 let fut = async move { 3944 3978 let method = PlayDirectorySvc(inner); 3979 + let codec = tonic::codec::ProstCodec::default(); 3980 + let mut grpc = tonic::server::Grpc::new(codec) 3981 + .apply_compression_config( 3982 + accept_compression_encodings, 3983 + send_compression_encodings, 3984 + ) 3985 + .apply_max_message_size_config( 3986 + max_decoding_message_size, 3987 + max_encoding_message_size, 3988 + ); 3989 + let res = grpc.unary(method, req).await; 3990 + Ok(res) 3991 + }; 3992 + Box::pin(fut) 3993 + } 3994 + "/rockbox.v1alpha1.PlaybackService/PlayMusicDirectory" => { 3995 + #[allow(non_camel_case_types)] 3996 + struct PlayMusicDirectorySvc<T: PlaybackService>(pub Arc<T>); 3997 + impl<T: PlaybackService> 3998 + tonic::server::UnaryService<super::PlayMusicDirectoryRequest> 3999 + for PlayMusicDirectorySvc<T> 4000 + { 4001 + type Response = super::PlayMusicDirectoryResponse; 4002 + type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 4003 + fn call( 4004 + &mut self, 4005 + request: tonic::Request<super::PlayMusicDirectoryRequest>, 4006 + ) -> Self::Future { 4007 + let inner = Arc::clone(&self.0); 4008 + let fut = async move { 4009 + <T as PlaybackService>::play_music_directory(&inner, request).await 4010 + }; 4011 + Box::pin(fut) 4012 + } 4013 + } 4014 + let accept_compression_encodings = self.accept_compression_encodings; 4015 + let send_compression_encodings = self.send_compression_encodings; 4016 + let max_decoding_message_size = self.max_decoding_message_size; 4017 + let max_encoding_message_size = self.max_encoding_message_size; 4018 + let inner = self.inner.clone(); 4019 + let fut = async move { 4020 + let method = PlayMusicDirectorySvc(inner); 3945 4021 let codec = tonic::codec::ProstCodec::default(); 3946 4022 let mut grpc = tonic::server::Grpc::new(codec) 3947 4023 .apply_compression_config(
crates/rpc/src/api/rockbox_descriptor.bin

This is a binary file and will not be displayed.

+94 -1
crates/rpc/src/playback.rs
··· 1 1 use std::{ 2 - fs, 2 + env, fs, 3 3 pin::Pin, 4 4 sync::{mpsc::Sender, Arc, Mutex}, 5 5 }; ··· 478 478 .map_err(|e| tonic::Status::internal(e.to_string()))?; 479 479 480 480 Ok(tonic::Response::new(PlayDirectoryResponse::default())) 481 + } 482 + 483 + async fn play_music_directory( 484 + &self, 485 + request: tonic::Request<PlayMusicDirectoryRequest>, 486 + ) -> Result<tonic::Response<PlayMusicDirectoryResponse>, tonic::Status> { 487 + let request = request.into_inner(); 488 + let path = format!("{}/Music", env::var("HOME").unwrap()); 489 + let recurse = request.recurse; 490 + let shuffle = request.shuffle; 491 + let position = request.position; 492 + let mut tracks: Vec<String> = vec![]; 493 + 494 + let recurse = match position { 495 + Some(_) => Some(false), 496 + None => recurse, 497 + }; 498 + 499 + if !std::path::Path::new(&path).is_dir() { 500 + return Err(tonic::Status::invalid_argument("Path is not a directory")); 501 + } 502 + 503 + match recurse { 504 + Some(true) => { 505 + tracks = read_files(path) 506 + .await 507 + .map_err(|e| tonic::Status::internal(e.to_string()))? 508 + } 509 + _ => { 510 + for file in 511 + fs::read_dir(&path).map_err(|e| tonic::Status::internal(e.to_string()))? 512 + { 513 + let file = file.map_err(|e| tonic::Status::internal(e.to_string()))?; 514 + 515 + if file 516 + .metadata() 517 + .map_err(|e| tonic::Status::internal(e.to_string()))? 518 + .is_file() 519 + && !AUDIO_EXTENSIONS.iter().any(|ext| { 520 + file.path() 521 + .to_string_lossy() 522 + .ends_with(&format!(".{}", ext)) 523 + }) 524 + { 525 + continue; 526 + } 527 + 528 + tracks.push(file.path().to_string_lossy().to_string()); 529 + } 530 + } 531 + } 532 + 533 + tracks.sort(); 534 + 535 + let body = serde_json::json!({ 536 + "tracks": tracks 537 + }); 538 + 539 + let response = PlayMusicDirectoryResponse::default(); 540 + check_and_load_player!(response, tracks, shuffle.unwrap_or_default()); 541 + 542 + let url = format!("{}/playlists", rockbox_url()); 543 + let client = reqwest::Client::new(); 544 + client 545 + .post(&url) 546 + .json(&body) 547 + .send() 548 + .await 549 + .map_err(|e| tonic::Status::internal(e.to_string()))?; 550 + 551 + if let Some(true) = shuffle { 552 + let url = format!("{}/playlists/shuffle", rockbox_url()); 553 + let client = reqwest::Client::new(); 554 + client 555 + .put(&url) 556 + .send() 557 + .await 558 + .map_err(|e| tonic::Status::internal(e.to_string()))?; 559 + } 560 + 561 + let url = match position { 562 + Some(position) => format!("{}/playlists/start?start_index={}", rockbox_url(), position), 563 + None => format!("{}/playlists/start", rockbox_url()), 564 + }; 565 + 566 + let client = reqwest::Client::new(); 567 + client 568 + .put(&url) 569 + .send() 570 + .await 571 + .map_err(|e| tonic::Status::internal(e.to_string()))?; 572 + 573 + Ok(tonic::Response::new(PlayMusicDirectoryResponse::default())) 481 574 } 482 575 483 576 async fn play_track(
macos/Rockbox.xcodeproj/project.xcworkspace/xcuserdata/tsirysandratraina.xcuserdatad/UserInterfaceState.xcuserstate

This is a binary file and will not be displayed.

+12 -1
macos/Rockbox/Services/PlaybackService.swift
··· 146 146 } 147 147 } 148 148 149 - func playDirectory(path: String, shuffle: Bool = false, host: String = "127.0.0.1", port: Int = 6061) async throws -> Void { 149 + func playDirectory(path: String, shuffle: Bool = false, position: Int32 = 0, host: String = "127.0.0.1", port: Int = 6061) async throws -> Void { 150 150 try await withGRPCClient( 151 151 transport: .http2NIOPosix( 152 152 target: .dns(host: host, port: port), 153 153 transportSecurity: .plaintext 154 154 ) 155 155 ) { grpcClient in 156 + if path.isEmpty { 157 + let playback = Rockbox_V1alpha1_PlaybackService.Client(wrapping: grpcClient) 158 + var req = Rockbox_V1alpha1_PlayMusicDirectoryRequest() 159 + req.position = position 160 + req.shuffle = shuffle 161 + let _ = try await playback.playMusicDirectory(req) 162 + return 163 + } 156 164 let playback = Rockbox_V1alpha1_PlaybackService.Client(wrapping: grpcClient) 157 165 var req = Rockbox_V1alpha1_PlayDirectoryRequest() 158 166 req.path = path 159 167 req.shuffle = shuffle 168 + req.position = position 160 169 let _ = try await playback.playDirectory(req) 161 170 } 162 171 } 172 + 173 + 163 174 164 175 func playTrack(path: String, host: String = "127.0.0.1", port: Int = 6061) async throws -> Void { 165 176 try await withGRPCClient(
+193
macos/Rockbox/Services/rockbox/v1alpha1/playback.grpc.swift
··· 224 224 method: "PlayDirectory" 225 225 ) 226 226 } 227 + /// Namespace for "PlayMusicDirectory" metadata. 228 + internal enum PlayMusicDirectory: Sendable { 229 + /// Request type for "PlayMusicDirectory". 230 + internal typealias Input = Rockbox_V1alpha1_PlayMusicDirectoryRequest 231 + /// Response type for "PlayMusicDirectory". 232 + internal typealias Output = Rockbox_V1alpha1_PlayMusicDirectoryResponse 233 + /// Descriptor for "PlayMusicDirectory". 234 + internal static let descriptor = GRPCCore.MethodDescriptor( 235 + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "rockbox.v1alpha1.PlaybackService"), 236 + method: "PlayMusicDirectory" 237 + ) 238 + } 227 239 /// Namespace for "PlayTrack" metadata. 228 240 internal enum PlayTrack: Sendable { 229 241 /// Request type for "PlayTrack". ··· 315 327 PlayArtistTracks.descriptor, 316 328 PlayPlaylist.descriptor, 317 329 PlayDirectory.descriptor, 330 + PlayMusicDirectory.descriptor, 318 331 PlayTrack.descriptor, 319 332 PlayLikedTracks.descriptor, 320 333 PlayAllTracks.descriptor, ··· 584 597 context: GRPCCore.ServerContext 585 598 ) async throws -> GRPCCore.StreamingServerResponse<Rockbox_V1alpha1_PlayDirectoryResponse> 586 599 600 + /// Handle the "PlayMusicDirectory" method. 601 + /// 602 + /// - Parameters: 603 + /// - request: A streaming request of `Rockbox_V1alpha1_PlayMusicDirectoryRequest` messages. 604 + /// - context: Context providing information about the RPC. 605 + /// - Throws: Any error which occurred during the processing of the request. Thrown errors 606 + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted 607 + /// to an internal error. 608 + /// - Returns: A streaming response of `Rockbox_V1alpha1_PlayMusicDirectoryResponse` messages. 609 + func playMusicDirectory( 610 + request: GRPCCore.StreamingServerRequest<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 611 + context: GRPCCore.ServerContext 612 + ) async throws -> GRPCCore.StreamingServerResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse> 613 + 587 614 /// Handle the "PlayTrack" method. 588 615 /// 589 616 /// - Parameters: ··· 915 942 context: GRPCCore.ServerContext 916 943 ) async throws -> GRPCCore.ServerResponse<Rockbox_V1alpha1_PlayDirectoryResponse> 917 944 945 + /// Handle the "PlayMusicDirectory" method. 946 + /// 947 + /// - Parameters: 948 + /// - request: A request containing a single `Rockbox_V1alpha1_PlayMusicDirectoryRequest` message. 949 + /// - context: Context providing information about the RPC. 950 + /// - Throws: Any error which occurred during the processing of the request. Thrown errors 951 + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted 952 + /// to an internal error. 953 + /// - Returns: A response containing a single `Rockbox_V1alpha1_PlayMusicDirectoryResponse` message. 954 + func playMusicDirectory( 955 + request: GRPCCore.ServerRequest<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 956 + context: GRPCCore.ServerContext 957 + ) async throws -> GRPCCore.ServerResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse> 958 + 918 959 /// Handle the "PlayTrack" method. 919 960 /// 920 961 /// - Parameters: ··· 1244 1285 context: GRPCCore.ServerContext 1245 1286 ) async throws -> Rockbox_V1alpha1_PlayDirectoryResponse 1246 1287 1288 + /// Handle the "PlayMusicDirectory" method. 1289 + /// 1290 + /// - Parameters: 1291 + /// - request: A `Rockbox_V1alpha1_PlayMusicDirectoryRequest` message. 1292 + /// - context: Context providing information about the RPC. 1293 + /// - Throws: Any error which occurred during the processing of the request. Thrown errors 1294 + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted 1295 + /// to an internal error. 1296 + /// - Returns: A `Rockbox_V1alpha1_PlayMusicDirectoryResponse` to respond with. 1297 + func playMusicDirectory( 1298 + request: Rockbox_V1alpha1_PlayMusicDirectoryRequest, 1299 + context: GRPCCore.ServerContext 1300 + ) async throws -> Rockbox_V1alpha1_PlayMusicDirectoryResponse 1301 + 1247 1302 /// Handle the "PlayTrack" method. 1248 1303 /// 1249 1304 /// - Parameters: ··· 1525 1580 } 1526 1581 ) 1527 1582 router.registerHandler( 1583 + forMethod: Rockbox_V1alpha1_PlaybackService.Method.PlayMusicDirectory.descriptor, 1584 + deserializer: GRPCProtobuf.ProtobufDeserializer<Rockbox_V1alpha1_PlayMusicDirectoryRequest>(), 1585 + serializer: GRPCProtobuf.ProtobufSerializer<Rockbox_V1alpha1_PlayMusicDirectoryResponse>(), 1586 + handler: { request, context in 1587 + try await self.playMusicDirectory( 1588 + request: request, 1589 + context: context 1590 + ) 1591 + } 1592 + ) 1593 + router.registerHandler( 1528 1594 forMethod: Rockbox_V1alpha1_PlaybackService.Method.PlayTrack.descriptor, 1529 1595 deserializer: GRPCProtobuf.ProtobufDeserializer<Rockbox_V1alpha1_PlayTrackRequest>(), 1530 1596 serializer: GRPCProtobuf.ProtobufSerializer<Rockbox_V1alpha1_PlayTrackResponse>(), ··· 1783 1849 return GRPCCore.StreamingServerResponse(single: response) 1784 1850 } 1785 1851 1852 + internal func playMusicDirectory( 1853 + request: GRPCCore.StreamingServerRequest<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 1854 + context: GRPCCore.ServerContext 1855 + ) async throws -> GRPCCore.StreamingServerResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse> { 1856 + let response = try await self.playMusicDirectory( 1857 + request: GRPCCore.ServerRequest(stream: request), 1858 + context: context 1859 + ) 1860 + return GRPCCore.StreamingServerResponse(single: response) 1861 + } 1862 + 1786 1863 internal func playTrack( 1787 1864 request: GRPCCore.StreamingServerRequest<Rockbox_V1alpha1_PlayTrackRequest>, 1788 1865 context: GRPCCore.ServerContext ··· 2067 2144 ) async throws -> GRPCCore.ServerResponse<Rockbox_V1alpha1_PlayDirectoryResponse> { 2068 2145 return GRPCCore.ServerResponse<Rockbox_V1alpha1_PlayDirectoryResponse>( 2069 2146 message: try await self.playDirectory( 2147 + request: request.message, 2148 + context: context 2149 + ), 2150 + metadata: [:] 2151 + ) 2152 + } 2153 + 2154 + internal func playMusicDirectory( 2155 + request: GRPCCore.ServerRequest<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 2156 + context: GRPCCore.ServerContext 2157 + ) async throws -> GRPCCore.ServerResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse> { 2158 + return GRPCCore.ServerResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse>( 2159 + message: try await self.playMusicDirectory( 2070 2160 request: request.message, 2071 2161 context: context 2072 2162 ), ··· 2497 2587 onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Rockbox_V1alpha1_PlayDirectoryResponse>) async throws -> Result 2498 2588 ) async throws -> Result where Result: Sendable 2499 2589 2590 + /// Call the "PlayMusicDirectory" method. 2591 + /// 2592 + /// - Parameters: 2593 + /// - request: A request containing a single `Rockbox_V1alpha1_PlayMusicDirectoryRequest` message. 2594 + /// - serializer: A serializer for `Rockbox_V1alpha1_PlayMusicDirectoryRequest` messages. 2595 + /// - deserializer: A deserializer for `Rockbox_V1alpha1_PlayMusicDirectoryResponse` messages. 2596 + /// - options: Options to apply to this RPC. 2597 + /// - handleResponse: A closure which handles the response, the result of which is 2598 + /// returned to the caller. Returning from the closure will cancel the RPC if it 2599 + /// hasn't already finished. 2600 + /// - Returns: The result of `handleResponse`. 2601 + func playMusicDirectory<Result>( 2602 + request: GRPCCore.ClientRequest<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 2603 + serializer: some GRPCCore.MessageSerializer<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 2604 + deserializer: some GRPCCore.MessageDeserializer<Rockbox_V1alpha1_PlayMusicDirectoryResponse>, 2605 + options: GRPCCore.CallOptions, 2606 + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse>) async throws -> Result 2607 + ) async throws -> Result where Result: Sendable 2608 + 2500 2609 /// Call the "PlayTrack" method. 2501 2610 /// 2502 2611 /// - Parameters: ··· 3138 3247 ) 3139 3248 } 3140 3249 3250 + /// Call the "PlayMusicDirectory" method. 3251 + /// 3252 + /// - Parameters: 3253 + /// - request: A request containing a single `Rockbox_V1alpha1_PlayMusicDirectoryRequest` message. 3254 + /// - serializer: A serializer for `Rockbox_V1alpha1_PlayMusicDirectoryRequest` messages. 3255 + /// - deserializer: A deserializer for `Rockbox_V1alpha1_PlayMusicDirectoryResponse` messages. 3256 + /// - options: Options to apply to this RPC. 3257 + /// - handleResponse: A closure which handles the response, the result of which is 3258 + /// returned to the caller. Returning from the closure will cancel the RPC if it 3259 + /// hasn't already finished. 3260 + /// - Returns: The result of `handleResponse`. 3261 + internal func playMusicDirectory<Result>( 3262 + request: GRPCCore.ClientRequest<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 3263 + serializer: some GRPCCore.MessageSerializer<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 3264 + deserializer: some GRPCCore.MessageDeserializer<Rockbox_V1alpha1_PlayMusicDirectoryResponse>, 3265 + options: GRPCCore.CallOptions = .defaults, 3266 + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse>) async throws -> Result = { response in 3267 + try response.message 3268 + } 3269 + ) async throws -> Result where Result: Sendable { 3270 + try await self.client.unary( 3271 + request: request, 3272 + descriptor: Rockbox_V1alpha1_PlaybackService.Method.PlayMusicDirectory.descriptor, 3273 + serializer: serializer, 3274 + deserializer: deserializer, 3275 + options: options, 3276 + onResponse: handleResponse 3277 + ) 3278 + } 3279 + 3141 3280 /// Call the "PlayTrack" method. 3142 3281 /// 3143 3282 /// - Parameters: ··· 3742 3881 ) 3743 3882 } 3744 3883 3884 + /// Call the "PlayMusicDirectory" method. 3885 + /// 3886 + /// - Parameters: 3887 + /// - request: A request containing a single `Rockbox_V1alpha1_PlayMusicDirectoryRequest` message. 3888 + /// - options: Options to apply to this RPC. 3889 + /// - handleResponse: A closure which handles the response, the result of which is 3890 + /// returned to the caller. Returning from the closure will cancel the RPC if it 3891 + /// hasn't already finished. 3892 + /// - Returns: The result of `handleResponse`. 3893 + internal func playMusicDirectory<Result>( 3894 + request: GRPCCore.ClientRequest<Rockbox_V1alpha1_PlayMusicDirectoryRequest>, 3895 + options: GRPCCore.CallOptions = .defaults, 3896 + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse>) async throws -> Result = { response in 3897 + try response.message 3898 + } 3899 + ) async throws -> Result where Result: Sendable { 3900 + try await self.playMusicDirectory( 3901 + request: request, 3902 + serializer: GRPCProtobuf.ProtobufSerializer<Rockbox_V1alpha1_PlayMusicDirectoryRequest>(), 3903 + deserializer: GRPCProtobuf.ProtobufDeserializer<Rockbox_V1alpha1_PlayMusicDirectoryResponse>(), 3904 + options: options, 3905 + onResponse: handleResponse 3906 + ) 3907 + } 3908 + 3745 3909 /// Call the "PlayTrack" method. 3746 3910 /// 3747 3911 /// - Parameters: ··· 4377 4541 metadata: metadata 4378 4542 ) 4379 4543 return try await self.playDirectory( 4544 + request: request, 4545 + options: options, 4546 + onResponse: handleResponse 4547 + ) 4548 + } 4549 + 4550 + /// Call the "PlayMusicDirectory" method. 4551 + /// 4552 + /// - Parameters: 4553 + /// - message: request message to send. 4554 + /// - metadata: Additional metadata to send, defaults to empty. 4555 + /// - options: Options to apply to this RPC, defaults to `.defaults`. 4556 + /// - handleResponse: A closure which handles the response, the result of which is 4557 + /// returned to the caller. Returning from the closure will cancel the RPC if it 4558 + /// hasn't already finished. 4559 + /// - Returns: The result of `handleResponse`. 4560 + internal func playMusicDirectory<Result>( 4561 + _ message: Rockbox_V1alpha1_PlayMusicDirectoryRequest, 4562 + metadata: GRPCCore.Metadata = [:], 4563 + options: GRPCCore.CallOptions = .defaults, 4564 + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Rockbox_V1alpha1_PlayMusicDirectoryResponse>) async throws -> Result = { response in 4565 + try response.message 4566 + } 4567 + ) async throws -> Result where Result: Sendable { 4568 + let request = GRPCCore.ClientRequest<Rockbox_V1alpha1_PlayMusicDirectoryRequest>( 4569 + message: message, 4570 + metadata: metadata 4571 + ) 4572 + return try await self.playMusicDirectory( 4380 4573 request: request, 4381 4574 options: options, 4382 4575 onResponse: handleResponse
+143 -29
macos/Rockbox/Services/rockbox/v1alpha1/playback.pb.swift
··· 44 44 init() {} 45 45 } 46 46 47 - struct Rockbox_V1alpha1_PauseRequest: Sendable { 48 - // SwiftProtobuf.Message conformance is added in an extension below. See the 49 - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for 50 - // methods supported on all messages. 51 - 52 - var unknownFields = SwiftProtobuf.UnknownStorage() 53 - 54 - init() {} 55 - } 56 - 57 47 struct Rockbox_V1alpha1_PlayOrPauseRequest: Sendable { 58 48 // SwiftProtobuf.Message conformance is added in an extension below. See the 59 49 // `Message` and `Message+*Additions` files in the SwiftProtobuf library for ··· 65 55 } 66 56 67 57 struct Rockbox_V1alpha1_PlayOrPauseResponse: Sendable { 58 + // SwiftProtobuf.Message conformance is added in an extension below. See the 59 + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for 60 + // methods supported on all messages. 61 + 62 + var unknownFields = SwiftProtobuf.UnknownStorage() 63 + 64 + init() {} 65 + } 66 + 67 + struct Rockbox_V1alpha1_PauseRequest: Sendable { 68 68 // SwiftProtobuf.Message conformance is added in an extension below. See the 69 69 // `Message` and `Message+*Additions` files in the SwiftProtobuf library for 70 70 // methods supported on all messages. ··· 710 710 fileprivate var _position: Int32? = nil 711 711 } 712 712 713 + struct Rockbox_V1alpha1_PlayMusicDirectoryRequest: Sendable { 714 + // SwiftProtobuf.Message conformance is added in an extension below. See the 715 + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for 716 + // methods supported on all messages. 717 + 718 + var shuffle: Bool { 719 + get {return _shuffle ?? false} 720 + set {_shuffle = newValue} 721 + } 722 + /// Returns true if `shuffle` has been explicitly set. 723 + var hasShuffle: Bool {return self._shuffle != nil} 724 + /// Clears the value of `shuffle`. Subsequent reads from it will return its default value. 725 + mutating func clearShuffle() {self._shuffle = nil} 726 + 727 + var recurse: Bool { 728 + get {return _recurse ?? false} 729 + set {_recurse = newValue} 730 + } 731 + /// Returns true if `recurse` has been explicitly set. 732 + var hasRecurse: Bool {return self._recurse != nil} 733 + /// Clears the value of `recurse`. Subsequent reads from it will return its default value. 734 + mutating func clearRecurse() {self._recurse = nil} 735 + 736 + var position: Int32 { 737 + get {return _position ?? 0} 738 + set {_position = newValue} 739 + } 740 + /// Returns true if `position` has been explicitly set. 741 + var hasPosition: Bool {return self._position != nil} 742 + /// Clears the value of `position`. Subsequent reads from it will return its default value. 743 + mutating func clearPosition() {self._position = nil} 744 + 745 + var unknownFields = SwiftProtobuf.UnknownStorage() 746 + 747 + init() {} 748 + 749 + fileprivate var _shuffle: Bool? = nil 750 + fileprivate var _recurse: Bool? = nil 751 + fileprivate var _position: Int32? = nil 752 + } 753 + 713 754 struct Rockbox_V1alpha1_PlayDirectoryResponse: Sendable { 755 + // SwiftProtobuf.Message conformance is added in an extension below. See the 756 + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for 757 + // methods supported on all messages. 758 + 759 + var unknownFields = SwiftProtobuf.UnknownStorage() 760 + 761 + init() {} 762 + } 763 + 764 + struct Rockbox_V1alpha1_PlayMusicDirectoryResponse: Sendable { 714 765 // SwiftProtobuf.Message conformance is added in an extension below. See the 715 766 // `Message` and `Message+*Additions` files in the SwiftProtobuf library for 716 767 // methods supported on all messages. ··· 908 959 } 909 960 } 910 961 911 - extension Rockbox_V1alpha1_PauseRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { 912 - static let protoMessageName: String = _protobuf_package + ".PauseRequest" 913 - static let _protobuf_nameMap = SwiftProtobuf._NameMap() 914 - 915 - mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws { 916 - // Load everything into unknown fields 917 - while try decoder.nextFieldNumber() != nil {} 918 - } 919 - 920 - func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws { 921 - try unknownFields.traverse(visitor: &visitor) 922 - } 923 - 924 - static func ==(lhs: Rockbox_V1alpha1_PauseRequest, rhs: Rockbox_V1alpha1_PauseRequest) -> Bool { 925 - if lhs.unknownFields != rhs.unknownFields {return false} 926 - return true 927 - } 928 - } 929 - 930 962 extension Rockbox_V1alpha1_PlayOrPauseRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { 931 963 static let protoMessageName: String = _protobuf_package + ".PlayOrPauseRequest" 932 964 static let _protobuf_nameMap = SwiftProtobuf._NameMap() ··· 960 992 } 961 993 962 994 static func ==(lhs: Rockbox_V1alpha1_PlayOrPauseResponse, rhs: Rockbox_V1alpha1_PlayOrPauseResponse) -> Bool { 995 + if lhs.unknownFields != rhs.unknownFields {return false} 996 + return true 997 + } 998 + } 999 + 1000 + extension Rockbox_V1alpha1_PauseRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { 1001 + static let protoMessageName: String = _protobuf_package + ".PauseRequest" 1002 + static let _protobuf_nameMap = SwiftProtobuf._NameMap() 1003 + 1004 + mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws { 1005 + // Load everything into unknown fields 1006 + while try decoder.nextFieldNumber() != nil {} 1007 + } 1008 + 1009 + func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws { 1010 + try unknownFields.traverse(visitor: &visitor) 1011 + } 1012 + 1013 + static func ==(lhs: Rockbox_V1alpha1_PauseRequest, rhs: Rockbox_V1alpha1_PauseRequest) -> Bool { 963 1014 if lhs.unknownFields != rhs.unknownFields {return false} 964 1015 return true 965 1016 } ··· 2074 2125 } 2075 2126 } 2076 2127 2128 + extension Rockbox_V1alpha1_PlayMusicDirectoryRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { 2129 + static let protoMessageName: String = _protobuf_package + ".PlayMusicDirectoryRequest" 2130 + static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}shuffle\0\u{1}recurse\0\u{1}position\0") 2131 + 2132 + mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws { 2133 + while let fieldNumber = try decoder.nextFieldNumber() { 2134 + // The use of inline closures is to circumvent an issue where the compiler 2135 + // allocates stack space for every case branch when no optimizations are 2136 + // enabled. https://github.com/apple/swift-protobuf/issues/1034 2137 + switch fieldNumber { 2138 + case 1: try { try decoder.decodeSingularBoolField(value: &self._shuffle) }() 2139 + case 2: try { try decoder.decodeSingularBoolField(value: &self._recurse) }() 2140 + case 3: try { try decoder.decodeSingularInt32Field(value: &self._position) }() 2141 + default: break 2142 + } 2143 + } 2144 + } 2145 + 2146 + func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws { 2147 + // The use of inline closures is to circumvent an issue where the compiler 2148 + // allocates stack space for every if/case branch local when no optimizations 2149 + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and 2150 + // https://github.com/apple/swift-protobuf/issues/1182 2151 + try { if let v = self._shuffle { 2152 + try visitor.visitSingularBoolField(value: v, fieldNumber: 1) 2153 + } }() 2154 + try { if let v = self._recurse { 2155 + try visitor.visitSingularBoolField(value: v, fieldNumber: 2) 2156 + } }() 2157 + try { if let v = self._position { 2158 + try visitor.visitSingularInt32Field(value: v, fieldNumber: 3) 2159 + } }() 2160 + try unknownFields.traverse(visitor: &visitor) 2161 + } 2162 + 2163 + static func ==(lhs: Rockbox_V1alpha1_PlayMusicDirectoryRequest, rhs: Rockbox_V1alpha1_PlayMusicDirectoryRequest) -> Bool { 2164 + if lhs._shuffle != rhs._shuffle {return false} 2165 + if lhs._recurse != rhs._recurse {return false} 2166 + if lhs._position != rhs._position {return false} 2167 + if lhs.unknownFields != rhs.unknownFields {return false} 2168 + return true 2169 + } 2170 + } 2171 + 2077 2172 extension Rockbox_V1alpha1_PlayDirectoryResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { 2078 2173 static let protoMessageName: String = _protobuf_package + ".PlayDirectoryResponse" 2079 2174 static let _protobuf_nameMap = SwiftProtobuf._NameMap() ··· 2088 2183 } 2089 2184 2090 2185 static func ==(lhs: Rockbox_V1alpha1_PlayDirectoryResponse, rhs: Rockbox_V1alpha1_PlayDirectoryResponse) -> Bool { 2186 + if lhs.unknownFields != rhs.unknownFields {return false} 2187 + return true 2188 + } 2189 + } 2190 + 2191 + extension Rockbox_V1alpha1_PlayMusicDirectoryResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { 2192 + static let protoMessageName: String = _protobuf_package + ".PlayMusicDirectoryResponse" 2193 + static let _protobuf_nameMap = SwiftProtobuf._NameMap() 2194 + 2195 + mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws { 2196 + // Load everything into unknown fields 2197 + while try decoder.nextFieldNumber() != nil {} 2198 + } 2199 + 2200 + func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws { 2201 + try unknownFields.traverse(visitor: &visitor) 2202 + } 2203 + 2204 + static func ==(lhs: Rockbox_V1alpha1_PlayMusicDirectoryResponse, rhs: Rockbox_V1alpha1_PlayMusicDirectoryResponse) -> Bool { 2091 2205 if lhs.unknownFields != rhs.unknownFields {return false} 2092 2206 return true 2093 2207 }
+3 -1
macos/Rockbox/Views/Files/FileRowView.swift
··· 10 10 struct FileRowView: View { 11 11 let file: FileItem 12 12 let isEven: Bool 13 + let selectedIndex: Int 14 + let currentDirectory: String 13 15 14 16 @State private var isHovering = false 15 17 @State private var errorText: String? = nil ··· 31 33 try await playDirectory(path: file.path) 32 34 return 33 35 } 34 - try await playTrack(path: file.path) 36 + try await playDirectory(path: currentDirectory, position: Int32(selectedIndex)) 35 37 } catch { 36 38 errorText = String(describing: error) 37 39 }
+1 -1
macos/Rockbox/Views/Files/FilesListView.swift
··· 46 46 47 47 // File rows 48 48 ForEach(Array(files.enumerated()), id: \.element.id) { index, file in 49 - FileRowView(file: file, isEven: index % 2 == 0) 49 + FileRowView(file: file, isEven: index % 2 == 0, selectedIndex: index, currentDirectory: currentPath ?? "") 50 50 .contentShape(Rectangle()) 51 51 .onTapGesture { 52 52 if file.type == .directory {