This commit is contained in:
2026-03-19 02:49:07 -04:00
parent b748973384
commit bb07d1b372
2 changed files with 276 additions and 54 deletions

View File

@@ -1,6 +1,13 @@
use crate::types; use crate::types;
use crate::util::clamp; use crate::util::clamp;
use eframe::egui; use eframe::egui;
use egui::ColorImage;
use futures_util::stream::StreamExt;
use human_bytes::human_bytes;
use image::{ColorType, GenericImage, Rgba};
use std::cmp::min;
use std::io::Write;
use std::path::Path;
use tokio::sync::mpsc; use tokio::sync::mpsc;
pub struct Application { pub struct Application {
@@ -12,6 +19,7 @@ pub struct Application {
show_side_panel: bool, show_side_panel: bool,
search_ctx: types::SearchContext, search_ctx: types::SearchContext,
download_ctx: types::DownloadContext,
} }
impl Application { impl Application {
@@ -38,7 +46,8 @@ impl Application {
// Search button and input // Search button and input
ui.label("Search: "); ui.label("Search: ");
if ui.text_edit_singleline(&mut self.query).lost_focus() if ui.text_edit_singleline(&mut self.query).lost_focus()
&& ui.input(|i| i.key_pressed(egui::Key::Enter)) && ui
.input(|i| i.key_pressed(egui::Key::Enter) && !self.search_ctx.is_searching)
{ {
let query = self.query.clone(); let query = self.query.clone();
let url = self.server_url.clone(); let url = self.server_url.clone();
@@ -66,9 +75,11 @@ impl Application {
"Page {} of {}", "Page {} of {}",
self.search_ctx.page, self.search_ctx.total_pages self.search_ctx.page, self.search_ctx.total_pages
)); ));
if ui.button(" - ").clicked() { if ui.button(" - ").clicked() && !self.search_ctx.is_searching {
let skip = self.search_ctx.page == 1;
self.search_ctx.page = self.search_ctx.page =
clamp(self.search_ctx.page - 1, 1, self.search_ctx.total_pages); clamp(self.search_ctx.page - 1, 1, self.search_ctx.total_pages);
if !skip {
let query = self.query.clone(); let query = self.query.clone();
let url = self.server_url.clone(); let url = self.server_url.clone();
let page = self.search_ctx.page.clone(); let page = self.search_ctx.page.clone();
@@ -89,10 +100,13 @@ impl Application {
}); });
ctx.forget_all_images(); ctx.forget_all_images();
} }
}
if ui.button("+").clicked() { if ui.button("+").clicked() && !self.search_ctx.is_searching {
let skip = self.search_ctx.page == self.search_ctx.total_pages;
self.search_ctx.page = self.search_ctx.page =
clamp(self.search_ctx.page + 1, 1, self.search_ctx.total_pages); clamp(self.search_ctx.page + 1, 1, self.search_ctx.total_pages);
if !skip {
let query = self.query.clone(); let query = self.query.clone();
let url = self.server_url.clone(); let url = self.server_url.clone();
let page = self.search_ctx.page.clone(); let page = self.search_ctx.page.clone();
@@ -111,6 +125,8 @@ impl Application {
let res = search_files(url, query, page, page_size).await; let res = search_files(url, query, page, page_size).await;
let _ = tx.send(res).await; let _ = tx.send(res).await;
}); });
ctx.forget_all_images();
}
} }
ui.label(&self.status); ui.label(&self.status);
@@ -153,6 +169,113 @@ impl Application {
}); });
} }
fn render_center_panel(&mut self, ctx: &egui::Context) {
egui::CentralPanel::default().show(ctx, |ui| {
if !self.search_ctx.search_results.is_empty() {
let cols = 5;
let mut rows = self.search_ctx.search_results.len() / cols;
if self.search_ctx.search_results.len() < cols {
rows = 1;
}
egui::ScrollArea::vertical().show(ui, |ui| {
egui_extras::StripBuilder::new(ui)
.sizes(egui_extras::Size::relative((rows as f32).recip()), rows)
.vertical(|mut strip| {
for r in 0..rows {
strip.cell(|ui| {
egui_extras::StripBuilder::new(ui)
.sizes(
egui_extras::Size::relative((cols as f32).recip()),
cols,
)
.horizontal(|mut strip| {
for c in 0..cols {
strip.cell(|ui| {
let file = &self.search_ctx.search_results
[r * cols + c];
if file.preview != "None" {
ui.add(
egui::Image::from_uri(
file.preview.clone(),
)
.shrink_to_fit(),
);
} else {
let mut img_data = image::DynamicImage::new(
1,
1,
ColorType::Rgb8,
);
img_data.put_pixel(
0,
0,
Rgba([0, 0, 0, 255]),
);
let img = ColorImage::from_rgb(
[1, 1],
&img_data.as_bytes(),
);
let handle = ctx.load_texture(
format!("preview_{}", file.name),
img,
egui::TextureOptions::default(),
);
let texture = egui::load::SizedTexture::new(
handle.id(),
egui::vec2(1.0, 1.0),
);
ui.add(
egui::Image::new(
egui::ImageSource::Texture(texture),
)
.shrink_to_fit(),
);
}
ui.label(format!("Name: {}", file.name));
ui.label(format!("Extension: {}", file.ext));
ui.label(format!(
"Size: {}",
human_bytes(file.size as f64)
));
ui.horizontal(|ui| {
let download_btn = ui.add_enabled(
!self.download_ctx.is_downloading,
egui::Button::new("Download"),
);
if download_btn.clicked() {
let path = self
.download_path
.clone()
.unwrap_or("".to_string());
let url = file.url.clone();
let (tx, rx) = mpsc::channel::<
Result<String, String>,
>(
1
);
self.download_ctx.download_rx =
Some(rx);
self.download_ctx.is_downloading = true;
self.status =
"Downloading...".to_string();
tokio::spawn(async move {
let res = download(url, path).await;
let _ = tx.send(res).await;
});
}
});
})
}
});
});
}
});
});
}
});
}
fn process_channels(&mut self, ctx: &egui::Context) { fn process_channels(&mut self, ctx: &egui::Context) {
if self.search_ctx.search_rx.is_some() { if self.search_ctx.search_rx.is_some() {
let mut clear_rx = false; let mut clear_rx = false;
@@ -188,6 +311,36 @@ impl Application {
self.search_ctx.search_rx = None; self.search_ctx.search_rx = None;
} }
} }
if self.download_ctx.download_rx.is_some() {
let mut clear_rx = false;
if let Some(rx) = self.download_ctx.download_rx.as_mut() {
match rx.try_recv() {
Ok(Ok(final_path)) => {
self.status = format!("Downloaded {}", final_path);
self.download_ctx.is_downloading = false;
clear_rx = true;
}
Ok(Err(e)) => {
self.status = format!("Download failed: {}", e);
self.download_ctx.is_downloading = false;
clear_rx = true;
}
Err(mpsc::error::TryRecvError::Empty) => {
ctx.request_repaint();
}
Err(mpsc::error::TryRecvError::Disconnected) => {
self.status = "Download task ended unexpectedly".to_string();
self.download_ctx.is_downloading = false;
clear_rx = true;
}
}
}
if clear_rx {
self.download_ctx.download_rx = None;
}
}
} }
} }
@@ -200,6 +353,7 @@ impl Default for Application {
status: "Ready!".to_string(), status: "Ready!".to_string(),
search_ctx: types::SearchContext::default(), search_ctx: types::SearchContext::default(),
show_side_panel: false, show_side_panel: false,
download_ctx: types::DownloadContext::default(),
} }
} }
} }
@@ -211,9 +365,7 @@ impl eframe::App for Application {
if self.show_side_panel { if self.show_side_panel {
self.render_side_panel(ctx); self.render_side_panel(ctx);
} }
egui::CentralPanel::default().show(ctx, |ui| { self.render_center_panel(ctx);
ui.heading("// TODO: Put shit here");
});
} }
} }
@@ -257,3 +409,67 @@ async fn search_files(
Ok((results.results, results.metadata)) Ok((results.results, results.metadata))
} }
async fn download(url: String, dir: String) -> Result<String, String> {
let file_name = Path::new(&url)
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string();
let file_path = Path::new(&dir).join(file_name);
if file_path.exists() {
return Ok(file_path.to_str().unwrap().to_string());
}
let mut file = match std::fs::File::create(file_path.clone()) {
Ok(file) => file,
Err(e) => return Err(format!("Failed to create the file: {}", e)),
};
let client = match reqwest::Client::builder()
.user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0",
)
.danger_accept_invalid_certs(true)
.build()
{
Ok(client) => client,
Err(e) => return Err(format!("Failed to create the client: {}", e)),
};
let res = client.get(&url).send().await;
let response = match res {
Ok(response_ok) => response_ok,
Err(e) => return Err(format!("Failed to download the file: {}", e)),
};
if response.status() != reqwest::StatusCode::OK {
return Err(format!(
"Failed to download the file: {}",
response.status()
));
}
let total_size_res = response
.content_length()
.ok_or("Failed to get the file size");
let total_size = match total_size_res {
Ok(res_ok) => res_ok,
Err(e) => return Err(format!("Failed to get the file size: {}", e)),
};
let mut downloaded: u64 = 0;
let mut stream = response.bytes_stream();
while let Some(item) = stream.next().await {
let chunk = item.or(Err(format!(
"Error while downloading file: {}",
url.clone()
)))?;
file.write_all(&chunk).or(Err(format!(
"Error while writing to file: {}",
file_path.to_str().unwrap()
)))?;
let new = min(downloaded + (chunk.len() as u64), total_size);
downloaded = new;
}
Ok(file_path.to_str().unwrap().to_string())
}

View File

@@ -22,7 +22,13 @@ pub struct FileEntry {
pub struct Metadata { pub struct Metadata {
pub page: usize, pub page: usize,
pub total_pages: usize, pub total_pages: usize,
pub page_size: usize pub page_size: usize,
}
#[derive(Default)]
pub struct DownloadContext {
pub is_downloading: bool,
pub download_rx: Option<mpsc::Receiver<Result<String, String>>>,
} }
pub struct SearchContext { pub struct SearchContext {