diff --git a/ytdlp-interface/forms/form_settings.cpp b/ytdlp-interface/forms/form_settings.cpp index 35282f7..411126b 100644 --- a/ytdlp-interface/forms/form_settings.cpp +++ b/ytdlp-interface/forms/form_settings.cpp @@ -105,13 +105,21 @@ void GUI::fm_settings() fm["about"] << about; about.div(R"(vert - - - > - > - > - > - > + + + > + > + > + > + > + + > + > + > + > + > + > + > )"); std::string vertext {ver_tag + " (" + (X64 ? "64-bit)" : "32-bit)") + @@ -120,12 +128,16 @@ void GUI::fm_settings() about_label l_about_ver {about, ""}; nana::panel pnl_header {about.handle()}; - widgets::Separator about_sep1 {about}; - widgets::Title libtitle {about, nana::to_string(u8"\u2606 Libraries used \u2606")}; + widgets::Separator about_sep1 {about}, about_sep2 {about}; + widgets::Title libtitle {about, nana::to_string(u8"\u2606 Libraries used \u2606")}, + kbtitle {about, nana::to_string(u8"\u2606 Keyboard shortcuts \u2606")}; widgets::Label l_nana {about, "Nana C++ GUI library"}, l_jpeg {about, "libjpeg-turbo"}, l_bit7z {about, "bit7z"}, - l_png {about, "libpng"}, l_json {about, "JSON for Modern C++"}; + l_png {about, "libpng"}, l_json {about, "JSON for Modern C++"}, l_ctrls {about, "Ctrl+S"}, l_ctrlf {about, "Ctrl+F"}, + l_ctrltab {about, "Ctrl+Tab"}, l_f2 {about, "F2"}, l_del {about, "Delete"}, l_esc {about, "Esc"}, l_ctrlnum0 {about, "Ctrl+Num0"}; widgets::Text l_nana_ver {about, "v1.8 (custom)"}, l_jpeg_ver {about, "v2.1.5.1"}, l_bit7z_ver {about, "v3.1.3"}, - l_png_ver {about, "v1.6.37"}, l_json_ver {about, "v3.11.3"}; + l_png_ver {about, "v1.6.37"}, l_json_ver {about, "v3.11.3"}, l_settings {about, "Settings"}, l_formats {about, "Formats"}, + l_view {about, "Switch view (queue/output)"}, l_fname {about, "Set file name of queue item"}, + l_delitem {about, "Delete queue item(s)"}, l_close {about, "Close window"}, l_winpos {about, "Reset window size and position"}; about["title"] << title; about["pnl_header"] << pnl_header; about["l_about_ver"] << l_about_ver; @@ -141,6 +153,22 @@ void GUI::fm_settings() about["l_png_ver"] << l_png_ver; about["l_bit7z"] << l_bit7z; about["l_bit7z_ver"] << l_bit7z_ver; + about["about_sep2"] << about_sep2; + about["kbtitle"] << kbtitle; + about["l_ctrls"] << l_ctrls; + about["l_settings"] << l_settings; + about["l_ctrlf"] << l_ctrlf; + about["l_formats"] << l_formats; + about["l_ctrltab"] << l_ctrltab; + about["l_view"] << l_view; + about["l_f2"] << l_f2; + about["l_fname"] << l_fname; + about["l_del"] << l_del; + about["l_delitem"] << l_delitem; + about["l_esc"] << l_esc; + about["l_close"] << l_close; + about["l_ctrlnum0"] << l_ctrlnum0; + about["l_winpos"] << l_winpos; libtitle.format(true); diff --git a/ytdlp-interface/gui.cpp b/ytdlp-interface/gui.cpp index 6ee446c..b097b45 100644 --- a/ytdlp-interface/gui.cpp +++ b/ytdlp-interface/gui.cpp @@ -140,28 +140,43 @@ GUI::GUI() : themed_form {std::bind(&GUI::apply_theme, this, std::placeholders:: } else { - if(sel.size() == lbq.at(0).size()) + if(sel.size() == lbq.at(sel.front().cat).size()) queue_remove_all(); else queue_remove_selected(); } } } } - else if(wparam == 'A') + else if(wparam == 'F') { if(GetAsyncKeyState(VK_CONTROL) & 0xff00 && queue_panel.visible()) { - auto hsel {lbq.events().selected.connect_front([](const nana::arg_listbox &arg) {arg.stop_propagation(); })}; - lbq.auto_draw(false); - for(auto item : lbq.at(0)) - item.select(true); - lbq.auto_draw(true); - lbq.events().selected.remove(hsel); + if(bottoms.current().btnfmt_visible()) + fm_formats(); + } + } + else if(wparam == 'A') + { + if(GetAsyncKeyState(VK_CONTROL) & 0xff00 && queue_panel.visible() && !lbq.first_visible().empty()) + { + if(api::focus_window() != lbq) + { + size_t cat {0}; + auto sel {lbq.selected()}; + if(!sel.empty()) + cat = sel.front().cat; + auto hsel {lbq.events().selected.connect_front([](const nana::arg_listbox &arg) {arg.stop_propagation(); })}; + lbq.auto_draw(false); + for(auto item : lbq.at(cat)) + item.select(true); + lbq.auto_draw(true); + lbq.events().selected.remove(hsel); + } } } else if(wparam == VK_F2) { - if(queue_panel.visible() && lbq.at(0).size()) + if(queue_panel.visible() && lbq.selected().size()) { auto &bottom {bottoms.current()}; if(!bottom.is_ytplaylist && !bottom.is_bcplaylist && !bottom.is_ytchan && !bottom.is_yttab && !bottom.is_bcchan) @@ -190,78 +205,92 @@ GUI::GUI() : themed_form {std::bind(&GUI::apply_theme, this, std::placeholders:: { if(wparam == VK_UP) { - if(lbq.at(0).size() > 1) + const auto sel {lbq.selected()}; + if(!sel.empty()) { - if(GetAsyncKeyState(VK_SHIFT) & 0xff00) + const auto cat {sel.front().cat}; + if(lbq.at(cat).size() > 1) { - const auto sel {lbq.selected()}; - if(!sel.empty() && sel.front().item > 0) - lbq.at(listbox::index_pair {0, sel.front().item - 1}).select(true, true); + if(GetAsyncKeyState(VK_SHIFT) & 0xff00) + { + if(!sel.empty() && sel.front().item > 0) + lbq.at(listbox::index_pair {0, sel.front().item - 1}).select(true, true); + } + else if(lbq.at(cat).size() > 1) + lbq.move_select(true); + return false; } - else if(lbq.at(0).size() > 1) - lbq.move_select(true); - return false; } } else if(wparam == VK_DOWN) { - if(lbq.at(0).size() > 1) + const auto sel {lbq.selected()}; + if(!sel.empty()) { - if(GetAsyncKeyState(VK_SHIFT) & 0xff00) + const auto cat {sel.front().cat}; + if(lbq.at(cat).size() > 1) { - const auto sel {lbq.selected()}; - if(!sel.empty() && sel.back().item < lbq.at(0).size() - 1) - lbq.at(listbox::index_pair {0, sel.back().item + 1}).select(true, true); + if(GetAsyncKeyState(VK_SHIFT) & 0xff00) + { + if(!sel.empty() && sel.back().item < lbq.at(cat).size() - 1) + lbq.at(listbox::index_pair {0, sel.back().item + 1}).select(true, true); + } + else lbq.move_select(false); + return false; } - else lbq.move_select(false); - return false; } } else if(wparam == VK_HOME) { - auto cat {lbq.at(0)}; - if(cat.size() > 1) + if(!lbq.first_visible().empty()) { - auto first_item {cat.at(0)}; - if(GetAsyncKeyState(VK_SHIFT) & 0xff00) + const auto sel {lbq.selected()}; + auto cat {lbq.at(sel.empty() ? 0 : sel.front().cat)}; + if(cat.size() > 1) { - const auto sel {lbq.selected()}; - if(!sel.empty() && sel.front().item > 0) + auto first_item {cat.at(0)}; + if(GetAsyncKeyState(VK_SHIFT) & 0xff00) { - for(auto n {sel.front().item}; n != npos; n--) - cat.at(n).select(true); - lbq.scroll(false); + if(!sel.empty() && sel.front().item > 0) + { + for(auto n {sel.front().item}; n != npos; n--) + cat.at(n).select(true); + lbq.scroll(false); + } } + else + { + cat.select(false); + cat.at(0).select(true, true); + } + return false; } - else - { - cat.select(false); - cat.at(0).select(true, true); - } - return false; } } else if(wparam == VK_END) { - auto cat {lbq.at(0)}; - if(cat.size() > 1) + if(!lbq.first_visible().empty()) { - if(GetAsyncKeyState(VK_SHIFT) & 0xff00) + const auto sel {lbq.selected()}; + auto cat {lbq.at(sel.empty() ? 0 : sel.front().cat)}; + if(cat.size() > 1) { - const auto sel {lbq.selected()}; - if(!sel.empty() && sel.back().item < cat.size() - 1) + if(GetAsyncKeyState(VK_SHIFT) & 0xff00) { - for(auto n {sel.back().item}; n < cat.size(); n++) - cat.at(n).select(true); - lbq.scroll(true); + if(!sel.empty() && sel.back().item < cat.size() - 1) + { + for(auto n {sel.back().item}; n < cat.size(); n++) + cat.at(n).select(true); + lbq.scroll(true); + } } + else + { + cat.select(false); + cat.at(cat.size() - 1).select(true, true); + } + return false; } - else - { - cat.select(false); - cat.at(cat.size()-1).select(true, true); - } - return false; } } } @@ -270,6 +299,17 @@ GUI::GUI() : themed_form {std::bind(&GUI::apply_theme, this, std::placeholders:: close(); return false; } + else if(wparam == VK_TAB) + { + if(GetAsyncKeyState(VK_CONTROL) & 0xff00) + { + if(queue_panel.visible()) + show_output(); + else show_queue(); + api::refresh_window(*this); + return false; + } + } return true; }); @@ -484,9 +524,9 @@ GUI::GUI() : themed_form {std::bind(&GUI::apply_theme, this, std::placeholders:: const auto url {to_utf8(wurl)}; conf.unfinished_queue_items.push_back(url); auto &j {unfinished_qitems_data[url]}; - bottoms.at(url).to_json(j); + auto &bottom {bottoms.at(url)}; + bottom.to_json(j); j["columns"]["website"] = item.text(1); - j["columns"]["title"] = item.text(2); j["columns"]["status"] = text; j["columns"]["format"] = item.text(4); j["columns"]["format_note"] = item.text(5); @@ -1292,7 +1332,24 @@ void GUI::add_url(std::wstring url, bool refresh, bool saveq) { auto item {lbq.item_from_value(url)}; item.text(1, j["columns"]["website"].get()); - item.text(2, j["columns"]["title"].get()); + std::wstring_convert, char16_t> u16conv; + auto media_title {bottom.media_title}; + if(!is_utf8(media_title)) + { + std::wstring_convert, char16_t> u16conv; + auto u16str {u16conv.from_bytes(media_title)}; + std::wstring wstr(u16str.size(), L'\0'); + memcpy(&wstr.front(), &u16str.front(), wstr.size() * 2); + std::wstring_convert> u8conv; + media_title = u8conv.to_bytes(wstr); + } + if(media_title.empty()) + { + if(j["columns"].contains("title")) + item.text(2, j["columns"]["title"].get()); + else item.text(2, "!!! failed to restore the media title !!!"); + } + else item.text(2, media_title); item.text(3, j["columns"]["status"].get()); if(item.text(3) == "skip") { @@ -1503,6 +1560,7 @@ void GUI::add_url(std::wstring url, bool refresh, bool saveq) break; } auto media_title {bottom.playlist_info["title"].get()}; + bottom.media_title = media_title; if(!is_utf8(media_title)) { @@ -1678,6 +1736,7 @@ void GUI::add_url(std::wstring url, bool refresh, bool saveq) if(bottom.vidinfo_contains("formats")) bottom.show_btnfmt(true); } + bottom.media_title = media_title; if(!is_utf8(media_title)) { std::wstring_convert, char16_t> u16conv; diff --git a/ytdlp-interface/gui.hpp b/ytdlp-interface/gui.hpp index ccb4673..6e9c769 100644 --- a/ytdlp-interface/gui.hpp +++ b/ytdlp-interface/gui.hpp @@ -49,7 +49,7 @@ class GUI : public themed_form, IDropTarget thr_qitem_data; CComPtr i_taskbar; UINT WM_TASKBAR_BUTTON_CREATED {0}; - const std::string ver_tag {"v2.14.0"}, title {"ytdlp-interface " + ver_tag/*.substr(0, 5)*/}, + const std::string ver_tag {"v2.14.1"}, title {"ytdlp-interface " + ver_tag/*.substr(0, 5)*/}, ytdlp_fname {X64 ? "yt-dlp.exe" : "yt-dlp_x86.exe"}; const unsigned MINW {900}, MINH {700}; // min client area size nana::drawerbase::listbox::item_proxy *last_selected {nullptr}; @@ -89,6 +89,7 @@ class GUI : public themed_form, IDropTarget std::vector playlist_selection; std::vector> sections; std::wstring url, strfmt, fmt1, fmt2, playsel_string, cmdinfo, playlist_vid_cmdinfo; + std::string media_title; std::thread dl_thread, info_thread; int index {0}; unsigned idx_error {0}; diff --git a/ytdlp-interface/gui_bottom.cpp b/ytdlp-interface/gui_bottom.cpp index c27c109..de2b775 100644 --- a/ytdlp-interface/gui_bottom.cpp +++ b/ytdlp-interface/gui_bottom.cpp @@ -405,8 +405,8 @@ GUI::gui_bottom::gui_bottom(GUI &gui, bool visible) com_chap.events().selected([&] { - conf.com_chap = com_rate.option(); - if(conf.common_dl_options && com_rate == api::focus_window()) + conf.com_chap = com_chap.option(); + if(conf.common_dl_options) gui.bottoms.propagate_misc_options(*this); }); @@ -823,6 +823,8 @@ void GUI::gui_bottom::from_json(const nlohmann::json &j) pgui->outbox.buffer(url, j["output_buffer"].get()); if(j.contains("dlcmd")) pgui->outbox.commands[url] = j["dlcmd"].get(); + if(j.contains("media_title")) + media_title = j["media_title"].get(); if(j.contains("outfile")) { outfile = fs::u8path(j["outfile"].get()); @@ -885,6 +887,8 @@ void GUI::gui_bottom::to_json(nlohmann::json &j) j["dlcmd"] = pgui->outbox.commands[url]; if(!outfile.empty()) j["outfile"] = outfile.string(); + if(!media_title.empty()) + j["media_title"] = media_title; if(!conf.common_dl_options) { j["outpath"] = outpath.string(); diff --git a/ytdlp-interface/widgets.cpp b/ytdlp-interface/widgets.cpp index 8ff5f08..db069fa 100644 --- a/ytdlp-interface/widgets.cpp +++ b/ytdlp-interface/widgets.cpp @@ -391,12 +391,13 @@ std::string Listbox::favicon_url_from_value(std::wstring val) } -nana::drawerbase::listbox::item_proxy Listbox::item_from_value(std::wstring val, size_t cat) +nana::drawerbase::listbox::item_proxy Listbox::item_from_value(std::wstring val) { - for(auto item : at(cat)) - if(item.value() == val) - return item; - return at(cat).end(); + for(size_t cat {0}; cat < size_categ(); cat++) + for(auto item : at(cat)) + if(item.value() == val) + return item; + return at(0).end(); } diff --git a/ytdlp-interface/widgets.hpp b/ytdlp-interface/widgets.hpp index 7982ad1..f066c15 100644 --- a/ytdlp-interface/widgets.hpp +++ b/ytdlp-interface/widgets.hpp @@ -227,7 +227,7 @@ namespace widgets size_t item_count(); std::string favicon_url_from_value(std::wstring val); - nana::drawerbase::listbox::item_proxy item_from_value(std::wstring val, size_t cat = 0); + nana::drawerbase::listbox::item_proxy item_from_value(std::wstring val); void hilight_checked(bool enable) { hilite_checked = enable; refresh_theme(); } void refresh_theme(); void fit_column_content(); diff --git a/ytdlp-interface/ytdlp-interface.rc b/ytdlp-interface/ytdlp-interface.rc index 82dff35..b3d88d4 100644 --- a/ytdlp-interface/ytdlp-interface.rc +++ b/ytdlp-interface/ytdlp-interface.rc @@ -61,8 +61,8 @@ IDI_APPICON ICON "Youtube-Round.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,14,0,0 - PRODUCTVERSION 2,14,0,0 + FILEVERSION 2,14,1,0 + PRODUCTVERSION 2,14,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -78,12 +78,12 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Graphical interface for yt-dlp.exe" - VALUE "FileVersion", "2.14.0.0" + VALUE "FileVersion", "2.14.1.0" VALUE "InternalName", "ytdlp-interface.exe" VALUE "LegalCopyright", "Copyright (C) 2023" VALUE "OriginalFilename", "ytdlp-interface.exe" VALUE "ProductName", "ytdlp-interface" - VALUE "ProductVersion", "2.14.0.0" + VALUE "ProductVersion", "2.14.1.0" END END BLOCK "VarFileInfo"