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.

library: scan audio files only on first start or if ROCKBOX_UPDATE_LIBRARY is defined

+157 -9
+3 -2
Cargo.lock
··· 475 475 476 476 [[package]] 477 477 name = "anyhow" 478 - version = "1.0.89" 478 + version = "1.0.90" 479 479 source = "registry+https://github.com/rust-lang/crates.io-index" 480 - checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" 480 + checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" 481 481 482 482 [[package]] 483 483 name = "arrayvec" ··· 5645 5645 name = "rockbox-cli" 5646 5646 version = "0.1.0" 5647 5647 dependencies = [ 5648 + "anyhow", 5648 5649 "clap", 5649 5650 "owo-colors 4.1.0", 5650 5651 "rockbox-library",
+1
crates/cli/Cargo.toml
··· 7 7 crate-type = ["staticlib"] 8 8 9 9 [dependencies] 10 + anyhow = "1.0.90" 10 11 clap = "4.5.17" 11 12 owo-colors = "4.1.0" 12 13 rockbox-library = {path = "../library"}
+16 -2
crates/cli/src/lib.rs
··· 1 + use anyhow::Error; 1 2 use clap::Command; 2 3 use owo_colors::OwoColorize; 3 4 use rockbox_library::audio_scan::scan_audio_files; 4 - use rockbox_library::create_connection_pool; 5 + use rockbox_library::{create_connection_pool, repo}; 5 6 use std::{env, ffi::CStr}; 6 7 use std::{fs, thread}; 7 8 ··· 60 61 } 61 62 } 62 63 64 + let update_library = match env::var("ROCKBOX_UPDATE_LIBRARY") 65 + .as_ref() 66 + .map(|s| s.as_str()) 67 + { 68 + Ok("1") => true, 69 + Ok("true") => true, 70 + Ok(_) => false, 71 + Err(_) => false, 72 + }; 63 73 let path = env::var("ROCKBOX_LIBRARY").unwrap_or(format!("{}/Music", home)); 64 74 let rt = tokio::runtime::Runtime::new().unwrap(); 65 75 rt.block_on(async { 66 76 let pool = create_connection_pool().await?; 67 - scan_audio_files(pool, path.into()).await 77 + let tracks = repo::track::all(pool.clone()).await?; 78 + if tracks.is_empty() || update_library { 79 + scan_audio_files(pool, path.into()).await?; 80 + } 81 + Ok::<(), Error>(()) 68 82 }) 69 83 .unwrap(); 70 84
+11 -1
crates/graphql/src/schema/library.rs
··· 1 + use std::env; 2 + 1 3 use async_graphql::*; 2 - use rockbox_library::{entity::favourites::Favourites, repo}; 4 + use rockbox_library::{audio_scan::scan_audio_files, entity::favourites::Favourites, repo}; 3 5 use sqlx::{Pool, Sqlite}; 4 6 5 7 use crate::schema::objects::track::Track; ··· 118 120 async fn unlike_album(&self, ctx: &Context<'_>, id: String) -> Result<i32, Error> { 119 121 let pool = ctx.data::<Pool<Sqlite>>()?; 120 122 repo::favourites::delete(pool.clone(), &id).await?; 123 + Ok(0) 124 + } 125 + 126 + async fn scan_library(&self, ctx: &Context<'_>) -> Result<i32, Error> { 127 + let pool = ctx.data::<Pool<Sqlite>>()?; 128 + let home = env::var("HOME")?; 129 + let path = env::var("ROCKBOX_LIBRARY").unwrap_or(format!("{}/Music", home)); 130 + scan_audio_files(pool.clone(), path.into()).await?; 121 131 Ok(0) 122 132 } 123 133 }
+5
crates/rpc/proto/rockbox/v1alpha1/library.proto
··· 127 127 repeated Album albums = 1; 128 128 } 129 129 130 + message ScanLibraryRequest {} 131 + 132 + message ScanLibraryResponse {} 133 + 130 134 service LibraryService { 131 135 rpc GetAlbums(GetAlbumsRequest) returns (GetAlbumsResponse); 132 136 rpc GetArtists(GetArtistsRequest) returns (GetArtistsResponse); ··· 140 144 rpc UnlikeAlbum(UnlikeAlbumRequest) returns (UnlikeAlbumResponse); 141 145 rpc GetLikedTracks(GetLikedTracksRequest) returns (GetLikedTracksResponse); 142 146 rpc GetLikedAlbums(GetLikedAlbumsRequest) returns (GetLikedAlbumsResponse); 147 + rpc ScanLibrary(ScanLibraryRequest) returns (ScanLibraryResponse); 143 148 }
+83
crates/rpc/src/api/rockbox.v1alpha1.rs
··· 501 501 #[prost(message, repeated, tag = "1")] 502 502 pub albums: ::prost::alloc::vec::Vec<Album>, 503 503 } 504 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 505 + pub struct ScanLibraryRequest {} 506 + #[derive(Clone, Copy, PartialEq, ::prost::Message)] 507 + pub struct ScanLibraryResponse {} 504 508 /// Generated client implementations. 505 509 pub mod library_service_client { 506 510 #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] ··· 896 900 ); 897 901 self.inner.unary(req, path, codec).await 898 902 } 903 + pub async fn scan_library( 904 + &mut self, 905 + request: impl tonic::IntoRequest<super::ScanLibraryRequest>, 906 + ) -> std::result::Result< 907 + tonic::Response<super::ScanLibraryResponse>, 908 + tonic::Status, 909 + > { 910 + self.inner 911 + .ready() 912 + .await 913 + .map_err(|e| { 914 + tonic::Status::new( 915 + tonic::Code::Unknown, 916 + format!("Service was not ready: {}", e.into()), 917 + ) 918 + })?; 919 + let codec = tonic::codec::ProstCodec::default(); 920 + let path = http::uri::PathAndQuery::from_static( 921 + "/rockbox.v1alpha1.LibraryService/ScanLibrary", 922 + ); 923 + let mut req = request.into_request(); 924 + req.extensions_mut() 925 + .insert( 926 + GrpcMethod::new("rockbox.v1alpha1.LibraryService", "ScanLibrary"), 927 + ); 928 + self.inner.unary(req, path, codec).await 929 + } 899 930 } 900 931 } 901 932 /// Generated server implementations. ··· 987 1018 request: tonic::Request<super::GetLikedAlbumsRequest>, 988 1019 ) -> std::result::Result< 989 1020 tonic::Response<super::GetLikedAlbumsResponse>, 1021 + tonic::Status, 1022 + >; 1023 + async fn scan_library( 1024 + &self, 1025 + request: tonic::Request<super::ScanLibraryRequest>, 1026 + ) -> std::result::Result< 1027 + tonic::Response<super::ScanLibraryResponse>, 990 1028 tonic::Status, 991 1029 >; 992 1030 } ··· 1593 1631 let inner = self.inner.clone(); 1594 1632 let fut = async move { 1595 1633 let method = GetLikedAlbumsSvc(inner); 1634 + let codec = tonic::codec::ProstCodec::default(); 1635 + let mut grpc = tonic::server::Grpc::new(codec) 1636 + .apply_compression_config( 1637 + accept_compression_encodings, 1638 + send_compression_encodings, 1639 + ) 1640 + .apply_max_message_size_config( 1641 + max_decoding_message_size, 1642 + max_encoding_message_size, 1643 + ); 1644 + let res = grpc.unary(method, req).await; 1645 + Ok(res) 1646 + }; 1647 + Box::pin(fut) 1648 + } 1649 + "/rockbox.v1alpha1.LibraryService/ScanLibrary" => { 1650 + #[allow(non_camel_case_types)] 1651 + struct ScanLibrarySvc<T: LibraryService>(pub Arc<T>); 1652 + impl< 1653 + T: LibraryService, 1654 + > tonic::server::UnaryService<super::ScanLibraryRequest> 1655 + for ScanLibrarySvc<T> { 1656 + type Response = super::ScanLibraryResponse; 1657 + type Future = BoxFuture< 1658 + tonic::Response<Self::Response>, 1659 + tonic::Status, 1660 + >; 1661 + fn call( 1662 + &mut self, 1663 + request: tonic::Request<super::ScanLibraryRequest>, 1664 + ) -> Self::Future { 1665 + let inner = Arc::clone(&self.0); 1666 + let fut = async move { 1667 + <T as LibraryService>::scan_library(&inner, request).await 1668 + }; 1669 + Box::pin(fut) 1670 + } 1671 + } 1672 + let accept_compression_encodings = self.accept_compression_encodings; 1673 + let send_compression_encodings = self.send_compression_encodings; 1674 + let max_decoding_message_size = self.max_decoding_message_size; 1675 + let max_encoding_message_size = self.max_encoding_message_size; 1676 + let inner = self.inner.clone(); 1677 + let fut = async move { 1678 + let method = ScanLibrarySvc(inner); 1596 1679 let codec = tonic::codec::ProstCodec::default(); 1597 1680 let mut grpc = tonic::server::Grpc::new(codec) 1598 1681 .apply_compression_config(
crates/rpc/src/api/rockbox_descriptor.bin

This is a binary file and will not be displayed.

+20 -3
crates/rpc/src/library.rs
··· 1 - use rockbox_library::{entity::favourites::Favourites, repo}; 1 + use std::env; 2 + 3 + use rockbox_library::{audio_scan::scan_audio_files, entity::favourites::Favourites, repo}; 2 4 use sqlx::Sqlite; 3 5 4 6 use crate::api::rockbox::v1alpha1::{ ··· 6 8 GetAlbumsRequest, GetAlbumsResponse, GetArtistRequest, GetArtistResponse, GetArtistsRequest, 7 9 GetArtistsResponse, GetLikedAlbumsRequest, GetLikedAlbumsResponse, GetLikedTracksRequest, 8 10 GetLikedTracksResponse, GetTrackRequest, GetTrackResponse, GetTracksRequest, GetTracksResponse, 9 - LikeAlbumRequest, LikeAlbumResponse, LikeTrackRequest, LikeTrackResponse, UnlikeAlbumRequest, 10 - UnlikeAlbumResponse, UnlikeTrackRequest, UnlikeTrackResponse, 11 + LikeAlbumRequest, LikeAlbumResponse, LikeTrackRequest, LikeTrackResponse, ScanLibraryRequest, 12 + ScanLibraryResponse, UnlikeAlbumRequest, UnlikeAlbumResponse, UnlikeTrackRequest, 13 + UnlikeTrackResponse, 11 14 }; 12 15 13 16 pub struct Library { ··· 198 201 Ok(tonic::Response::new(GetLikedAlbumsResponse { 199 202 albums: albums.into_iter().map(|a| a.into()).collect(), 200 203 })) 204 + } 205 + 206 + async fn scan_library( 207 + &self, 208 + _request: tonic::Request<ScanLibraryRequest>, 209 + ) -> Result<tonic::Response<ScanLibraryResponse>, tonic::Status> { 210 + let home = env::var("HOME").map_err(|e| tonic::Status::internal(e.to_string()))?; 211 + let path = env::var("ROCKBOX_LIBRARY").unwrap_or(format!("{}/Music", home)); 212 + 213 + scan_audio_files(self.pool.clone(), path.into()) 214 + .await 215 + .map_err(|e| tonic::Status::internal(e.to_string()))?; 216 + 217 + Ok(tonic::Response::new(ScanLibraryResponse {})) 201 218 } 202 219 }
+1
crates/server/src/handlers/mod.rs
··· 60 60 async_handler!(tracks, get_track); 61 61 async_handler!(system, get_rockbox_version); 62 62 async_handler!(system, get_status); 63 + async_handler!(system, scan_library); 63 64 async_handler!(settings, get_global_settings); 64 65 async_handler!(docs, get_openapi); 65 66 async_handler!(docs, index);
+16 -1
crates/server/src/handlers/system.rs
··· 1 + use std::env; 2 + 1 3 use crate::http::{Context, Request, Response}; 2 4 use anyhow::Error; 5 + use rockbox_library::audio_scan::scan_audio_files; 3 6 use rockbox_sys as rb; 4 7 5 8 pub async fn get_status(_ctx: &Context, _req: &Request, res: &mut Response) -> Result<(), Error> { ··· 8 11 Ok(()) 9 12 } 10 13 11 - pub async fn get_rockbox_version(_ctx: &Context, _req: &Request, res: &mut Response) -> Result<(), Error> { 14 + pub async fn get_rockbox_version( 15 + _ctx: &Context, 16 + _req: &Request, 17 + res: &mut Response, 18 + ) -> Result<(), Error> { 12 19 let version = rb::system::get_rockbox_version(); 13 20 res.json(&version); 14 21 Ok(()) 15 22 } 23 + 24 + pub async fn scan_library(ctx: &Context, _req: &Request, res: &mut Response) -> Result<(), Error> { 25 + let home = env::var("HOME")?; 26 + let path = env::var("ROCKBOX_LIBRARY").unwrap_or(format!("{}/Music", home)); 27 + scan_audio_files(ctx.pool.clone(), path.into()).await?; 28 + res.text("0"); 29 + Ok(()) 30 + }
+1
crates/server/src/lib.rs
··· 78 78 app.get("/version", get_rockbox_version); 79 79 app.get("/status", get_status); 80 80 app.get("/settings", get_global_settings); 81 + app.put("/scan-library", scan_library); 81 82 82 83 app.get("/", index); 83 84 app.get("/operations/:id", index);