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.

expose get_entries function to gRPC, GraphQL and TypeScript API

+155 -15
+1
crates/ext/example.ts
··· 6 6 console.log(await rb.playlist.resumeTrack()); 7 7 console.log(await rb.playlist.getCurrent()); 8 8 console.log(await rb.playlist.amount()); 9 + console.log(await rb.browse.tree.getEntries("/"));
+5
crates/ext/src/browse/browse.js
··· 4 4 rockboxBrowse: () => { 5 5 return ops.op_rockbox_browse(); 6 6 }, 7 + tree: { 8 + getEntries: (path) => { 9 + return ops.op_tree_get_entries(path); 10 + }, 11 + }, 7 12 }; 8 13 9 14 globalThis.rb = { browse };
+15 -2
crates/ext/src/browse/mod.rs
··· 1 1 use std::path::PathBuf; 2 2 3 - use deno_core::{extension, op2}; 3 + use deno_core::{error::AnyError, extension, op2}; 4 + use rockbox_sys::types::tree::Entry; 5 + 6 + use crate::rockbox_url; 4 7 5 8 extension!( 6 9 rb_browse, 7 - ops = [op_rockbox_browse], 10 + ops = [op_rockbox_browse, op_tree_get_entries], 8 11 esm = ["src/browse/browse.js"], 9 12 ); 10 13 ··· 16 19 pub async fn op_rockbox_browse() { 17 20 println!("op_rockbox_browse ..."); 18 21 } 22 + 23 + #[op2(async)] 24 + #[serde] 25 + pub async fn op_tree_get_entries(#[string] path: String) -> Result<Vec<Entry>, AnyError> { 26 + let client = reqwest::Client::new(); 27 + let url = format!("{}/tree_entries?q={}", rockbox_url(), path); 28 + let response = client.get(&url).send().await?; 29 + let entries = response.json::<Vec<Entry>>().await?; 30 + Ok(entries) 31 + }
+12 -2
crates/graphql/src/schema/browse.rs
··· 1 1 use async_graphql::*; 2 2 3 + use crate::{rockbox_url, schema::objects::entry::Entry}; 4 + 3 5 #[derive(Default)] 4 6 pub struct BrowseQuery; 5 7 ··· 13 15 "tree get context".to_string() 14 16 } 15 17 16 - async fn tree_get_entry_at(&self) -> String { 17 - "tree get entry at".to_string() 18 + async fn tree_get_entries(&self, ctx: &Context<'_>, path: String) -> Result<Vec<Entry>, Error> { 19 + let client = ctx.data::<reqwest::Client>().unwrap(); 20 + let url = format!("{}/tree_entries?q={}", rockbox_url(), path); 21 + let response = client.get(&url).send().await?; 22 + let response = response.json::<Vec<Entry>>().await?; 23 + Ok(response) 24 + } 25 + 26 + async fn tree_get_entry_at(&self, ctx: &Context<'_>) -> Result<Entry, Error> { 27 + todo!() 18 28 } 19 29 20 30 async fn rockbox_browse(&self) -> String {
+40
crates/graphql/src/schema/objects/entry.rs
··· 1 + use async_graphql::*; 2 + use serde::{Deserialize, Serialize}; 3 + 4 + #[derive(Default, Clone, Serialize, Deserialize)] 5 + pub struct Entry { 6 + pub name: String, 7 + pub attr: i32, 8 + pub time_write: u32, 9 + pub customaction: i32, 10 + } 11 + 12 + #[Object] 13 + impl Entry { 14 + async fn name(&self) -> &str { 15 + &self.name 16 + } 17 + 18 + async fn attr(&self) -> i32 { 19 + self.attr 20 + } 21 + 22 + async fn time_write(&self) -> u32 { 23 + self.time_write 24 + } 25 + 26 + async fn customaction(&self) -> i32 { 27 + self.customaction 28 + } 29 + } 30 + 31 + impl From<rockbox_sys::types::tree::Entry> for Entry { 32 + fn from(entry: rockbox_sys::types::tree::Entry) -> Self { 33 + Self { 34 + name: entry.name, 35 + attr: entry.attr, 36 + time_write: entry.time_write, 37 + customaction: entry.customaction, 38 + } 39 + } 40 + }
+1
crates/graphql/src/schema/objects/mod.rs
··· 1 1 pub mod compressor_settings; 2 + pub mod entry; 2 3 pub mod eq_band_setting; 3 4 pub mod playlist; 4 5 pub mod replaygain_settings;
+9
crates/rpc/proto/rockbox/v1alpha1/browse.proto
··· 15 15 } 16 16 17 17 message TreeGetEntriesRequest { 18 + string path = 1; 19 + } 20 + 21 + message Entry { 22 + string name = 1; 23 + int32 attr = 2; 24 + uint32 time_write = 3; 25 + int32 customaction = 4; 18 26 } 19 27 20 28 message TreeGetEntriesResponse { 29 + repeated Entry entries = 1; 21 30 } 22 31 23 32
+21 -4
crates/rpc/src/api/rockbox.v1alpha1.rs
··· 7 7 pub struct TreeGetContextRequest {} 8 8 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 9 9 pub struct TreeGetContextResponse {} 10 - #[derive(Clone, Copy, PartialEq, ::prost::Message)] 11 - pub struct TreeGetEntriesRequest {} 12 - #[derive(Clone, Copy, PartialEq, ::prost::Message)] 13 - pub struct TreeGetEntriesResponse {} 10 + #[derive(Clone, PartialEq, ::prost::Message)] 11 + pub struct TreeGetEntriesRequest { 12 + #[prost(string, tag = "1")] 13 + pub path: ::prost::alloc::string::String, 14 + } 15 + #[derive(Clone, PartialEq, ::prost::Message)] 16 + pub struct Entry { 17 + #[prost(string, tag = "1")] 18 + pub name: ::prost::alloc::string::String, 19 + #[prost(int32, tag = "2")] 20 + pub attr: i32, 21 + #[prost(uint32, tag = "3")] 22 + pub time_write: u32, 23 + #[prost(int32, tag = "4")] 24 + pub customaction: i32, 25 + } 26 + #[derive(Clone, PartialEq, ::prost::Message)] 27 + pub struct TreeGetEntriesResponse { 28 + #[prost(message, repeated, tag = "1")] 29 + pub entries: ::prost::alloc::vec::Vec<Entry>, 30 + } 14 31 #[derive(Clone, Copy, PartialEq, ::prost::Message)] 15 32 pub struct TreeGetEntryAtRequest {} 16 33 #[derive(Clone, Copy, PartialEq, ::prost::Message)]
crates/rpc/src/api/rockbox_descriptor.bin

This is a binary file and will not be displayed.

+31 -3
crates/rpc/src/browse.rs
··· 1 - use crate::api::rockbox::v1alpha1::{browse_service_server::BrowseService, *}; 1 + use crate::{ 2 + api::rockbox::v1alpha1::{browse_service_server::BrowseService, *}, 3 + rockbox_url, 4 + }; 5 + use rockbox_sys as rb; 2 6 3 7 #[derive(Default)] 4 - pub struct Browse; 8 + pub struct Browse { 9 + client: reqwest::Client, 10 + } 11 + 12 + impl Browse { 13 + pub fn new(client: reqwest::Client) -> Self { 14 + Self { client } 15 + } 16 + } 5 17 6 18 #[tonic::async_trait] 7 19 impl BrowseService for Browse { ··· 23 35 &self, 24 36 request: tonic::Request<TreeGetEntriesRequest>, 25 37 ) -> Result<tonic::Response<TreeGetEntriesResponse>, tonic::Status> { 26 - Ok(tonic::Response::new(TreeGetEntriesResponse::default())) 38 + let path = request.into_inner().path; 39 + let url = format!("{}/tree_entries?q={}", rockbox_url(), path); 40 + let response = self 41 + .client 42 + .get(url) 43 + .send() 44 + .await 45 + .map_err(|e| tonic::Status::internal(e.to_string()))?; 46 + let data = response 47 + .json::<Vec<rb::types::tree::Entry>>() 48 + .await 49 + .map_err(|e| tonic::Status::internal(e.to_string()))?; 50 + let entries = data 51 + .into_iter() 52 + .map(|entry| Entry::from(entry)) 53 + .collect::<Vec<Entry>>(); 54 + Ok(tonic::Response::new(TreeGetEntriesResponse { entries })) 27 55 } 28 56 29 57 async fn tree_get_entry_at(
+17 -1
crates/rpc/src/lib.rs
··· 17 17 user_settings::{CompressorSettings, EqBandSetting, ReplaygainSettings, UserSettings}, 18 18 }; 19 19 use v1alpha1::{ 20 - CurrentTrackResponse, GetGlobalSettingsResponse, GetGlobalStatusResponse, 20 + CurrentTrackResponse, Entry, GetGlobalSettingsResponse, GetGlobalStatusResponse, 21 21 NextTrackResponse, 22 22 }; 23 23 ··· 634 634 last_screen, 635 635 viewer_icon_count, 636 636 last_volume_change, 637 + } 638 + } 639 + } 640 + 641 + impl From<rockbox_sys::types::tree::Entry> for Entry { 642 + fn from(entry: rockbox_sys::types::tree::Entry) -> Self { 643 + let name = entry.name; 644 + let attr = entry.attr; 645 + let time_write = entry.time_write; 646 + let customaction = entry.customaction; 647 + 648 + Entry { 649 + name, 650 + attr, 651 + time_write, 652 + customaction, 637 653 } 638 654 } 639 655 }
+3 -3
crates/rpc/src/server.rs
··· 51 51 .add_service(tonic_web::enable(PlaybackServiceServer::new( 52 52 Playback::new(cmd_tx.clone(), client.clone()), 53 53 ))) 54 - .add_service(tonic_web::enable(BrowseServiceServer::new( 55 - Browse::default(), 56 - ))) 54 + .add_service(tonic_web::enable(BrowseServiceServer::new(Browse::new( 55 + client.clone(), 56 + )))) 57 57 .add_service(tonic_web::enable(SoundServiceServer::new(Sound::default()))) 58 58 .add_service(tonic_web::enable(SettingsServiceServer::new( 59 59 Settings::new(client.clone()),