add modification
This commit is contained in:
parent
c3a225ff77
commit
86ffdd03d1
3 changed files with 334 additions and 16 deletions
92
Cargo.lock
generated
92
Cargo.lock
generated
|
@ -2,6 +2,15 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
|
@ -47,12 +56,28 @@ dependencies = [
|
|||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_home"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "field-offset"
|
||||
version = "0.3.6"
|
||||
|
@ -402,6 +427,12 @@ version = "0.2.175"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
|
@ -486,6 +517,35 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
|
@ -495,6 +555,19 @@ dependencies = [
|
|||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
|
@ -618,6 +691,17 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d"
|
||||
dependencies = [
|
||||
"env_home",
|
||||
"rustix",
|
||||
"winsafe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
|
@ -707,10 +791,18 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winsafe"
|
||||
version = "0.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||
|
||||
[[package]]
|
||||
name = "yt-snd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gio",
|
||||
"gtk4",
|
||||
"regex",
|
||||
"which",
|
||||
]
|
||||
|
|
|
@ -6,3 +6,5 @@ edition = "2021"
|
|||
[dependencies]
|
||||
gio = "0.21.1"
|
||||
gtk4 = { version = "0.10.0", features = ["v4_12", "v4_14", "v4_10"] }
|
||||
regex = "1.11.1"
|
||||
which = "8.0.0"
|
||||
|
|
256
src/main.rs
256
src/main.rs
|
@ -4,16 +4,168 @@ use gio::glib::property::PropertyGet;
|
|||
use gtk4 as gtk;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{glib, Application, ApplicationWindow, Button, ScrolledWindow};
|
||||
use gio::ListModel;
|
||||
use regex::Regex;
|
||||
use std::process::Command;
|
||||
use which::which;
|
||||
use crate::glib::GString;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum OutputFormat {
|
||||
FLAC,
|
||||
MP3,
|
||||
OGG,
|
||||
}
|
||||
|
||||
fn main() -> glib::ExitCode {
|
||||
|
||||
fn show_multi_select_error(v: ListModel, w: Option<&ApplicationWindow>, path_entry: gtk4::Entry){
|
||||
let orig_path = path_entry.buffer().text();
|
||||
let warning = gtk::AlertDialog::builder()
|
||||
.modal(true)
|
||||
.cancel_button(2)
|
||||
.message("Muitlple folder choosing is not permited!")
|
||||
.detail(format!("Only one folder should be selected, but you selected {} item(s). None of them will be selected.", v.n_items()))
|
||||
.default_button(1)
|
||||
.build();
|
||||
|
||||
warning.show(w);
|
||||
path_entry.buffer().set_text(orig_path);
|
||||
}
|
||||
|
||||
fn show_generic_error(w : Option<&ApplicationWindow>, msg : &str, detail : &str){
|
||||
let err_dialog = gtk::AlertDialog::builder()
|
||||
.modal(true)
|
||||
.message(msg)
|
||||
.detail(detail)
|
||||
.build();
|
||||
|
||||
err_dialog.show(w);
|
||||
}
|
||||
|
||||
fn show_empty_output_error(w: Option<&ApplicationWindow>){
|
||||
show_generic_error(w, "Empty path is not permitted!", "Please insert the video urls in the box");
|
||||
}
|
||||
|
||||
//let mut links = vec!();
|
||||
|
||||
fn download_and_convert(mut links : &Vec<&'static str>,
|
||||
output_path : &str,
|
||||
format : OutputFormat,
|
||||
tmp_window_clone : &ApplicationWindow,
|
||||
status_info_label: >k4::Label){
|
||||
let tmp_window_clone = tmp_window_clone.clone();
|
||||
let tmp_window_clone2: ApplicationWindow = tmp_window_clone.clone();
|
||||
|
||||
fn real_download(output_path: &str, format: OutputFormat, tmp_window_clone: &ApplicationWindow, status_info_label: >k4::Label, lnk: String, is_processed: bool) {
|
||||
let output_format = match format {
|
||||
OutputFormat::FLAC => "flac",
|
||||
OutputFormat::MP3 => "mp3",
|
||||
OutputFormat::OGG => "vorbis",
|
||||
};
|
||||
|
||||
if is_processed == true {
|
||||
let res = Command::new("yt-dlp")
|
||||
.arg(&lnk)
|
||||
.arg("--paths")
|
||||
.arg(output_path)
|
||||
.arg("--extract-audio")
|
||||
.arg("--audio-format")
|
||||
.arg(output_format)
|
||||
.output();
|
||||
|
||||
match res {
|
||||
Ok(_) => status_info_label.set_text(format!("\"{}\" downloaded!", &lnk).as_str()),
|
||||
Err(masg) => {
|
||||
let title = "Downloading or converting failed!";
|
||||
let failed_detail_format = format!("\"{}\" failed to download or convert. More information:\n{}", lnk, masg);
|
||||
let failed_detail = failed_detail_format.as_str();
|
||||
status_info_label.set_text(failed_detail);
|
||||
show_generic_error(Some(&tmp_window_clone) ,title , failed_detail);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
let yt_dlp_result = which("yt-dlp");
|
||||
let ffmpeg_result = which("ffmpeg");
|
||||
|
||||
let mut lack_of_program: Vec<&'static str> = vec!();
|
||||
|
||||
match yt_dlp_result {
|
||||
Ok(_) => (),
|
||||
Err(_) => lack_of_program.push("yt-dlp"),
|
||||
}
|
||||
|
||||
|
||||
match ffmpeg_result {
|
||||
Ok(_) => (),
|
||||
Err(_) => lack_of_program.push("ffmpeg"),
|
||||
}
|
||||
|
||||
|
||||
if lack_of_program.len() > 0{
|
||||
let lack_of_program_str_joined = lack_of_program.join(", ");
|
||||
|
||||
let detail = format!("Please install the following program(s):\n{}", lack_of_program_str_joined);
|
||||
let detail_str = detail.as_str();
|
||||
show_generic_error(Some(&tmp_window_clone), "Dependent program lacked", detail_str);
|
||||
}
|
||||
|
||||
let mut links = links.clone();
|
||||
for lnk in links.iter_mut(){
|
||||
|
||||
let re = Regex::new(r"list=").unwrap();
|
||||
let capturing = re.captures(lnk);
|
||||
|
||||
let mut is_processed = true;
|
||||
|
||||
let format_clone = format.clone();
|
||||
let lnk_clone = lnk.clone();
|
||||
match capturing{
|
||||
None =>{is_processed = true; real_download(output_path.clone(), format_clone, &tmp_window_clone, status_info_label, lnk.to_string(), is_processed);},
|
||||
Some(v) => {
|
||||
is_processed = false;
|
||||
let dialog = gtk::AlertDialog::builder()
|
||||
//.modal(true)
|
||||
.cancel_button(1)
|
||||
.message("\"list\" contained in one of the url")
|
||||
.detail(format!("It will download and convert ALL the videoes!"))
|
||||
.default_button(0)
|
||||
.buttons(vec!["Continue".to_string(), "Cancel".to_string()])
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
|
||||
dialog.choose(Some(&tmp_window_clone), gio::Cancellable::NONE, move |result| {
|
||||
if result.is_err() || result.unwrap() != 1 {
|
||||
println!("continue");
|
||||
let output_path = Box::new(output_path);
|
||||
real_download(*output_path, format, &tmp_window_clone2, status_info_label, lnk_clone.to_string(), is_processed);
|
||||
}else{
|
||||
println!("cancel");
|
||||
is_processed = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
_ = gtk::init();
|
||||
let main_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
|
||||
let label_box_of_links: gtk4::Label = gtk::Label::new(Some("Enter links from Youtube, seperated by newline character (Enter)"));
|
||||
let label_box_of_links: gtk4::Label = gtk::Label::new(Some("Enter links from Youtube, seperated by newline character (Enter):"));
|
||||
label_box_of_links.set_halign(gtk::Align::Start); // align from starting (left)
|
||||
|
||||
let scrolled_window = ScrolledWindow::new();
|
||||
|
@ -40,7 +192,7 @@ fn main() -> glib::ExitCode {
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -48,7 +200,7 @@ fn main() -> glib::ExitCode {
|
|||
|
||||
let buffer = path_entry.buffer();
|
||||
let home_path2 = format!("{}", home_path.clone());
|
||||
let home_path3 = format!("{}", home_path.clone());
|
||||
//let home_path3 = format!("{}", home_path.clone());
|
||||
|
||||
buffer.set_text(home_path2.to_string());
|
||||
|
||||
|
@ -68,9 +220,18 @@ fn main() -> glib::ExitCode {
|
|||
let format_run_box: gtk4::Box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
let format_label: gtk4::Label = gtk::Label::new(Some("Format:"));
|
||||
let flac_checker = gtk::CheckButton::with_label("flac");
|
||||
let use_flac = flac_checker.is_active();
|
||||
|
||||
let mp3_checker = gtk::CheckButton::with_label("mp3");
|
||||
mp3_checker.set_active(true);
|
||||
let use_mp3: bool = mp3_checker.is_active();
|
||||
|
||||
let ogg_checker: gtk4::CheckButton = gtk::CheckButton::with_label("ogg");
|
||||
let dummy_label: gtk4::Label = gtk::Label::new(Some(""));
|
||||
let use_ogg = ogg_checker.is_active();
|
||||
|
||||
//println!("mp3 {} | ogg {}", useMp3 , useOgg);
|
||||
|
||||
let dummy_label: gtk4::Label = gtk::Label::new(Some("")); // interface workaround
|
||||
dummy_label.set_hexpand(true);
|
||||
|
||||
let convert_button = Button::builder()
|
||||
|
@ -82,6 +243,8 @@ fn main() -> glib::ExitCode {
|
|||
.halign(gtk::Align::End)
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
flac_checker.set_halign(gtk::Align::Start);
|
||||
mp3_checker.set_halign(gtk::Align::Start);
|
||||
ogg_checker.set_halign(gtk::Align::Start);
|
||||
|
@ -108,6 +271,10 @@ fn main() -> glib::ExitCode {
|
|||
main_box.append(&path_box);
|
||||
main_box.append(&format_run_box);
|
||||
|
||||
let status_info_label: gtk4::Label = gtk::Label::new(Some("Ready")); // interface workaround
|
||||
status_info_label.set_halign(gtk::Align::Start);
|
||||
main_box.append(&status_info_label);
|
||||
|
||||
|
||||
let app = Application::builder()
|
||||
.application_id("info.kianting.yt-snd")
|
||||
|
@ -116,7 +283,7 @@ fn main() -> glib::ExitCode {
|
|||
|
||||
|
||||
|
||||
let cancellable: Option<&gio::Cancellable> = None;
|
||||
let cancellable: Option<&gio::Cancellable> = None;
|
||||
|
||||
|
||||
|
||||
|
@ -129,6 +296,50 @@ fn main() -> glib::ExitCode {
|
|||
.child(&main_box)
|
||||
.build();
|
||||
|
||||
let path_entry_cloned_for_converting = path_entry.clone();
|
||||
|
||||
let text_buffer2 = text_buffer.clone();
|
||||
let tmp_window_clone: ApplicationWindow = window.clone();
|
||||
let start_iter = text_buffer2.start_iter();
|
||||
let end_iter = text_buffer2.end_iter();
|
||||
//let mut links = text_buffer2.text(&start_iter, &end_iter, true);
|
||||
|
||||
let status_info_label_cloned = status_info_label.clone();
|
||||
convert_button.connect_clicked(move|_|{
|
||||
let new_line_pattern = Regex::new(r"(\r?\n)+").expect("invalid regex");
|
||||
|
||||
let mut links = text_buffer2.text(&start_iter, &end_iter, true);
|
||||
let link_as_str = links.as_str();
|
||||
|
||||
let link_vector = new_line_pattern.split(link_as_str).collect();
|
||||
|
||||
|
||||
if link_vector == [""]{
|
||||
show_empty_output_error(Some(&tmp_window_clone));
|
||||
}
|
||||
|
||||
let output_path = path_entry_cloned_for_converting.buffer().text();
|
||||
|
||||
let format: OutputFormat;
|
||||
|
||||
|
||||
if use_mp3 == true{
|
||||
format = OutputFormat::MP3;
|
||||
}
|
||||
else if use_ogg == true{
|
||||
format = OutputFormat::OGG;
|
||||
}else {
|
||||
format = OutputFormat::FLAC;
|
||||
}
|
||||
|
||||
download_and_convert(link_vector, output_path.as_str(),
|
||||
format,
|
||||
&tmp_window_clone,
|
||||
&status_info_label_cloned);
|
||||
|
||||
|
||||
});
|
||||
|
||||
let window_clone = window.clone();
|
||||
|
||||
let path_entry_cloned = path_entry.clone();
|
||||
|
@ -139,27 +350,40 @@ fn main() -> glib::ExitCode {
|
|||
let opened_directory_wrapped = gio::File::for_path(&opened_directory);
|
||||
folder_dialog.set_initial_folder(Some(&opened_directory_wrapped));
|
||||
folder_dialog.set_modal(false);
|
||||
folder_dialog.select_multiple_folders(Some(&window_clone), cancellable, |x| {
|
||||
let window_clone2 = window_clone.clone();
|
||||
let path_entry_cloned2 = path_entry_cloned.clone();
|
||||
folder_dialog.select_multiple_folders(Some(&window_clone), cancellable, move |x | {
|
||||
|
||||
|
||||
println!( "{}", match x {
|
||||
Ok(v) => {let a = format!("==={:?}", v.item(0));
|
||||
let b = v.item(0).unwrap().downcast::<gio::File>().unwrap().path().unwrap();
|
||||
|
||||
println!("{:?}", b);
|
||||
|
||||
a}
|
||||
match x {
|
||||
Ok(v) => {
|
||||
let link_list_length = v.n_items();
|
||||
if link_list_length > 1{
|
||||
show_multi_select_error(v.clone(), Some(&window_clone2), path_entry_cloned2);
|
||||
"multiple select error".to_string()
|
||||
}else{
|
||||
|
||||
let path_ = v.item(0).unwrap().downcast::<gio::File>().unwrap().path();
|
||||
let path2 = path_.unwrap();
|
||||
let path3 = path2.to_str().unwrap();
|
||||
|
||||
|
||||
path_entry_cloned2.buffer().set_text(path3);
|
||||
|
||||
"Ok".to_string()
|
||||
}
|
||||
}
|
||||
Err(_) => "Error!".to_string(),
|
||||
});
|
||||
};
|
||||
});
|
||||
//folder_dialog.save(Some(&window_clone), cancellable, move |folder|{println!("~~~~~~")})
|
||||
});
|
||||
|
||||
window.present();
|
||||
|
||||
// Show the window.
|
||||
|
||||
});
|
||||
|
||||
app.run()
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue