use crate::core::{
download_mode::{DownloadSource, DownloadType, Quality},
flags::Flag,
runner::{self, ChildHandle, DownloadRequest},
};
use dioxus::prelude::*;
#[derive(Props, Clone, PartialEq)]
pub struct UrlBarProps {
pub download_type: Signal<DownloadType>,
pub download_source: Signal<DownloadSource>,
pub quality: Signal<Quality>,
pub url: Signal<String>,
pub batch_file: Signal<String>,
pub archive_file: Signal<String>,
pub output_dir: Signal<String>,
pub built_command: ReadSignal<String>,
pub active_flags: Signal<Vec<Flag>>,
pub log_lines: Signal<Vec<String>>,
pub is_running: Signal<bool>,
pub child_handle: Signal<ChildHandle>,
}
#[component]
pub fn UrlBar(props: UrlBarProps) -> Element {
let mut url = props.url;
let batch_file = props.batch_file;
let mut archive_file = props.archive_file;
let output_dir = props.output_dir;
let built_command = props.built_command;
let active_flags = props.active_flags;
let log_lines = props.log_lines;
let is_running = props.is_running;
let child_handle = props.child_handle;
let download_type = props.download_type;
let download_source = props.download_source;
let quality = props.quality;
let is_batch = *download_source.read() == DownloadSource::Batch;
let on_pick_batch = move |_| {
let mut batch_file = batch_file;
async move {
if let Some(file) = rfd::AsyncFileDialog::new()
.set_title("Choose batch URL file")
.add_filter("Text files", &["txt"])
.pick_file()
.await
{
batch_file.set(file.path().to_string_lossy().to_string());
}
}
};
let on_pick_archive = move |_| {
let mut archive_file = archive_file;
async move {
if let Some(file) = rfd::AsyncFileDialog::new()
.set_title("Choose download archive file")
.add_filter("Text files", &["txt"])
.pick_file()
.await
{
archive_file.set(file.path().to_string_lossy().to_string());
}
}
};
let on_pick_folder = move |_| {
let mut output_dir = output_dir;
async move {
if let Some(folder) = rfd::AsyncFileDialog::new()
.set_title("Choose download folder")
.pick_folder()
.await
{
output_dir.set(folder.path().to_string_lossy().to_string());
}
}
};
let on_download = move |_| {
let req = DownloadRequest {
url: url.read().clone(),
batch_file: batch_file.read().clone(),
archive_file: archive_file.read().clone(),
download_type: download_type.read().clone(),
download_source: download_source.read().clone(),
quality: quality.read().clone(),
output_dir: output_dir.read().clone(),
extra_flags: active_flags.read().clone(),
};
let log = log_lines;
let running = is_running;
let handle = child_handle.read().clone();
spawn(async move {
runner::run_download(req, log, running, handle).await;
});
};
let on_stop = move |_| {
let handle = child_handle.read().clone();
runner::cancel_download(&handle, log_lines, is_running);
};
rsx! {
div {
style: "display: flex; flex-direction: column; gap: 8px;",
div {
style: "display: flex; gap: 8px; align-items: center;",
if is_batch {
div {
style: "
flex: 1;
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
background: #1e1e2e;
border: 1px solid #3a3a5a;
border-radius: 6px;
",
span { style: "font-size: 14px;", "📄" }
span {
style: "
flex: 1;
color: #e0e0e0;
font-size: 13px;
font-family: monospace;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
",
if batch_file.read().is_empty() {
span { style: "color: #444;", "Pick a .txt file with one URL per line…" }
} else {
"{batch_file}"
}
}
button {
style: "
padding: 4px 10px;
background: #2a2a4a;
color: #a0a0c0;
border: 1px solid #3a3a6a;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
white-space: nowrap;
",
onclick: on_pick_batch,
"Pick file"
}
}
} else {
input {
style: "
flex: 1;
padding: 10px 14px;
background: #1e1e2e;
border: 1px solid #3a3a5a;
border-radius: 6px;
color: #e0e0e0;
font-size: 14px;
outline: none;
",
r#type: "text",
placeholder: "Paste a YouTube / any URL here…",
value: "{url}",
oninput: move |e| url.set(e.value()),
}
}
button {
style: btn_style(BtnKind::Folder),
onclick: on_pick_folder,
"📁"
}
if *is_running.read() {
button {
style: btn_style(BtnKind::Stop),
onclick: on_stop,
"⏹ Stop"
}
} else {
button {
style: btn_style(BtnKind::Download),
onclick: on_download,
"▶ Download"
}
}
}
div {
style: "display: flex; align-items: center; gap: 8px;",
span { style: "font-size: 11px; color: #444; min-width: 70px;", "⏺ Archive" }
input {
style: "
flex: 1;
padding: 6px 10px;
background: #0d0d1a;
border: 1px solid #1e1e3a;
border-radius: 4px;
color: #888;
font-size: 11px;
font-family: monospace;
outline: none;
",
r#type: "text",
placeholder: "Path to download archive .txt (optional)",
value: "{archive_file}",
oninput: move |e| archive_file.set(e.value()),
}
button {
style: "
padding: 5px 8px;
background: #1a1a2e;
color: #555;
border: 1px solid #2a2a4a;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
",
onclick: on_pick_archive,
"📁"
}
}
div {
style: "font-size: 11px; color: #555; padding: 0 2px;",
"📂 "
span { style: "color: #888;", "{output_dir}" }
}
div {
style: "
background: #060612;
border: 1px solid #1e1e3a;
border-radius: 6px;
padding: 10px 14px;
font-size: 11px;
color: #555;
word-break: break-all;
font-family: monospace;
line-height: 1.6;
",
span { style: "color: #6c63ff;", "$ " }
span { style: "color: #7878aa;", "{built_command}" }
}
}
}
}
enum BtnKind {
Download,
Stop,
Folder,
}
fn btn_style(kind: BtnKind) -> &'static str {
match kind {
BtnKind::Download => {
"
padding: 10px 18px;
background: #6c63ff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: bold;
white-space: nowrap;
"
}
BtnKind::Stop => {
"
padding: 10px 18px;
background: #ff4444;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: bold;
white-space: nowrap;
"
}
BtnKind::Folder => {
"
padding: 10px 12px;
background: #1e1e3a;
color: #888;
border: 1px solid #2a2a4a;
border-radius: 6px;
cursor: pointer;
font-size: 15px;
"
}
}
}