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.

Format GPUI modules for consistent style

+192 -103
+41 -20
gpui/src/client.rs
··· 1 1 use crate::api::v1alpha1::{ 2 - browse_service_client::BrowseServiceClient, 3 - library_service_client::LibraryServiceClient, playback_service_client::PlaybackServiceClient, 4 - playlist_service_client::PlaylistServiceClient, 2 + browse_service_client::BrowseServiceClient, library_service_client::LibraryServiceClient, 3 + playback_service_client::PlaybackServiceClient, playlist_service_client::PlaylistServiceClient, 5 4 settings_service_client::SettingsServiceClient, sound_service_client::SoundServiceClient, 6 - system_service_client::SystemServiceClient, AdjustVolumeRequest, GetArtistsRequest, 7 - GetCurrentRequest, GetGlobalSettingsRequest, GetGlobalStatusRequest, GetLikedTracksRequest, 8 - GetTracksRequest, InsertDirectoryRequest, InsertTracksRequest, LikeTrackRequest, NextRequest, 9 - PauseRequest, PlayAlbumRequest, PlayAllTracksRequest, PlayArtistTracksRequest, 10 - PlayDirectoryRequest, PlayTrackRequest, FastForwardRewindRequest, PlaylistResumeRequest, 11 - PreviousRequest, RemoveTracksRequest, ResumeRequest, ResumeTrackRequest, 12 - SaveSettingsRequest, SearchRequest, ShufflePlaylistRequest, StartRequest, 13 - StatusRequest, StreamCurrentTrackRequest, StreamLibraryRequest, StreamPlaylistRequest, 14 - StreamStatusRequest, TreeGetEntriesRequest, UnlikeTrackRequest, 5 + system_service_client::SystemServiceClient, AdjustVolumeRequest, FastForwardRewindRequest, 6 + GetArtistsRequest, GetCurrentRequest, GetGlobalSettingsRequest, GetGlobalStatusRequest, 7 + GetLikedTracksRequest, GetTracksRequest, InsertDirectoryRequest, InsertTracksRequest, 8 + LikeTrackRequest, NextRequest, PauseRequest, PlayAlbumRequest, PlayAllTracksRequest, 9 + PlayArtistTracksRequest, PlayDirectoryRequest, PlayTrackRequest, PlaylistResumeRequest, 10 + PreviousRequest, RemoveTracksRequest, ResumeRequest, ResumeTrackRequest, SaveSettingsRequest, 11 + SearchRequest, ShufflePlaylistRequest, StartRequest, StatusRequest, StreamCurrentTrackRequest, 12 + StreamLibraryRequest, StreamPlaylistRequest, StreamStatusRequest, TreeGetEntriesRequest, 13 + UnlikeTrackRequest, 15 14 }; 16 15 use crate::state::{SearchAlbum, SearchArtist, SearchResults}; 17 16 ··· 69 68 // Resume from saved state after a daemon restart (playlist_resume + resume_track). 70 69 pub async fn resume_track() -> Result<()> { 71 70 let mut c = PlaylistServiceClient::connect(URL).await?; 72 - c.resume_track(ResumeTrackRequest { start_index: 0, crc: 0, elapsed: 0, offset: 0 }).await?; 71 + c.resume_track(ResumeTrackRequest { 72 + start_index: 0, 73 + crc: 0, 74 + elapsed: 0, 75 + offset: 0, 76 + }) 77 + .await?; 73 78 Ok(()) 74 79 } 75 80 ··· 82 87 /// Seek to `new_time_ms` milliseconds from the start of the current track. 83 88 pub async fn seek(new_time_ms: i32) -> Result<()> { 84 89 let mut c = PlaybackServiceClient::connect(URL).await?; 85 - c.fast_forward_rewind(FastForwardRewindRequest { new_time: new_time_ms }).await?; 90 + c.fast_forward_rewind(FastForwardRewindRequest { 91 + new_time: new_time_ms, 92 + }) 93 + .await?; 86 94 Ok(()) 87 95 } 88 96 ··· 211 219 image: a.image, 212 220 }) 213 221 .collect(); 214 - Ok(SearchResults { tracks, albums, artists }) 222 + Ok(SearchResults { 223 + tracks, 224 + albums, 225 + artists, 226 + }) 215 227 } 216 228 217 229 pub async fn fetch_queue(tx: Sender<StateUpdate>) { ··· 262 274 }) 263 275 .await?; 264 276 if shuffle { 265 - c.shuffle_playlist(ShufflePlaylistRequest { start_index: 0 }).await?; 277 + c.shuffle_playlist(ShufflePlaylistRequest { start_index: 0 }) 278 + .await?; 266 279 } 267 280 c.start(StartRequest { 268 281 start_index: Some(0), ··· 277 290 278 291 pub async fn remove_from_queue(position: i32) -> Result<()> { 279 292 let mut c = PlaylistServiceClient::connect(URL).await?; 280 - c.remove_tracks(RemoveTracksRequest { positions: vec![position] }) 281 - .await?; 293 + c.remove_tracks(RemoveTracksRequest { 294 + positions: vec![position], 295 + }) 296 + .await?; 282 297 Ok(()) 283 298 } 284 299 ··· 320 335 Ok(resp) => { 321 336 let s = resp.into_inner(); 322 337 if s.resume_elapsed > 0 { 323 - let _ = tx.send(StateUpdate::Position(s.resume_elapsed as u64 / 1000)).await; 338 + let _ = tx 339 + .send(StateUpdate::Position(s.resume_elapsed as u64 / 1000)) 340 + .await; 324 341 } 325 342 } 326 343 Err(e) => log::warn!("resume info sync: {e}"), ··· 619 636 .and_then(|n| n.to_str()) 620 637 .unwrap_or(&e.name) 621 638 .to_string(); 622 - FileEntry { name, path: e.name, is_dir } 639 + FileEntry { 640 + name, 641 + path: e.name, 642 + is_dir, 643 + } 623 644 }) 624 645 .collect(); 625 646 entries.sort_by(|a, b| match (a.is_dir, b.is_dir) {
+15 -3
gpui/src/controller.rs
··· 68 68 StateUpdate::SearchResults(results) => { 69 69 s.search_results = results; 70 70 } 71 - StateUpdate::Settings { volume, shuffling, repeat_mode } => { 71 + StateUpdate::Settings { 72 + volume, 73 + shuffling, 74 + repeat_mode, 75 + } => { 72 76 s.volume = volume; 73 77 s.shuffling = shuffling; 74 78 s.repeat = repeat_mode != 0; ··· 136 140 }) 137 141 .detach(); 138 142 139 - Controller { state, rt, tx, search_gen: Arc::new(AtomicU64::new(0)), now_playing } 143 + Controller { 144 + state, 145 + rt, 146 + tx, 147 + search_gen: Arc::new(AtomicU64::new(0)), 148 + now_playing, 149 + } 140 150 } 141 151 142 152 /// Cloneable handle to the tokio runtime — use for fire-and-forget spawns. ··· 156 166 157 167 /// Seek to `position_secs` seconds from the start of the current track. 158 168 pub fn seek(&self, position_secs: u64, duration_secs: u64) { 159 - if duration_secs == 0 { return; } 169 + if duration_secs == 0 { 170 + return; 171 + } 160 172 let ms = (position_secs as i32).saturating_mul(1000); 161 173 self.rt().spawn(crate::client::seek(ms)); 162 174 }
+15 -3
gpui/src/now_playing.rs
··· 112 112 if has_track { 113 113 let meta = track 114 114 .map(|t| MediaMetadata { 115 - title: if t.title.is_empty() { None } else { Some(t.title.as_str()) }, 116 - artist: if t.artist.is_empty() { None } else { Some(t.artist.as_str()) }, 117 - album: if t.album.is_empty() { None } else { Some(t.album.as_str()) }, 115 + title: if t.title.is_empty() { 116 + None 117 + } else { 118 + Some(t.title.as_str()) 119 + }, 120 + artist: if t.artist.is_empty() { 121 + None 122 + } else { 123 + Some(t.artist.as_str()) 124 + }, 125 + album: if t.album.is_empty() { 126 + None 127 + } else { 128 + Some(t.album.as_str()) 129 + }, 118 130 cover_url: cover_url.as_deref(), 119 131 duration: if t.duration > 0 { 120 132 Some(Duration::from_secs(t.duration))
+5 -1
gpui/src/state.rs
··· 139 139 ArtistImages(ArtistImages), 140 140 LikedTracks(Vec<String>), 141 141 SearchResults(Option<SearchResults>), 142 - Settings { volume: i32, shuffling: bool, repeat_mode: i32 }, 142 + Settings { 143 + volume: i32, 144 + shuffling: bool, 145 + repeat_mode: i32, 146 + }, 143 147 } 144 148 145 149 pub fn format_duration(secs: u64) -> String {
+3 -6
gpui/src/ui/animations.rs
··· 1 1 use gpui::{ 2 - div, px, rgb, AnyElement, Animation, AnimationExt as _, IntoElement, ParentElement, SharedString, 3 - Styled, 2 + div, px, rgb, Animation, AnimationExt as _, AnyElement, IntoElement, ParentElement, 3 + SharedString, Styled, 4 4 }; 5 5 use std::f32::consts::PI; 6 6 use std::time::Duration; ··· 14 14 const STATIC_H: [f32; 3] = [5.0, 9.0, 7.0]; 15 15 16 16 let make_bar = move |bar_idx: usize| -> AnyElement { 17 - let b = div() 18 - .w(px(3.0)) 19 - .rounded(px(1.5)) 20 - .bg(rgb(COLOR)); 17 + let b = div().w(px(3.0)).rounded(px(1.5)).bg(rgb(COLOR)); 21 18 if is_playing { 22 19 let anim_id: SharedString = format!("eq-{bar_idx}-{id}").into(); 23 20 b.with_animation(
+55 -31
gpui/src/ui/components/miniplayer.rs
··· 1 1 use crate::client::{adjust_volume, save_repeat, save_shuffle}; 2 2 use crate::controller::Controller; 3 - use crate::state::{format_duration, volume_fraction, PlaybackStatus, VOLUME_MAX_DB, VOLUME_MIN_DB}; 3 + use crate::state::{ 4 + format_duration, volume_fraction, PlaybackStatus, VOLUME_MAX_DB, VOLUME_MIN_DB, 5 + }; 4 6 use crate::ui::components::icons::{Icon, Icons}; 7 + use crate::ui::components::seek_bar::SeekBar; 5 8 use crate::ui::components::{LikedSongs, Page}; 6 9 use crate::ui::global_keybinds::play_pause; 7 - use crate::ui::components::seek_bar::SeekBar; 8 10 use crate::ui::theme::Theme; 9 11 use gpui::prelude::FluentBuilder; 10 12 use gpui::{ ··· 47 49 let vol_pct = (vol_fill * 100.0) as u32; 48 50 let is_shuffling = state.shuffling; 49 51 let is_repeat = state.repeat; 50 - let current_path = state.current_track().map(|t| t.path.clone()).unwrap_or_default(); 52 + let current_path = state 53 + .current_track() 54 + .map(|t| t.path.clone()) 55 + .unwrap_or_default(); 51 56 let track_id = state 52 57 .tracks 53 58 .iter() ··· 142 147 } else { 143 148 theme.player_icons_text 144 149 }) 145 - .hover(|this| this.bg(theme.player_icons_bg_hover)) 150 + .hover(|this| { 151 + this.bg(theme.player_icons_bg_hover) 152 + }) 146 153 .on_click(move |_, _, cx: &mut App| { 147 154 cx.stop_propagation(); 148 155 let rt = cx.global::<Controller>().rt(); 149 - let liked = &mut cx.global_mut::<LikedSongs>().0; 156 + let liked = 157 + &mut cx.global_mut::<LikedSongs>().0; 150 158 if liked.contains(&track_id) { 151 159 liked.remove(&track_id); 152 - rt.spawn(crate::client::unlike_track(track_id.clone())); 160 + rt.spawn(crate::client::unlike_track( 161 + track_id.clone(), 162 + )); 153 163 } else { 154 164 liked.insert(track_id.clone()); 155 - rt.spawn(crate::client::like_track(track_id.clone())); 165 + rt.spawn(crate::client::like_track( 166 + track_id.clone(), 167 + )); 156 168 } 157 169 }) 158 - .child(Icon::new(if is_liked { Icons::Heart } else { Icons::HeartOutline }).size_5()), 170 + .child( 171 + Icon::new(if is_liked { 172 + Icons::Heart 173 + } else { 174 + Icons::HeartOutline 175 + }) 176 + .size_5(), 177 + ), 159 178 ), 160 179 ) 161 180 .child( ··· 334 353 theme.playback_slider_fill, 335 354 px(3.0), 336 355 ) 337 - .on_seek(move |frac, _window, cx: &mut App| { 338 - let seek_secs = (frac * duration as f32) as u64; 339 - cx.global::<Controller>().seek(seek_secs, duration); 340 - }), 356 + .on_seek( 357 + move |frac, _window, cx: &mut App| { 358 + let seek_secs = (frac * duration as f32) as u64; 359 + cx.global::<Controller>().seek(seek_secs, duration); 360 + }, 361 + ), 341 362 ) 342 363 .child( 343 364 div() ··· 368 389 .rounded_full() 369 390 .cursor_pointer() 370 391 .bg(theme.volume_slider_track) 371 - .on_scroll_wheel(|event: &ScrollWheelEvent, _window, cx: &mut App| { 372 - let delta = event.delta.pixel_delta(px(12.0)); 373 - let steps = (-f32::from(delta.y) / 12.0).round() as i32; 374 - if steps != 0 { 375 - let (state, rt) = { 376 - let ctrl = cx.global::<Controller>(); 377 - (ctrl.state.clone(), ctrl.rt()) 378 - }; 379 - let new_vol = { 380 - let current = state.read(cx).volume; 381 - (current + steps).clamp(VOLUME_MIN_DB, VOLUME_MAX_DB) 382 - }; 383 - state.update(cx, |s, cx| { 384 - s.volume = new_vol; 385 - cx.notify(); 386 - }); 387 - rt.spawn(adjust_volume(steps)); 388 - } 389 - }) 392 + .on_scroll_wheel( 393 + |event: &ScrollWheelEvent, _window, cx: &mut App| { 394 + let delta = event.delta.pixel_delta(px(12.0)); 395 + let steps = (-f32::from(delta.y) / 12.0).round() as i32; 396 + if steps != 0 { 397 + let (state, rt) = { 398 + let ctrl = cx.global::<Controller>(); 399 + (ctrl.state.clone(), ctrl.rt()) 400 + }; 401 + let new_vol = { 402 + let current = state.read(cx).volume; 403 + (current + steps) 404 + .clamp(VOLUME_MIN_DB, VOLUME_MAX_DB) 405 + }; 406 + state.update(cx, |s, cx| { 407 + s.volume = new_vol; 408 + cx.notify(); 409 + }); 410 + rt.spawn(adjust_volume(steps)); 411 + } 412 + }, 413 + ) 390 414 .child( 391 415 div() 392 416 .h_full()
+12 -8
gpui/src/ui/components/pages/files.rs
··· 18 18 pub fn new(cx: &mut App) -> Self { 19 19 cx.set_global(FilesBrowseState::default()); 20 20 cx.set_global(FileContextMenuState::default()); 21 - FilesView { entries: Vec::new(), last_requested_path: None } 21 + FilesView { 22 + entries: Vec::new(), 23 + last_requested_path: None, 24 + } 22 25 } 23 26 24 27 fn load_if_needed(&mut self, cx: &mut Context<Self>) { ··· 36 39 // Run the gRPC fetch on the Tokio runtime (requires a Tokio reactor). 37 40 let (tx, rx) = tokio::sync::oneshot::channel::<Vec<FileEntry>>(); 38 41 cx.global::<Controller>().rt().spawn(async move { 39 - let entries = 40 - crate::client::tree_get_entries(current_path).await.unwrap_or_default(); 42 + let entries = crate::client::tree_get_entries(current_path) 43 + .await 44 + .unwrap_or_default(); 41 45 let _ = tx.send(entries); 42 46 }); 43 47 ··· 100 104 .text_color(theme.library_text) 101 105 .opacity(if can_go_back { 1.0 } else { 0.3 }) 102 106 .when(can_go_back, |this| { 103 - this.hover(|t| t.bg(theme.library_track_bg_hover)) 104 - .on_click(|_, _, cx: &mut App| { 107 + this.hover(|t| t.bg(theme.library_track_bg_hover)).on_click( 108 + |_, _, cx: &mut App| { 105 109 let state = cx.global_mut::<FilesBrowseState>(); 106 110 if let Some(prev) = state.path_history.pop() { 107 111 state.current_path = prev; 108 112 } 109 - }) 113 + }, 114 + ) 110 115 }) 111 116 .child(Icon::new(Icons::ChevronLeft).size_4()), 112 117 ) ··· 124 129 range 125 130 .map(|idx| { 126 131 let entry = entries[idx].clone(); 127 - let group_name: gpui::SharedString = 128 - format!("file_row_{}", idx).into(); 132 + let group_name: gpui::SharedString = format!("file_row_{}", idx).into(); 129 133 let gn_icon = group_name.clone(); 130 134 let gn_play = group_name.clone(); 131 135 let gn_opts = group_name.clone();
+19 -6
gpui/src/ui/components/pages/player.rs
··· 52 52 let bg_art_url = album_art_url.clone(); 53 53 let queue_total = state.queue.len(); 54 54 let queue_pos = state.current_idx.map(|i| i + 1); 55 - let current_path = state.current_track().map(|t| t.path.clone()).unwrap_or_default(); 55 + let current_path = state 56 + .current_track() 57 + .map(|t| t.path.clone()) 58 + .unwrap_or_default(); 56 59 let track_id = state 57 60 .tracks 58 61 .iter() ··· 164 167 let liked = &mut cx.global_mut::<LikedSongs>().0; 165 168 if liked.contains(&track_id) { 166 169 liked.remove(&track_id); 167 - rt.spawn(crate::client::unlike_track(track_id.clone())); 170 + rt.spawn(crate::client::unlike_track( 171 + track_id.clone(), 172 + )); 168 173 } else { 169 174 liked.insert(track_id.clone()); 170 - rt.spawn(crate::client::like_track(track_id.clone())); 175 + rt.spawn(crate::client::like_track( 176 + track_id.clone(), 177 + )); 171 178 } 172 179 }) 173 - .child(Icon::new(if is_liked { Icons::Heart } else { Icons::HeartOutline }).size_7()), 180 + .child( 181 + Icon::new(if is_liked { 182 + Icons::Heart 183 + } else { 184 + Icons::HeartOutline 185 + }) 186 + .size_7(), 187 + ), 174 188 ), 175 189 ) 176 190 .child( ··· 316 330 let ctrl = cx.global::<Controller>(); 317 331 (ctrl.state.clone(), ctrl.rt()) 318 332 }; 319 - let new_mode = 320 - if state.read(cx).repeat { 0 } else { 1 }; 333 + let new_mode = if state.read(cx).repeat { 0 } else { 1 }; 321 334 state.update(cx, |s, cx| { 322 335 s.repeat = new_mode != 0; 323 336 cx.notify();
+25 -20
gpui/src/ui/components/seek_bar.rs
··· 1 1 use gpui::{ 2 - App, BorderStyle, Bounds, CursorStyle, DispatchPhase, Element, ElementId, GlobalElementId, 2 + px, App, BorderStyle, Bounds, CursorStyle, DispatchPhase, Element, ElementId, GlobalElementId, 3 3 Hitbox, HitboxBehavior, InspectorElementId, IntoElement, LayoutId, MouseDownEvent, Pixels, 4 - Window, px, 4 + Window, 5 5 }; 6 6 7 7 /// A clickable horizontal seek bar. ··· 35 35 } 36 36 } 37 37 38 - pub fn on_seek( 39 - mut self, 40 - f: impl Fn(f32, &mut Window, &mut App) + 'static, 41 - ) -> Self { 38 + pub fn on_seek(mut self, f: impl Fn(f32, &mut Window, &mut App) + 'static) -> Self { 42 39 self.on_seek = Some(Box::new(f)); 43 40 self 44 41 } ··· 46 43 47 44 impl IntoElement for SeekBar { 48 45 type Element = Self; 49 - fn into_element(self) -> Self { self } 46 + fn into_element(self) -> Self { 47 + self 48 + } 50 49 } 51 50 52 51 pub struct SeekBarPrepaint { ··· 57 56 type RequestLayoutState = LayoutId; 58 57 type PrepaintState = SeekBarPrepaint; 59 58 60 - fn id(&self) -> Option<ElementId> { Some(self.id.clone()) } 59 + fn id(&self) -> Option<ElementId> { 60 + Some(self.id.clone()) 61 + } 61 62 62 - fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { None } 63 + fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { 64 + None 65 + } 63 66 64 67 fn request_layout( 65 68 &mut self, ··· 142 145 let hitbox = prepaint.hitbox.clone(); 143 146 let origin_x = bounds.origin.x; 144 147 let width = bounds.size.width; 145 - window.on_mouse_event( 146 - move |event: &MouseDownEvent, phase, window, cx| { 147 - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { 148 - let rel_x = f32::from(event.position.x - origin_x); 149 - let w = f32::from(width); 150 - let fraction = if w > 0.0 { (rel_x / w).clamp(0.0, 1.0) } else { 0.0 }; 151 - on_seek(fraction, window, cx); 152 - cx.stop_propagation(); 153 - } 154 - }, 155 - ); 148 + window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| { 149 + if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { 150 + let rel_x = f32::from(event.position.x - origin_x); 151 + let w = f32::from(width); 152 + let fraction = if w > 0.0 { 153 + (rel_x / w).clamp(0.0, 1.0) 154 + } else { 155 + 0.0 156 + }; 157 + on_seek(fraction, window, cx); 158 + cx.stop_propagation(); 159 + } 160 + }); 156 161 } 157 162 } 158 163 }
+2 -5
gpui/src/ui/startup_gate.rs
··· 191 191 192 192 let start_hint = matches!(self.error, Some(StartupError::NotInstalled)); 193 193 194 - let main_code_block = 195 - self.code_block("copy-main-cmd", code, CopiedState::Main, theme, cx); 194 + let main_code_block = self.code_block("copy-main-cmd", code, CopiedState::Main, theme, cx); 196 195 let start_code_block = 197 196 self.code_block("copy-start-cmd", "rockboxd", CopiedState::Start, theme, cx); 198 197 ··· 306 305 .bg(gpui::rgb(0x6F00FF)) 307 306 .cursor_pointer() 308 307 .hover(|this| this.bg(gpui::rgb(0x5A00D6))) 309 - .when(checking, |this| { 310 - this.opacity(0.6).cursor_default() 311 - }) 308 + .when(checking, |this| this.opacity(0.6).cursor_default()) 312 309 .on_click(cx.listener(|this, _, _, cx| { 313 310 this.retry(cx); 314 311 }))