modules and packages

This commit is contained in:
Matt Nish-Lapidus 2025-05-28 22:43:54 -04:00
parent e86b68dc49
commit 7a0a8537b9
6 changed files with 714 additions and 21 deletions

30
flake.lock generated
View file

@ -88,11 +88,11 @@
"nixpkgs-stable": "nixpkgs-stable" "nixpkgs-stable": "nixpkgs-stable"
}, },
"locked": { "locked": {
"lastModified": 1748309015, "lastModified": 1748449332,
"narHash": "sha256-NVgo/saT8uehYYwwhzWrTTFlpH0icR2E3tHHKsUouJ4=", "narHash": "sha256-3Qr0pThpHgbIhscOia2ETxo2ge7el458Z9pv5+ONTKc=",
"owner": "nix-community", "owner": "nix-community",
"repo": "emacs-overlay", "repo": "emacs-overlay",
"rev": "0fba546d9aa235fc726fe9c8c3bb703e918c14c4", "rev": "44f10e03d04b678d13ee9c36a9bd632dbce84103",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -376,11 +376,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1748391243, "lastModified": 1748455938,
"narHash": "sha256-7sCuihzsTRZemtbTXaFUoGJUfuQErhKEcL9v7HKIo1k=", "narHash": "sha256-mQ/iNzPra2WtDQ+x2r5IadcWNr0m3uHvLMzJkXKAG/8=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "f5b12be834874f7661db4ced969a621ab2d57971", "rev": "02077149e2921014511dac2729ae6dadb4ec50e2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -785,11 +785,11 @@
"xwayland-satellite-unstable": "xwayland-satellite-unstable" "xwayland-satellite-unstable": "xwayland-satellite-unstable"
}, },
"locked": { "locked": {
"lastModified": 1748441725, "lastModified": 1748450232,
"narHash": "sha256-Coxe8x6tuE7URW73/qjwKeggrxMYTcdlmMx0u/TUfUg=", "narHash": "sha256-6w7klfyLkEaQ9PlE6z3vYPRJWsguL9JsUdU1Bkv6fbs=",
"owner": "sodiboo", "owner": "sodiboo",
"repo": "niri-flake", "repo": "niri-flake",
"rev": "2933c5d663b47e2934cf09fda3b5aebaa9bc2124", "rev": "8319d7cc74becbce927ab6c1e5b0027d22855c9f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -1004,11 +1004,11 @@
}, },
"nixpkgs-stable_2": { "nixpkgs-stable_2": {
"locked": { "locked": {
"lastModified": 1748162331, "lastModified": 1748302896,
"narHash": "sha256-rqc2RKYTxP3tbjA+PB3VMRQNnjesrT0pEofXQTrMsS8=", "narHash": "sha256-ixMT0a8mM091vSswlTORZj93WQAJsRNmEvqLL+qwTFM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7c43f080a7f28b2774f3b3f43234ca11661bf334", "rev": "7848cd8c982f7740edf76ddb3b43d234cb80fc4d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -1659,11 +1659,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1748436066, "lastModified": 1748467104,
"narHash": "sha256-smVwlauRs/j/mlxYfEqncNg1tvxAlkC/taw1+pJHtWw=", "narHash": "sha256-EgYaWbzbtYFLZH1/Eni1ZdLgTwYhe8g9m10UPdBMCmY=",
"owner": "0xc000022070", "owner": "0xc000022070",
"repo": "zen-browser-flake", "repo": "zen-browser-flake",
"rev": "f39ee486e6a14fc30c4e7d00bf389046640c2f63", "rev": "68d2b647f845f6679db96468336aba5dfae901fa",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -37,6 +37,7 @@ in
texlive.combined.scheme-full texlive.combined.scheme-full
openrgb-with-all-plugins openrgb-with-all-plugins
bazecor bazecor
faircamp
]) ])
]; ];

View file

@ -28,10 +28,22 @@
inputs.nh.overlays.default inputs.nh.overlays.default
# inputs.emacs-lsp-booster.overlays.default # inputs.emacs-lsp-booster.overlays.default
# (final: prev: {
# termusic-patched = pkgs.termusic.overrideAttrs (old: {
# src = pkgs.fetchFromGithub {
# owner = "";
# repo = "";
# rev = "";
# hash = "";
# };
# patches = old.patches ++ [ ../../patches/termusic-503.patch ];
# });
# })
(final: prev: { (final: prev: {
wineWowPackages.stagingFull = pkgs-stable.wineWowPackages.stagingFull.overrideAttrs wineWowPackages.stagingFull = pkgs-stable.wineWowPackages.stagingFull.overrideAttrs
(old: { (old: {
patches = old.patches ++ [ ../../patches/wine-6006.patch ]; patches = [ ../../patches/wine-6006.patch ];
waylandSupport = true; waylandSupport = true;
fontconfigSupport = true; fontconfigSupport = true;
vulkanSupport = true; vulkanSupport = true;

View file

@ -4,6 +4,8 @@
home.packages = with pkgs; [ home.packages = with pkgs; [
mpc mpc
nix-config.packages.x86_64-linux.rmpc-latest nix-config.packages.x86_64-linux.rmpc-latest
# termusic-patched
termusic
]; ];
services = { services = {

View file

@ -241,6 +241,14 @@ window#waybar {
border: none; border: none;
} }
#cpu {
border-right: 0;
}
#memory {
border-left: 0;
}
#taskbar button { #taskbar button {
margin: 0 3px; margin: 0 3px;
padding: 3px 6px; padding: 3px 6px;
@ -270,17 +278,14 @@ window#waybar {
#battery.charging { #battery.charging {
color: #ffffff; color: #ffffff;
background-color: #26A65B;
} }
#battery.warning:not(.charging) { #battery.warning:not(.charging) {
background-color: #ffbe61; color: #ffbe61;
color: black;
} }
#battery.critical:not(.charging) { #battery.critical:not(.charging) {
background-color: #f53c3c; color: #f53c3c;
color: #ffffff;
animation-name: blink; animation-name: blink;
animation-duration: 0.5s; animation-duration: 0.5s;
animation-timing-function: linear; animation-timing-function: linear;

673
patches/termusic-503.patch Normal file
View file

@ -0,0 +1,673 @@
From a319c68c62ae93c1604c84d488192ca0e8551cb5 Mon Sep 17 00:00:00 2001
From: hasezoey <hasezoey@gmail.com>
Date: Wed, 28 May 2025 16:21:25 +0200
Subject: [PATCH 1/3] feat(tui::components::music_library): change music
library tree loading to be async and non-blocking
Downside is that the placeholder still acts as a correct path and that multiple loads can be done and overwrite eachother.
fixes #481
---
CHANGELOG.md | 1 +
lib/src/types.rs | 13 ++
tui/src/ui/components/config_editor/view.rs | 2 +-
tui/src/ui/components/music_library.rs | 226 +++++++++++---------
tui/src/ui/components/playlist.rs | 2 +-
tui/src/ui/components/tag_editor/update.rs | 2 +-
tui/src/ui/model/mod.rs | 19 +-
tui/src/ui/model/update.rs | 32 +--
8 files changed, 172 insertions(+), 125 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2cd911a25..0641e5f29 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
- Fix(tui): on track change, dont select "current track" playlist item if the old "current track" was not selected.
- Fix(tui): re-select the (approximately) same playlist item after a shuffle.
- Fix(tui): escape key will now no longer also act as a quit key.
+- Fix(tui): changed the music library tree loading to be async.
- Fix(server): with `rusty-soundtouch` on `rusty` backend, dont take initial samples until necessary.
- Fix(server): on rusty backend, always decode and use `f32` samples. (instead of `i16`)
- Fix(server): on rusty backend, update `soundtouch` version to fix build issues on latest arch & gcc 15.
diff --git a/lib/src/types.rs b/lib/src/types.rs
index d26802f9d..f60080802 100644
--- a/lib/src/types.rs
+++ b/lib/src/types.rs
@@ -1,3 +1,4 @@
+use std::path::PathBuf;
use std::sync::Arc;
use crate::config::v2::tui::{keys::KeyBinding, theme::styles::ColorTermusic};
@@ -387,6 +388,14 @@ pub enum KFMsg {
PodcastRefreshAllFeedsBlurUp,
}
+/// Basically a Tree Node, but without having to include `tui-realm-treeview` as another dependency for lib
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct RecVec<T, V> {
+ pub id: T,
+ pub value: V,
+ pub children: Vec<RecVec<T, V>>,
+}
+
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LIMsg {
TreeStepInto(String),
@@ -397,6 +406,10 @@ pub enum LIMsg {
SwitchRoot,
AddRoot,
RemoveRoot,
+
+ /// A requested node is ready from loading.
+ /// `(Tree, FocusNode)`
+ TreeNodeReady(RecVec<PathBuf, String>, Option<String>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
diff --git a/tui/src/ui/components/config_editor/view.rs b/tui/src/ui/components/config_editor/view.rs
index de259702a..1a96d2177 100644
--- a/tui/src/ui/components/config_editor/view.rs
+++ b/tui/src/ui/components/config_editor/view.rs
@@ -1732,7 +1732,7 @@ impl Model {
}
pub fn umount_config_editor(&mut self) {
- self.library_reload_tree();
+ self.library_scan_dir(&self.library.tree_path, None);
self.playlist_reload();
self.database_reload();
self.progress_reload();
diff --git a/tui/src/ui/components/music_library.rs b/tui/src/ui/components/music_library.rs
index afd6cc6ad..6a4d65e5c 100644
--- a/tui/src/ui/components/music_library.rs
+++ b/tui/src/ui/components/music_library.rs
@@ -1,14 +1,15 @@
-use crate::ui::model::Model;
+use crate::ui::model::{DownloadTracker, Model};
use crate::ui::tui_cmd::TuiCmd;
use crate::utils::get_pin_yin;
use anyhow::{bail, Context, Result};
use std::fs::{remove_dir_all, remove_file, rename, DirEntry};
use std::path::{Path, PathBuf};
+use std::sync::mpsc::Sender;
use termusiclib::config::v2::server::config_extra::ServerConfigVersionedDefaulted;
use termusiclib::config::v2::server::ScanDepth;
use termusiclib::config::SharedTuiSettings;
use termusiclib::ids::Id;
-use termusiclib::types::{GSMsg, LIMsg, Msg, PLMsg, TEMsg, YSMsg};
+use termusiclib::types::{GSMsg, LIMsg, Msg, PLMsg, RecVec, TEMsg, YSMsg};
use tui_realm_treeview::{Node, Tree, TreeView, TREE_CMD_CLOSE, TREE_CMD_OPEN, TREE_INITIAL_NODE};
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
use tuirealm::event::{Key, KeyEvent, KeyModifiers, NoUserEvent};
@@ -234,14 +235,6 @@ impl Component<Msg, NoUserEvent> for MusicLibrary {
}
impl Model {
- pub fn library_scan_dir<P: Into<PathBuf>>(&mut self, p: P) {
- self.library.tree_path = p.into();
- self.library.tree = Tree::new(Self::library_dir_tree(
- &self.library.tree_path,
- self.config_server.read().get_library_scan_depth(),
- ));
- }
-
pub fn library_upper_dir(&self) -> Option<PathBuf> {
self.library
.tree_path
@@ -249,12 +242,58 @@ impl Model {
.map(std::path::Path::to_path_buf)
}
- pub fn library_dir_tree(p: &Path, depth: ScanDepth) -> Node<String> {
- let name: String = match p.file_name() {
+ /// Execute [`Self::library_scan`] from a `&self` instance.
+ #[inline]
+ pub fn library_scan_dir<P: Into<PathBuf>>(&self, path: P, focus_node: Option<String>) {
+ Self::library_scan(
+ self.tx_to_main.clone(),
+ self.download_tracker.clone(),
+ path,
+ self.config_server.read().get_library_scan_depth(),
+ focus_node,
+ );
+ }
+
+ pub fn loading_tree() -> Tree<String> {
+ Tree::new(Node::new("/dev/null".to_string(), "Loading...".to_string()))
+ }
+
+ /// Execute a library scan on a different thread.
+ ///
+ /// Executes [`Self::library_dir_tree`] on a different thread and send a [`LIMsg::TreeNodeReady`] on finish
+ pub fn library_scan<P: Into<PathBuf>>(
+ tx: Sender<Msg>,
+ download_tracker: DownloadTracker,
+ path: P,
+ depth: ScanDepth,
+ focus_node: Option<String>,
+ ) {
+ let path = path.into();
+ std::thread::Builder::new()
+ .name("library tree scan".to_string())
+ .spawn(move || {
+ download_tracker.increase_one(path.to_string_lossy());
+ let root_node = Self::library_dir_tree(&path, depth);
+
+ let _ = tx.send(Msg::Library(LIMsg::TreeNodeReady(root_node, focus_node)));
+ download_tracker.decrease_one(&path.to_string_lossy());
+ })
+ .expect("Failed to spawn thread");
+ }
+
+ /// Scan the given `path` for up to `depth`, and return a [`Node`] tree.
+ ///
+ /// Note: consider using [`Self::library_scan`] instead of this directly.
+ fn library_dir_tree(path: &Path, depth: ScanDepth) -> RecVec<PathBuf, String> {
+ let name: String = match path.file_name() {
None => "/".to_string(),
Some(n) => n.to_string_lossy().into_owned(),
};
- let mut node: Node<String> = Node::new(p.to_string_lossy().into_owned(), name);
+ let mut node = RecVec {
+ id: path.to_path_buf(),
+ value: name,
+ children: Vec::new(),
+ };
let depth = match depth {
ScanDepth::Limited(v) => v,
@@ -262,8 +301,8 @@ impl Model {
ScanDepth::Unlimited => u32::MAX,
};
- if depth > 0 && p.is_dir() {
- if let Ok(paths) = std::fs::read_dir(p) {
+ if depth > 0 && path.is_dir() {
+ if let Ok(paths) = std::fs::read_dir(path) {
let mut paths: Vec<(String, PathBuf)> = paths
.filter_map(std::result::Result::ok)
.filter(|p| !p.file_name().to_string_lossy().starts_with('.'))
@@ -273,12 +312,14 @@ impl Model {
paths.sort_by(|a, b| alphanumeric_sort::compare_str(&a.0, &b.0));
for p in paths {
- node.add_child(Self::library_dir_tree(&p.1, ScanDepth::Limited(depth - 1)));
+ node.children
+ .push(Self::library_dir_tree(&p.1, ScanDepth::Limited(depth - 1)));
}
}
}
node
}
+
pub fn library_dir_children(p: &Path) -> Vec<String> {
let mut children: Vec<String> = vec![];
if p.is_dir() {
@@ -300,88 +341,69 @@ impl Model {
children
}
- pub fn library_reload_with_node_focus(&mut self, node: Option<&str>) {
+ /// Reload the library with the given `node` as a focus, also starts a new database sync worker for the current path.
+ pub fn library_reload_with_node_focus(&mut self, node: Option<String>) {
self.db.sync_database(self.library.tree_path.as_path());
self.database_reload();
- self.library_reload_tree();
- if let Some(n) = node {
- assert!(self
- .app
- .attr(
- &Id::Library,
- Attribute::Custom(TREE_INITIAL_NODE),
- AttrValue::String(n.to_string()),
- )
- .is_ok());
+ self.library_scan_dir(&self.library.tree_path, node);
+ }
+
+ /// Convert a [`RecVec`] to a [`Node`].
+ fn recvec_to_node(vec: RecVec<PathBuf, String>) -> Node<String> {
+ let mut node = Node::new(vec.id.to_string_lossy().to_string(), vec.value);
+
+ for val in vec.children {
+ node.add_child(Self::recvec_to_node(val));
}
+
+ node
}
- pub fn library_reload_tree(&mut self) {
- self.library.tree = Tree::new(Self::library_dir_tree(
- self.library.tree_path.as_ref(),
- self.config_server.read().get_library_scan_depth(),
- ));
- let current_node = match self.app.state(&Id::Library).ok().unwrap() {
+ /// Apply the given [`RecVec`] as a tree
+ pub fn library_apply_as_tree(
+ &mut self,
+ msg: RecVec<PathBuf, String>,
+ focus_node: Option<String>,
+ ) {
+ let root_path = msg.id.clone();
+ let root_node = Self::recvec_to_node(msg);
+
+ let old_current_node = match self.app.state(&Id::Library).ok().unwrap() {
State::One(StateValue::String(id)) => Some(id),
_ => None,
};
- let mut focus = false;
-
- if let Ok(f) = self.app.query(&Id::Library, Attribute::Focus) {
- if Some(AttrValue::Flag(true)) == f {
- focus = true;
- }
- }
- if focus {
- self.app.umount(&Id::Library).ok();
- assert!(self
- .app
- .mount(
- Id::Library,
- Box::new(MusicLibrary::new(
- &self.library.tree,
- current_node,
- self.config_tui.clone(),
- ),),
- Vec::new()
- )
- .is_ok());
- self.app.active(&Id::Library).ok();
- return;
+ self.library.tree_path = root_path;
+ self.library.tree = Tree::new(root_node);
+
+ // remount preserves focus
+ let _ = self.app.remount(
+ Id::Library,
+ Box::new(MusicLibrary::new(
+ &self.library.tree,
+ old_current_node,
+ self.config_tui.clone(),
+ )),
+ Vec::new(),
+ );
+
+ // focus the specified node
+ if let Some(id) = focus_node {
+ let _ = self.app.attr(
+ &Id::Library,
+ Attribute::Custom(TREE_INITIAL_NODE),
+ AttrValue::String(id),
+ );
}
-
- assert!(self
- .app
- .remount(
- Id::Library,
- Box::new(MusicLibrary::new(
- &self.library.tree,
- current_node,
- self.config_tui.clone(),
- ),),
- Vec::new()
- )
- .is_ok());
}
- // Kept for debugging focus issue
- // if let Ok(f) = self.app.query(&Id::Library, Attribute::Focus) {
- // if Some(AttrValue::Flag(true)) == f {
- // error!("focus after remount: true");
- // } else {
- // error!("focus after remount: false");
- // }
- // }
pub fn library_stepinto(&mut self, node_id: &str) {
- self.library_scan_dir(PathBuf::from(node_id));
- self.library_reload_tree();
+ self.library_scan_dir(PathBuf::from(node_id), None);
}
pub fn library_stepout(&mut self) {
if let Some(p) = self.library_upper_dir() {
- self.library_scan_dir(p);
- self.library_reload_tree();
+ self.library_scan_dir(p, None);
}
}
@@ -396,6 +418,7 @@ impl Model {
}
}
+ /// Delete the currently selected node from the filesystem and reload the tree and remove the deleted paths from the playlist.
pub fn library_delete_node(&mut self) -> Result<()> {
if let Ok(State::One(StateValue::String(node_id))) = self.app.state(&Id::Library) {
if let Some(mut route) = self.library.tree.root().route_by_node(&node_id) {
@@ -407,28 +430,27 @@ impl Model {
remove_dir_all(p)?;
}
- // this is to keep the state of playlist
- self.library_reload_tree();
- let tree = self.library.tree.clone();
- if let Some(new_node) = tree.root().node_by_route(&route) {
- self.library_reload_with_node_focus(Some(new_node.id()));
- } else {
- //special case 1: old route not available but have siblings
- if let Some(last) = route.last_mut() {
- if last > &mut 0 {
- *last -= 1;
- }
- }
- if let Some(new_node) = tree.root().node_by_route(&route) {
- self.library_reload_with_node_focus(Some(new_node.id()));
- } else {
- //special case 2: old route not available and no siblings
- route.truncate(route.len() - 1);
- if let Some(new_node) = tree.root().node_by_route(&route) {
- self.library_reload_with_node_focus(Some(new_node.id()));
+ let mut tree = self.library.tree.clone();
+ tree.root_mut().remove_child(&node_id);
+ let mut focus_node: Option<String> = None;
+ // case 1: the route still exists due to having a sibling beyond the index which now takes the same index
+ if let Some(node) = tree.root().node_by_route(&route) {
+ focus_node = Some(node.id().to_string());
+ } else if !route.is_empty() {
+ let _ = route.pop();
+ // case 2: the route does not exist anymore, but there is a parent in the route
+ if let Some(parent) = tree.root().node_by_route(&route) {
+ // case 2.1: the parent has children, select the last of them
+ if let Some(last_child) = parent.children().last() {
+ focus_node = Some(last_child.id().to_string());
+ } else {
+ // case 2.2: the parent exists, but has no children
+ focus_node = Some(parent.id().to_string());
}
}
}
+
+ self.library_scan_dir(&self.library.tree_path, focus_node);
}
// this line remove the deleted songs from playlist
self.playlist_update_library_delete();
@@ -459,7 +481,7 @@ impl Model {
p_parent.join(pold_filename)
};
rename(pold, new_node_id.as_path())?;
- self.library_reload_with_node_focus(new_node_id.to_str());
+ self.library_reload_with_node_focus(Some(new_node_id.to_string_lossy().to_string()));
}
self.library.yanked_node_id = None;
self.playlist_update_library_delete();
@@ -520,7 +542,7 @@ impl Model {
}
if let Some(dir) = vec.get(index) {
let pathbuf = PathBuf::from(dir);
- self.library_scan_dir(pathbuf);
+ self.library_scan_dir(pathbuf, None);
self.library_reload_with_node_focus(None);
}
}
diff --git a/tui/src/ui/components/playlist.rs b/tui/src/ui/components/playlist.rs
index 96254dd8b..a2a333b9e 100644
--- a/tui/src/ui/components/playlist.rs
+++ b/tui/src/ui/components/playlist.rs
@@ -888,7 +888,7 @@ impl Model {
// TODO: move this to server?
self.playback.playlist.save_m3u(filename)?;
- self.library_reload_with_node_focus(Some(&filename.to_string_lossy()));
+ self.library_reload_with_node_focus(Some(filename.to_string_lossy().to_string()));
Ok(())
}
diff --git a/tui/src/ui/components/tag_editor/update.rs b/tui/src/ui/components/tag_editor/update.rs
index ca268cacf..5b2554501 100644
--- a/tui/src/ui/components/tag_editor/update.rs
+++ b/tui/src/ui/components/tag_editor/update.rs
@@ -34,7 +34,7 @@ impl Model {
TEMsg::TagEditorClose => {
if let Some(s) = self.tageditor_song.clone() {
// TODO: this should be re-done and take actual track ids themself, or at least verified to use the same functions to result in the same id
- self.library_reload_with_node_focus(Some(&s.path_as_id_str()));
+ self.library_reload_with_node_focus(Some(s.path_as_id_str().to_string()));
}
self.umount_tageditor();
}
diff --git a/tui/src/ui/model/mod.rs b/tui/src/ui/model/mod.rs
index 8916513aa..c5515e81d 100644
--- a/tui/src/ui/model/mod.rs
+++ b/tui/src/ui/model/mod.rs
@@ -35,7 +35,7 @@ use super::components::TETrack;
use super::tui_cmd::TuiCmd;
use crate::ui::Application;
use crate::CombinedSettings;
-use download_tracker::DownloadTracker;
+pub use download_tracker::DownloadTracker;
mod download_tracker;
mod playlist;
@@ -362,10 +362,7 @@ impl Model {
} = config;
let path = Self::get_full_path_from_config(&config_server.read());
// TODO: refactor music library tree to be Paths instead?
- let tree = Tree::new(Self::library_dir_tree(
- &path,
- config_server.read().get_library_scan_depth(),
- ));
+ let tree = Self::loading_tree();
let viuer_supported = if config_tui.read().cover_features_enabled() {
get_viuer_support()
@@ -412,6 +409,16 @@ impl Model {
let ce_theme = config_tui.read().settings.theme.clone();
let xywh = xywh::Xywh::from(&config_tui.read().settings.coverart);
+ let download_tracker = DownloadTracker::default();
+
+ Self::library_scan(
+ tx_to_main.clone(),
+ download_tracker.clone(),
+ &path,
+ config_server.read().get_library_scan_depth(),
+ None,
+ );
+
Self {
app,
quit: false,
@@ -460,7 +467,7 @@ impl Model {
taskpool,
tx_to_main,
rx_to_main,
- download_tracker: DownloadTracker::default(),
+ download_tracker,
current_track_lyric: None,
playback: Playback::new(),
cmd_to_server_tx,
diff --git a/tui/src/ui/model/update.rs b/tui/src/ui/model/update.rs
index 7e9111ec5..066f56f8f 100644
--- a/tui/src/ui/model/update.rs
+++ b/tui/src/ui/model/update.rs
@@ -57,7 +57,7 @@ impl Update<Msg> for Model {
None
}
Msg::Library(m) => {
- self.update_library(&m);
+ self.update_library(m);
None
}
Msg::GeneralSearch(m) => {
@@ -110,7 +110,7 @@ impl Update<Msg> for Model {
Msg::Podcast(m) => self.update_podcast(m),
Msg::LyricMessage(m) => self.update_lyric_textarea(m),
- Msg::Download(m) => self.update_download_msg(&m),
+ Msg::Download(m) => self.update_download_msg(m),
Msg::Xywh(m) => self.update_xywh_msg(m),
Msg::ForceRedraw => None,
@@ -558,13 +558,13 @@ impl Model {
None
}
- fn update_library(&mut self, msg: &LIMsg) {
+ fn update_library(&mut self, msg: LIMsg) {
match msg {
LIMsg::TreeBlur => {
assert!(self.app.active(&Id::Playlist).is_ok());
}
LIMsg::TreeStepInto(path) => {
- self.library_stepinto(path);
+ self.library_stepinto(&path);
}
LIMsg::TreeStepOut => {
self.library_stepout();
@@ -588,6 +588,9 @@ impl Model {
self.mount_error_popup(e.context("library remove root"));
}
}
+ LIMsg::TreeNodeReady(vec, focus_node) => {
+ self.library_apply_as_tree(vec, focus_node);
+ }
}
}
@@ -916,20 +919,20 @@ impl Model {
}
// change status bar text to indicate the downloading state
- fn update_download_msg(&mut self, msg: &DLMsg) -> Option<Msg> {
+ fn update_download_msg(&mut self, msg: DLMsg) -> Option<Msg> {
self.redraw = true;
match msg {
DLMsg::DownloadRunning(url, title) => {
- self.download_tracker.increase_one(&**url);
+ self.download_tracker.increase_one(&*url);
self.show_message_timeout_label_help(
- self.download_tracker.message_download_start(title),
+ self.download_tracker.message_download_start(&title),
None,
None,
None,
);
}
DLMsg::DownloadSuccess(url) => {
- self.download_tracker.decrease_one(url);
+ self.download_tracker.decrease_one(&url);
self.show_message_timeout_label_help(
self.download_tracker.message_download_complete(),
None,
@@ -945,13 +948,14 @@ impl Model {
if self.download_tracker.visible() {
return None;
}
- self.library_reload_with_node_focus(file.as_deref());
+ self.library_reload_with_node_focus(file);
}
DLMsg::DownloadErrDownload(url, title, error_message) => {
- self.download_tracker.decrease_one(url);
+ self.download_tracker.decrease_one(&url);
self.mount_error_popup(anyhow!("download failed: {error_message}"));
self.show_message_timeout_label_help(
- self.download_tracker.message_download_error_response(title),
+ self.download_tracker
+ .message_download_error_response(&title),
None,
None,
None,
@@ -961,17 +965,17 @@ impl Model {
self.mount_error_popup(anyhow!("download ok but tag info is not complete."));
self.show_message_timeout_label_help(
self.download_tracker
- .message_download_error_embed_data(title),
+ .message_download_error_embed_data(&title),
None,
None,
None,
);
}
DLMsg::MessageShow((title, text)) => {
- self.mount_message(title, text);
+ self.mount_message(&title, &text);
}
DLMsg::MessageHide((title, text)) => {
- self.umount_message(title, text);
+ self.umount_message(&title, &text);
}
DLMsg::FetchPhotoSuccess(image_wrapper) => {
self.show_image(&image_wrapper.data).ok();
From 8b1fd1216fb81bfa982958086832c10b52d6e494 Mon Sep 17 00:00:00 2001
From: hasezoey <hasezoey@gmail.com>
Date: Wed, 28 May 2025 16:25:41 +0200
Subject: [PATCH 2/3] fix(tui::components::music_library::library_stepout):
focus the last root node in the new tree
Instead of always focusing the root node of the new tree.
---
CHANGELOG.md | 1 +
tui/src/ui/components/music_library.rs | 5 ++++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0641e5f29..3e05516e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@
- Fix(tui): re-select the (approximately) same playlist item after a shuffle.
- Fix(tui): escape key will now no longer also act as a quit key.
- Fix(tui): changed the music library tree loading to be async.
+- Fix(tui): in the music library tree, when stepping out now the correct node if focused instead of the root node.
- Fix(server): with `rusty-soundtouch` on `rusty` backend, dont take initial samples until necessary.
- Fix(server): on rusty backend, always decode and use `f32` samples. (instead of `i16`)
- Fix(server): on rusty backend, update `soundtouch` version to fix build issues on latest arch & gcc 15.
diff --git a/tui/src/ui/components/music_library.rs b/tui/src/ui/components/music_library.rs
index 6a4d65e5c..498a29ffb 100644
--- a/tui/src/ui/components/music_library.rs
+++ b/tui/src/ui/components/music_library.rs
@@ -397,13 +397,16 @@ impl Model {
}
}
+ /// Handle stepping into a node on the tree
pub fn library_stepinto(&mut self, node_id: &str) {
self.library_scan_dir(PathBuf::from(node_id), None);
}
+ /// Handle stepping out of the current root node on the tree
pub fn library_stepout(&mut self) {
if let Some(p) = self.library_upper_dir() {
- self.library_scan_dir(p, None);
+ let focus_node = Some(self.library.tree_path.to_string_lossy().to_string());
+ self.library_scan_dir(p, focus_node);
}
}
From 47a13fc83e3c353a516d83e892ea87c7e26ed430 Mon Sep 17 00:00:00 2001
From: hasezoey <hasezoey@gmail.com>
Date: Wed, 28 May 2025 16:32:42 +0200
Subject: [PATCH 3/3] style(tui::components::music_library): add a todo to try
to load the directory if not loaded yet
---
tui/src/ui/components/music_library.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/tui/src/ui/components/music_library.rs b/tui/src/ui/components/music_library.rs
index 498a29ffb..d98cd6c09 100644
--- a/tui/src/ui/components/music_library.rs
+++ b/tui/src/ui/components/music_library.rs
@@ -90,6 +90,7 @@ impl MusicLibrary {
let current_node = self.component.tree_state().selected().unwrap();
let p: &Path = Path::new(current_node);
if p.is_dir() {
+ // TODO: try to load the directory if it is not loaded yet.
(self.perform(Cmd::Custom(TREE_CMD_OPEN)), None)
} else {
(