This commit is contained in:
Lucy 2025-09-30 18:52:50 +02:00
parent 814554e208
commit db62ec1d88
3 changed files with 39 additions and 41 deletions

View file

@ -1,7 +1,9 @@
pub fn get_md5sums() -> Result<String, Box<dyn std::error::Error>> { use anyhow::Result;
let client = reqwest::blocking::Client::builder() use reqwest::blocking::Client;
.redirect(reqwest::redirect::Policy::none()) use reqwest::redirect::Policy;
.build()?;
pub fn get_md5sums() -> Result<String> {
let client = Client::builder().redirect(Policy::none()).build()?;
let res = client let res = client
.get("https://www.linuxfromscratch.org/~thomas/multilib-m32/md5sums") .get("https://www.linuxfromscratch.org/~thomas/multilib-m32/md5sums")
.send()? .send()?

View file

@ -15,6 +15,8 @@ use ratatui::{
widgets::{Block, Borders, List, ListItem, ListState}, widgets::{Block, Borders, List, ListItem, ListState},
}; };
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
use spinners::{Spinner, Spinners};
#[cfg(feature = "tui")]
use std::{ use std::{
collections::HashMap, collections::HashMap,
env, env,
@ -93,7 +95,7 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
let mut mirrors_list: Vec<String> = Vec::new(); let mut mirrors_list: Vec<String> = Vec::new();
let mut selected_mirror: Option<String> = None; let mut selected_mirror: Option<String> = None;
let log_messages: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new())); let log_messages: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
let progress_state: Arc<Mutex<HashMap<String, String>>> = let progress_state: Arc<Mutex<HashMap<String, Option<Spinner>>>> =
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(HashMap::new()));
let (tx, rx) = channel::<String>(); let (tx, rx) = channel::<String>();
@ -116,7 +118,6 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
) )
.split(size); .split(size);
// Render menu items
for (i, item) in menu_items.iter().enumerate() { for (i, item) in menu_items.iter().enumerate() {
let style = if Some(i) == state.selected() { let style = if Some(i) == state.selected() {
Style::default().bg(Color::Blue).fg(Color::White) Style::default().bg(Color::Blue).fg(Color::White)
@ -130,7 +131,6 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
); );
} }
// Render logs & progress
let logs = log_messages.lock().unwrap(); let logs = log_messages.lock().unwrap();
let mut combined_logs: Vec<ListItem> = logs let mut combined_logs: Vec<ListItem> = logs
.iter() .iter()
@ -140,8 +140,13 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
.collect(); .collect();
let progress = progress_state.lock().unwrap(); let progress = progress_state.lock().unwrap();
for (file, status) in progress.iter() { for (file, spinner_opt) in progress.iter() {
combined_logs.push(ListItem::new(format!("{}: {}", file, status))); let display_status = if let Some(spinner) = spinner_opt {
spinner.to_string()
} else {
"✅ Done".to_string()
};
combined_logs.push(ListItem::new(format!("{}: {}", file, display_status)));
} }
f.render_widget( f.render_widget(
@ -159,13 +164,11 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
KeyCode::Up => state.select(state.selected().map(|i| i.saturating_sub(1))), KeyCode::Up => state.select(state.selected().map(|i| i.saturating_sub(1))),
KeyCode::Enter => match state.selected() { KeyCode::Enter => match state.selected() {
Some(0) => { Some(0) => {
// Init environment
let (path, msg) = init_environment(); let (path, msg) = init_environment();
lfs_sources = Some(path); lfs_sources = Some(path);
log_messages.lock().unwrap().push(msg); log_messages.lock().unwrap().push(msg);
} }
Some(1) => { Some(1) => {
// Mirror selection
if mirrors_list.is_empty() { if mirrors_list.is_empty() {
mirrors_list = mirrors::fetch_mirrors().unwrap_or_else(|_| { mirrors_list = mirrors::fetch_mirrors().unwrap_or_else(|_| {
vec![ vec![
@ -232,60 +235,52 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
} }
} }
Some(2) => { Some(2) => {
// Download packages
if let Some(ref path) = lfs_sources { if let Some(ref path) = lfs_sources {
let mirror: Option<String> = selected_mirror.clone(); // Correct Option<String> let mirror = selected_mirror
log_messages.lock().unwrap().push(format!( .clone()
"Using mirror: {}", .unwrap_or_else(|| "ftp.fau.de".to_string());
mirror.clone().unwrap_or_else(|| "default mirror".into())
));
let path_clone = path.clone();
let log_clone = Arc::clone(&log_messages);
let tx_clone = tx.clone();
let wget_list = prepare_wget_list(); let wget_list = prepare_wget_list();
let md5_map = prepare_md5_map(); let md5_map = prepare_md5_map();
if wget_list.is_empty() { if wget_list.is_empty() {
log_clone log_messages
.lock() .lock()
.unwrap() .unwrap()
.push("⚠️ No packages to download!".into()); .push("⚠️ No packages to download!".into());
continue; continue;
} }
// Initialize progress
{
let mut prog = progress_state.lock().unwrap();
prog.clear();
for file in &wget_list {
prog.insert(file.clone(), "Pending".into());
}
}
// Spawn download thread
let progress_clone = Arc::clone(&progress_state); let progress_clone = Arc::clone(&progress_state);
let tx_clone = tx.clone();
let path_clone = path.clone();
thread::spawn(move || { thread::spawn(move || {
for file in wget_list { for file in wget_list {
let spinner = Spinner::new(
Spinners::Dots9,
format!("Downloading {}", file),
);
progress_clone progress_clone
.lock() .lock()
.unwrap() .unwrap()
.insert(file.clone(), "Downloading...".into()); .insert(file.clone(), Some(spinner));
let result = downloader::download_files( let result = downloader::download_files(
&file, &file,
&path_clone, &path_clone,
mirror.clone(), Some(mirror.clone()),
Some(&md5_map), Some(&md5_map),
); );
let status = match result {
Ok(_) => "✅ Done",
Err(_) => "❌ Failed",
};
progress_clone progress_clone
.lock() .lock()
.unwrap() .unwrap()
.insert(file.clone(), status.to_string()); .insert(file.clone(), None);
let _ = tx_clone.send(format!("{} {}", status, file));
let status_msg = match result {
Ok(_) => format!("{}", file),
Err(_) => format!("{}", file),
};
let _ = tx_clone.send(status_msg);
} }
let _ = tx_clone.send("🎉 All downloads complete!".into()); let _ = tx_clone.send("🎉 All downloads complete!".into());
}); });

View file

@ -1,7 +1,8 @@
use anyhow::Result;
use reqwest::blocking::Client; use reqwest::blocking::Client;
use reqwest::redirect::Policy; use reqwest::redirect::Policy;
pub fn get_wget_list() -> Result<String, Box<dyn std::error::Error>> { pub fn get_wget_list() -> Result<String> {
let client = Client::builder().redirect(Policy::none()).build()?; let client = Client::builder().redirect(Policy::none()).build()?;
let res = client let res = client
.get("https://www.linuxfromscratch.org/~thomas/multilib-m32/wget-list-sysv") .get("https://www.linuxfromscratch.org/~thomas/multilib-m32/wget-list-sysv")