use std::path::{Path, PathBuf};
use termusiclib::{config::v2::server::ScanDepth, utils::get_pin_yin};
use tuirealm_orx_tree::types::{NodeIdx, Tree};
use crate::ui::{
components::orx_music_library::music_library::MusicLibData,
model::{DownloadTracker, TxToMain},
msg::{IsDir, LIMsg, LINodeReady, Msg, RecVec},
};
pub fn library_scan_cb<P: Into<PathBuf>, F>(
download_tracker: DownloadTracker,
path: P,
depth: ScanDepth,
cb: F,
) where
F: FnOnce(RecVec) + Send + 'static,
{
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 vec = library_dir_tree(&path, depth);
cb(vec);
download_tracker.decrease_one(&path.to_string_lossy());
})
.expect("Failed to spawn thread");
}
pub fn library_scan<P: Into<PathBuf>>(
download_tracker: DownloadTracker,
path: P,
depth: ScanDepth,
tx: TxToMain,
focus_node: Option<String>,
) {
library_scan_cb(download_tracker, path, depth, move |vec| {
let _ = tx.send(Msg::Library(LIMsg::TreeNodeReady(LINodeReady {
vec,
focus_node,
})));
});
}
#[inline]
pub fn library_dir_tree(path: &Path, depth: ScanDepth) -> RecVec {
library_dir_tree_inner(path, depth, None)
}
fn library_dir_tree_inner(path: &Path, depth: ScanDepth, is_dir: Option<bool>) -> RecVec {
let is_dir = is_dir.unwrap_or_else(|| path.is_dir());
let mut node = RecVec {
path: path.to_path_buf(),
is_dir: if is_dir {
IsDir::YesNotLoaded
} else {
IsDir::No
},
children: Vec::new(),
};
let depth = match depth {
ScanDepth::Limited(v) => v,
ScanDepth::Unlimited => u32::MAX,
};
if depth > 0
&& path.is_dir()
&& let Ok(paths) = std::fs::read_dir(path)
{
let mut paths: Vec<(String, (PathBuf, bool))> = paths
.filter_map(std::result::Result::ok)
.filter(|p| !p.file_name().to_string_lossy().starts_with('.'))
.map(|v| {
let sort_str = get_pin_yin(&v.file_name().to_string_lossy());
let is_dir = v.file_type().is_ok_and(|v| v.is_dir());
let path = v.path();
(sort_str, (path, is_dir))
})
.collect();
paths.sort_by(|a, b| alphanumeric_sort::compare_str(&a.0, &b.0));
node.is_dir = if paths.is_empty() {
IsDir::YesLoadedEmpty
} else {
IsDir::YesLoaded
};
for (_sort_str, (path, is_dir)) in paths {
node.children.push(library_dir_tree_inner(
&path,
ScanDepth::Limited(depth - 1),
Some(is_dir),
));
}
}
node
}
pub fn recvec_to_tree(vec: RecVec) -> (NodeIdx<MusicLibData>, Tree<MusicLibData>) {
let mut tree = Tree::default();
(recvec_to_node_rec(vec, None, &mut tree), tree)
}
pub fn recvec_to_node_rec(
vec: RecVec,
parent_node: Option<NodeIdx<MusicLibData>>,
tree: &mut Tree<MusicLibData>,
) -> NodeIdx<MusicLibData> {
let nodeidx = if let Some(idx) = parent_node {
tree.get_node_mut(idx)
.unwrap()
.push_child(MusicLibData::new(vec.path, vec.is_dir))
} else {
tree.push_root(MusicLibData::new(vec.path, vec.is_dir))
};
for val in vec.children {
recvec_to_node_rec(val, Some(nodeidx), tree);
}
nodeidx
}