From 27eac1c56904a853e79d66e1cf4daac7d8f8ba6b Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 7 May 2021 03:34:05 +0200 Subject: Add ctrl+i reverse image search to 4chan, add saucenao to launcher --- README.md | 9 +--- TODO | 1 - include/QuickMedia.hpp | 2 +- plugins/FileManager.hpp | 5 ++- plugins/Saucenao.hpp | 5 ++- src/QuickMedia.cpp | 101 +++++++++++++++++++++++++++----------------- src/plugins/FileManager.cpp | 2 + src/plugins/Mangadex.cpp | 6 --- src/plugins/Matrix.cpp | 8 ++++ src/plugins/Saucenao.cpp | 12 +++++- 10 files changed, 92 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 8dafdbf..9d1f325 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Press `Ctrl + C` to copy the text of the selected item to the clipboard.\ Press `U` in matrix or in a 4chan thread to bring up the file manager to choose a file to upload.\ Press `Ctrl + V` to upload media to room in matrix if the clipboard contains a valid absolute filepath.\ Press `Ctrl + D` to remove the file that was previously selected with `U` in a 4chan thread.\ +Press `Ctrl + I` to reverse image search the selected image on 4chan.\ Press `Ctrl+Alt+Arrow up` / `Ctrl+Alt+Arrow down` or `Ctrl+Alt+K` / `Ctrl+Alt+J` to view the room above/below the selected room in matrix. In matrix you can select a message with enter to open the url in the message (or if there are multiple urls then a menu will appear for selecting which to open). @@ -66,14 +67,6 @@ In matrix you can select a message with enter to open the url in the message (or `/leave` to leave the current room.\ `/me [text]` to send a message of type "m.emote".\ `/react [text]` to react to the selected message or if you are replying to message then it reacts to that message. -## Mangadex -To search for manga with mangadex, you need to be logged into mangadex in your browser and copy the `mangadex_rememberme_token` cookie from developer tools -and store it in `$HOME/.config/quickmedia/credentials/mangadex.json` under the key `rememberme_token`. Here is an example what the file should look like: -``` -{ - "rememberme_token": "21s9d3f7ad224a131239Dsfaf033029d2e390dAsfd3ldadb3a39dk43jfldj35das" -} -``` ## Environment variables Set `QM_PHONE_FACTOR=1` to disable the room list side panel in matrix. ## UI scaling diff --git a/TODO b/TODO index 847a185..289316d 100644 --- a/TODO +++ b/TODO @@ -20,7 +20,6 @@ Extract thumbnail from images that are being downloaded, while its downloading a Add setting to disable sending typing events to the server (matrix). Take code from dchat to support gifs (inline in text) and support animated webp (either animated or as a static thumbnail). Scrolling in images still messes up the |current| page sometimes, need a way to fix this. -Add ctrl+i keybind when viewing an image on 4chan to reverse image search it (using google, yandex and saucenao). Show filename at the bottom when viewing an image/video on 4chan. Add ctrl+c keybinding to copy the url of the previewing image. Add ctrl+c keybiding to copy the url to the currently selected post on 4chan. diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index cea9b88..5d5f613 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -100,7 +100,7 @@ namespace QuickMedia { Json::Value load_video_history_json(); private: void init(Window parent_window); - void load_plugin_by_name(std::vector &tabs, const char *start_dir, int &start_tab_index, FileManagerMimeType fm_mime_type); + void load_plugin_by_name(std::vector &tabs, const char *start_dir, int &start_tab_index, FileManagerMimeType fm_mime_type, FileSelectionHandler file_selection_handler); // Returns true if the window was closed bool handle_window_close(); void base_event_handler(sf::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_key_press = true, bool handle_searchbar = true); diff --git a/plugins/FileManager.hpp b/plugins/FileManager.hpp index 2e93a8e..72ac75d 100644 --- a/plugins/FileManager.hpp +++ b/plugins/FileManager.hpp @@ -11,10 +11,12 @@ namespace QuickMedia { }; static const FileManagerMimeType FILE_MANAGER_MIME_TYPE_ALL = (FileManagerMimeType)(FILE_MANAGER_MIME_TYPE_IMAGE|FILE_MANAGER_MIME_TYPE_VIDEO|FILE_MANAGER_MIME_TYPE_OTHER); + // Return the tags to go to after selecting a file, or return an empty array to exit the program + using FileSelectionHandler = std::function()>; class FileManagerPage : public Page { public: - FileManagerPage(Program *program, FileManagerMimeType mime_type = FILE_MANAGER_MIME_TYPE_ALL) : Page(program), current_dir("/"), mime_type(mime_type) {} + FileManagerPage(Program *program, FileManagerMimeType mime_type = FILE_MANAGER_MIME_TYPE_ALL, FileSelectionHandler selection_handler = nullptr) : Page(program), current_dir("/"), mime_type(mime_type), selection_handler(selection_handler) {} const char* get_title() const override { return current_dir.c_str(); } PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; bool is_single_page() const override { return true; } @@ -24,5 +26,6 @@ namespace QuickMedia { private: std::filesystem::path current_dir; FileManagerMimeType mime_type; + FileSelectionHandler selection_handler; }; } \ No newline at end of file diff --git a/plugins/Saucenao.hpp b/plugins/Saucenao.hpp index a5aadf9..297dde3 100644 --- a/plugins/Saucenao.hpp +++ b/plugins/Saucenao.hpp @@ -5,7 +5,7 @@ namespace QuickMedia { class SaucenaoPage : public LazyFetchPage { public: - SaucenaoPage(Program *program, const std::string &upload_filepath) : LazyFetchPage(program), upload_filepath(upload_filepath) {} + SaucenaoPage(Program *program, const std::string &path, bool is_local) : LazyFetchPage(program), path(path), is_local(is_local) {} const char* get_title() const override { return "SauceNAO"; } PluginResult submit(const std::string&, const std::string&, std::vector&) override { return PluginResult::OK; @@ -13,6 +13,7 @@ namespace QuickMedia { PluginResult lazy_fetch(BodyItems &result_items) override; bool is_single_page() const override { return true; } private: - std::string upload_filepath; + std::string path; + bool is_local; }; } \ No newline at end of file diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 9593e80..4647787 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -335,9 +335,9 @@ namespace QuickMedia { if(!thumbnail_url.empty()) { body_item->thumbnail_url = thumbnail_url; body_item->thumbnail_is_local = true; - body_item->thumbnail_size.x = 32; - body_item->thumbnail_size.y = 32; } + body_item->thumbnail_size.x = 32; + body_item->thumbnail_size.y = 32; return body_item; } @@ -414,7 +414,7 @@ namespace QuickMedia { if(upscale_image_action != UpscaleImageAction::NO) { if(!is_manga_plugin(plugin_name)) { - fprintf(stderr, "Option --upscale-images/-upscale-images-force is only valid for manganelo, manganelos, mangatown and mangadex\n"); + fprintf(stderr, "Option --upscale-images/-upscale-images-force is only valid for manga plugins\n"); return -2; } @@ -466,14 +466,21 @@ namespace QuickMedia { int start_tab_index = 0; FileManagerMimeType fm_mine_type = FILE_MANAGER_MIME_TYPE_ALL; + FileSelectionHandler file_selection_handler = nullptr; + + FileSelectionHandler saucenao_file_selection_handler = [this]() { + std::vector tabs; + tabs.push_back(Tab{create_body(), std::make_unique(this, selected_files[0], true), nullptr}); + return tabs; + }; init(parent_window); - bool is_saucenao = (strcmp(plugin_name, "saucenao") == 0); - if(is_saucenao) { + if(strcmp(plugin_name, "saucenao") == 0) { plugin_name = "file-manager"; fm_mine_type = FILE_MANAGER_MIME_TYPE_IMAGE; + file_selection_handler = std::move(saucenao_file_selection_handler); } - load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type); + load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type, std::move(file_selection_handler)); while(!tabs.empty() || matrix) { if(matrix) { @@ -493,10 +500,12 @@ namespace QuickMedia { if(strcmp(plugin_name, "launcher") == 0) { plugin_name = pipe_selected_text.c_str(); - load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type); - } else if(strcmp(plugin_name, "file-manager") == 0 && is_saucenao && !selected_files.empty()) { - plugin_name = "saucenao"; - load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type); + if(strcmp(plugin_name, "saucenao") == 0) { + plugin_name = "file-manager"; + fm_mine_type = FILE_MANAGER_MIME_TYPE_IMAGE; + file_selection_handler = std::move(saucenao_file_selection_handler); + } + load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type, std::move(file_selection_handler)); } } @@ -884,7 +893,7 @@ namespace QuickMedia { return PluginResult::OK; } - void Program::load_plugin_by_name(std::vector &tabs, const char *start_dir, int &start_tab_index, FileManagerMimeType fm_mime_type) { + void Program::load_plugin_by_name(std::vector &tabs, const char *start_dir, int &start_tab_index, FileManagerMimeType fm_mime_type, FileSelectionHandler file_selection_handler) { if(!plugin_name || plugin_name[0] == '\0') return; @@ -921,6 +930,7 @@ namespace QuickMedia { pipe_body->items.push_back(create_launcher_body_item("Readm", "readm", resources_root + "icons/readm_launcher.png")); pipe_body->items.push_back(create_launcher_body_item("Matrix", "matrix", resources_root + "icons/matrix_launcher.png")); pipe_body->items.push_back(create_launcher_body_item("Nyaa.si", "nyaa.si", resources_root + "icons/nyaa_si_launcher.png")); + pipe_body->items.push_back(create_launcher_body_item("SauceNAO", "saucenao", "")); pipe_body->items.push_back(create_launcher_body_item("Soundcloud", "soundcloud", resources_root + "icons/soundcloud_launcher.png")); pipe_body->items.push_back(create_launcher_body_item("Spotify", "spotify", resources_root + "icons/spotify_launcher.png")); pipe_body->items.push_back(create_launcher_body_item("YouTube", "youtube", resources_root + "icons/yt_launcher.png")); @@ -996,7 +1006,7 @@ namespace QuickMedia { boards_page->get_boards(boards_body->items); tabs.push_back(Tab{std::move(boards_body), std::move(boards_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); } else if(strcmp(plugin_name, "file-manager") == 0) { - auto file_manager_page = std::make_unique(this, fm_mime_type); + auto file_manager_page = std::make_unique(this, fm_mime_type, file_selection_handler); if(start_dir && !file_manager_page->set_current_directory(start_dir)) { fprintf(stderr, "Invalid directory provided with --dir: %s\n", start_dir); exit_code = -3; @@ -1009,8 +1019,6 @@ namespace QuickMedia { auto pipe_body = create_body(); PipePage::load_body_items_from_stdin(pipe_body->items); tabs.push_back(Tab{std::move(pipe_body), std::make_unique(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); - } else if(strcmp(plugin_name, "saucenao") == 0) { - tabs.push_back(Tab{create_body(), std::make_unique(this, selected_files[0]), nullptr}); } else if(strcmp(plugin_name, "youtube") == 0) { start_tab_index = 1; tabs.push_back(Tab{create_body(), std::make_unique(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); @@ -1212,11 +1220,6 @@ namespace QuickMedia { show_notification("QuickMedia", "Failed to create directory: " + content_storage_dir.data, Urgency::CRITICAL); exit(1); } - Path credentials_storage_dir = get_storage_dir().join("credentials"); - if(create_directory_recursive(credentials_storage_dir) != 0) { - show_notification("QuickMedia", "Failed to create directory: " + credentials_storage_dir.data, Urgency::CRITICAL); - exit(1); - } // TODO: Make asynchronous for_files_in_dir_sort_last_modified(content_storage_dir, [&history_items, plugin_name](const std::filesystem::path &filepath) { // This can happen when QuickMedia crashes/is killed while writing to storage. @@ -1320,6 +1323,7 @@ namespace QuickMedia { void Program::select_file(const std::string &filepath) { puts(filepath.c_str()); + selected_files.clear(); selected_files.push_back(filepath); } @@ -1471,12 +1475,15 @@ namespace QuickMedia { if(tabs[selected_tab].page->is_single_page()) { if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->clear(); - if(new_tabs.size() == 1) + if(new_tabs.size() == 1 && !new_tabs[0].page) { tabs[selected_tab].body = std::move(new_tabs[0].body); - else + tabs[selected_tab].page->submit_body_item = prev_selected_item; + return; + } else if(new_tabs.empty()) { loop_running = false; - tabs[selected_tab].page->submit_body_item = prev_selected_item; - return; + tabs[selected_tab].page->submit_body_item = prev_selected_item; + return; + } } if(new_tabs.empty()) { @@ -2927,6 +2934,7 @@ namespace QuickMedia { sf::Texture captcha_texture; sf::Sprite captcha_sprite; std::mutex captcha_image_mutex; + std::string attached_image_url; auto attached_image_texture = std::make_unique(); sf::Sprite attached_image_sprite; @@ -3153,21 +3161,21 @@ namespace QuickMedia { video_content_page(thread_page, thread_page, selected_item->get_title(), true, thread_body->items, thread_body->get_selected_item()); redraw = true; } else { - load_image_future.cancel(); - downloading_image = true; - navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE; - load_image_future = AsyncTask([&thread_body]() { - std::string image_data; - BodyItem *selected_item = thread_body->get_selected(); - if(!selected_item || selected_item->url.empty()) { + BodyItem *selected_item = thread_body->get_selected(); + if(selected_item && !selected_item->url.empty()) { + load_image_future.cancel(); + downloading_image = true; + navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE; + attached_image_url = selected_item->url; + load_image_future = AsyncTask([attached_image_url]() { + std::string image_data; + if(download_to_string_cache(attached_image_url, image_data, {}) != DownloadResult::OK) { + show_notification("QuickMedia", "Failed to download image: " + attached_image_url, Urgency::CRITICAL); + image_data.clear(); + } return image_data; - } - if(download_to_string_cache(selected_item->url, image_data, {}) != DownloadResult::OK) { - show_notification("QuickMedia", "Failed to download image: " + selected_item->url, Urgency::CRITICAL); - image_data.clear(); - } - return image_data; - }); + }); + } } } } else if(event.key.code == sf::Keyboard::U) { @@ -3207,6 +3215,15 @@ namespace QuickMedia { if(!clipboard.empty()) sf::Clipboard::setString(sf::String::fromUtf8(clipboard.begin(), clipboard.end())); } + } else if(event.key.code == sf::Keyboard::I && event.key.control) { + BodyItem *selected_item = thread_body->get_selected(); + if(selected_item && !selected_item->url.empty()) { + std::vector saucenao_tabs; + saucenao_tabs.push_back(Tab{create_body(), std::make_unique(this, selected_item->url, false), nullptr}); + page_loop(saucenao_tabs); + redraw = true; + frame_skip_text_entry = true; + } } BodyItem *selected_item = thread_body->get_selected(); @@ -3252,7 +3269,7 @@ namespace QuickMedia { comment_input.move_caret_to_end(); } - if(event.key.code == sf::Keyboard::I) { + if(event.key.code == sf::Keyboard::I && !event.key.control) { frame_skip_text_entry = true; navigation_stage = NavigationStage::REPLYING; comment_input.set_editable(true); @@ -3310,6 +3327,12 @@ namespace QuickMedia { if(event.key.code == sf::Keyboard::Escape || event.key.code == sf::Keyboard::BackSpace) { navigation_stage = NavigationStage::VIEWING_COMMENTS; attached_image_texture.reset(new sf::Texture()); + } else if(event.key.code == sf::Keyboard::I && event.key.control && !attached_image_url.empty()) { + std::vector saucenao_tabs; + saucenao_tabs.push_back(Tab{create_body(), std::make_unique(this, attached_image_url, false), nullptr}); + page_loop(saucenao_tabs); + redraw = true; + frame_skip_text_entry = true; } } } @@ -4885,7 +4908,7 @@ namespace QuickMedia { chat_input.set_editable(false); } - if(event.key.code == sf::Keyboard::I) { + if(event.key.code == sf::Keyboard::I && !event.key.control) { frame_skip_text_entry = true; chat_input.set_editable(true); chat_state = ChatState::TYPING_MESSAGE; diff --git a/src/plugins/FileManager.cpp b/src/plugins/FileManager.cpp index 47a8b57..52f9f4e 100644 --- a/src/plugins/FileManager.cpp +++ b/src/plugins/FileManager.cpp @@ -52,6 +52,8 @@ namespace QuickMedia { if(std::filesystem::is_regular_file(new_path)) { program->select_file(new_path); + if(selection_handler) + result_tabs = selection_handler(); return PluginResult::OK; } diff --git a/src/plugins/Mangadex.cpp b/src/plugins/Mangadex.cpp index 7332e21..f69484b 100644 --- a/src/plugins/Mangadex.cpp +++ b/src/plugins/Mangadex.cpp @@ -97,8 +97,6 @@ namespace QuickMedia { if(!attributes_json.isObject()) continue; - const Json::Value &description_json = attributes_json["description"]; - const Json::Value &title_json = attributes_json["title"]; if(!title_json.isObject()) continue; @@ -112,10 +110,6 @@ namespace QuickMedia { auto body_item = BodyItem::create(std::move(title)); body_item->url = id_json.asString(); - if(description_json.isString()) { - body_item->set_description(description_json.asString()); - body_item->set_description_color(sf::Color(179, 179, 179)); - } result_items.push_back(std::move(body_item)); } diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 6f8b30b..2740a8a 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -1801,10 +1801,18 @@ namespace QuickMedia { case ',': case '@': case ':': + case ';': case '?': case '!': case '<': case '>': + case '{': + case '}': + case '[': + case ']': + case '\'': + case '"': + case '#': case '\0': return true; default: diff --git a/src/plugins/Saucenao.cpp b/src/plugins/Saucenao.cpp index 302f25e..1278bed 100644 --- a/src/plugins/Saucenao.cpp +++ b/src/plugins/Saucenao.cpp @@ -4,8 +4,18 @@ namespace QuickMedia { PluginResult SaucenaoPage::lazy_fetch(BodyItems &result_items) { + std::vector additional_args; + if(is_local) { + additional_args.push_back({ "-F", "file=@" + path }); + } else { + std::string url = path; + if(url[0] == '@') + url = "\\" + url; + additional_args.push_back({ "-F", "url=" + url }); + } + std::string website_data; - DownloadResult download_result = download_to_string("https://saucenao.com/search.php", website_data, {{ "-F", "file=@" + upload_filepath }}, true); + DownloadResult download_result = download_to_string("https://saucenao.com/search.php", website_data, std::move(additional_args), true); if(download_result != DownloadResult::OK) return download_result_to_plugin_result(download_result); QuickMediaHtmlSearch html_search; -- cgit v1.2.3