aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-04-02 00:21:03 +0200
committerdec05eba <dec05eba@protonmail.com>2021-04-02 00:21:03 +0200
commitc2934be8485376571066a652e94ed16ba2bc8d81 (patch)
tree381e69b5b2ebdb42313fab7fa6978fcb966d580c
parentcfa37b674e9493388e7ebd89f53eacbb197cbdcb (diff)
Cancel all tasks when pressing escape to go to previous page or when closing the window
-rw-r--r--TODO2
-rw-r--r--include/AsyncTask.hpp13
-rw-r--r--include/GoogleCaptcha.hpp5
-rw-r--r--include/QuickMedia.hpp11
-rw-r--r--src/GoogleCaptcha.cpp8
-rw-r--r--src/QuickMedia.cpp126
6 files changed, 69 insertions, 96 deletions
diff --git a/TODO b/TODO
index cb64535..16b3513 100644
--- a/TODO
+++ b/TODO
@@ -92,13 +92,11 @@ Implement our own encryption for matrix. This is also needed to make forwarded m
Modify matrix sync to download and parse json but not handle it, and then add a function to handle the json. This would allow us to remove all the mutex code if we would call that new method from the main thread (similar to chromium multithreading).
Fetch replies/pinned message using multiple threads.
Show in room tags list when there is a message in any of the rooms in the tag.
-Cancel video download when pressing escape or closing window (important in matrix).
Support webp. Then switch to the youtube thumbnails from the response json instead of hqdefault, to remove the black bars.
Show images while they download by showing them as scanlines starting from the top. Needed for slow websites such as 4chan.
Use curl parallel download instead of downloading with multiple threads.
Handle matrix groups? (which also contains join, invite, leave...).
Add functionality to ignore users in matrix. This is done with an ignore request and we wont get messages and invites from that user anymore. Also add option to ignore in the invites page.
-Hide invites tab when there are no invites (to make room for other tabs in the future).
Add keybind to go to invites page from any page.
Show marker beside pinned messages tab name if there are new pinned messages.
Make /logout work everywhere, not only in room message input.
diff --git a/include/AsyncTask.hpp b/include/AsyncTask.hpp
index 0d9c453..abdc0f8 100644
--- a/include/AsyncTask.hpp
+++ b/include/AsyncTask.hpp
@@ -26,6 +26,19 @@ namespace QuickMedia {
return *this;
}
+ AsyncTask(AsyncTask &&other) {
+ cancel();
+ thread = std::move(other.thread);
+ future = std::move(other.future);
+ }
+
+ AsyncTask& operator=(AsyncTask &&other) {
+ cancel();
+ thread = std::move(other.thread);
+ future = std::move(other.future);
+ return *this;
+ }
+
~AsyncTask() {
cancel();
}
diff --git a/include/GoogleCaptcha.hpp b/include/GoogleCaptcha.hpp
index 62e47fa..f8063f3 100644
--- a/include/GoogleCaptcha.hpp
+++ b/include/GoogleCaptcha.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "AsyncTask.hpp"
#include <string>
#include <optional>
#include <functional>
@@ -15,7 +16,7 @@ namespace QuickMedia {
};
using RequestChallengeResponse = std::function<void(std::optional<GoogleCaptchaChallengeInfo>)>;
- std::future<bool> google_captcha_request_challenge(const std::string &api_key, const std::string &referer, RequestChallengeResponse challenge_response_callback);
+ AsyncTask<bool> google_captcha_request_challenge(const std::string &api_key, const std::string &referer, RequestChallengeResponse challenge_response_callback);
using PostSolutionResponse = std::function<void(std::optional<std::string>, std::optional<GoogleCaptchaChallengeInfo>)>;
- std::future<bool> google_captcha_post_solution(const std::string &api_key, const std::string &captcha_id, std::array<bool, 9> selected_images, PostSolutionResponse solution_response_callback);
+ AsyncTask<bool> google_captcha_post_solution(const std::string &api_key, const std::string &captcha_id, std::array<bool, 9> selected_images, PostSolutionResponse solution_response_callback);
} \ No newline at end of file
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index 3e8e7c7..497f202 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -6,6 +6,7 @@
#include "Storage.hpp"
#include "Tab.hpp"
#include "MessageQueue.hpp"
+#include "AsyncTask.hpp"
#include <vector>
#include <memory>
#include <SFML/Graphics/Font.hpp>
@@ -65,8 +66,8 @@ namespace QuickMedia {
bool fetching_next_page_running = false;
int fetched_page = 0;
sf::Text search_result_text;
- std::future<FetchResult> fetch_future;
- std::future<BodyItems> next_page_future;
+ AsyncTask<FetchResult> fetch_future;
+ AsyncTask<BodyItems> next_page_future;
};
class Program {
@@ -152,9 +153,9 @@ namespace QuickMedia {
std::string manga_id_base64;
Json::Value content_storage_json;
std::unordered_set<std::string> watched_videos;
- std::future<BodyItems> search_suggestion_future;
- std::future<std::string> autocomplete_future;
- std::future<void> image_download_future;
+ AsyncTask<BodyItems> search_suggestion_future;
+ AsyncTask<std::string> autocomplete_future;
+ AsyncTask<void> image_download_future;
std::thread image_upscale_thead;
MessageQueue<CopyOp> images_to_upscale_queue;
std::vector<char> image_upscale_status;
diff --git a/src/GoogleCaptcha.cpp b/src/GoogleCaptcha.cpp
index 3bd91eb..137eb24 100644
--- a/src/GoogleCaptcha.cpp
+++ b/src/GoogleCaptcha.cpp
@@ -99,8 +99,8 @@ namespace QuickMedia {
return strip_html_tags(html_source.substr(start_index, end_index - start_index));
}
- std::future<bool> google_captcha_request_challenge(const std::string &api_key, const std::string &referer, RequestChallengeResponse challenge_response_callback) {
- return std::async(std::launch::async, [challenge_response_callback, api_key, referer]() {
+ AsyncTask<bool> google_captcha_request_challenge(const std::string &api_key, const std::string &referer, RequestChallengeResponse challenge_response_callback) {
+ return AsyncTask<bool>([challenge_response_callback, api_key, referer]() {
std::string captcha_url = "https://www.google.com/recaptcha/api/fallback?k=" + api_key;
std::string response;
std::vector<CommandArg> additional_args = {
@@ -131,8 +131,8 @@ namespace QuickMedia {
return result;
}
- std::future<bool> google_captcha_post_solution(const std::string &api_key, const std::string &captcha_id, std::array<bool, 9> selected_images, PostSolutionResponse solution_response_callback) {
- return std::async(std::launch::async, [solution_response_callback, api_key, captcha_id, selected_images]() {
+ AsyncTask<bool> google_captcha_post_solution(const std::string &api_key, const std::string &captcha_id, std::array<bool, 9> selected_images, PostSolutionResponse solution_response_callback) {
+ return AsyncTask<bool>([solution_response_callback, api_key, captcha_id, selected_images]() {
std::string captcha_url = "https://www.google.com/recaptcha/api/fallback?k=" + api_key;
std::string response;
std::vector<CommandArg> additional_args = {
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 5281325..7dfd8dc 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -25,7 +25,6 @@
#include "../include/NetUtils.hpp"
#include "../include/SfmlFixes.hpp"
#include "../include/ResourceLoader.hpp"
-#include "../include/AsyncTask.hpp"
#include "../include/Utils.hpp"
#include "../external/hash-library/sha256.h"
@@ -259,11 +258,6 @@ namespace QuickMedia {
Page *search_page;
};
- template <typename T>
- static bool is_future_ready(const std::future<T> &future) {
- return future.valid() && future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
- }
-
static Path get_recommended_filepath(const char *plugin_name) {
Path video_history_dir = get_storage_dir().join("recommended");
if(create_directory_recursive(video_history_dir) != 0) {
@@ -1155,6 +1149,7 @@ namespace QuickMedia {
chapters_body->filter_search_fuzzy(""); // Needed (or not really) to go to the next chapter when reaching the last page of a chapter
MangaImagesPage *manga_images_page = static_cast<MangaImagesPage*>(new_tabs[0].page.get());
window.setKeyRepeatEnabled(false);
+ downloading_chapter_url.clear();
while(true) {
if(current_page == PageType::IMAGES) {
window.setFramerateLimit(20);
@@ -1327,12 +1322,12 @@ namespace QuickMedia {
int next_page = tab_associated_data[selected_tab].fetched_page + 1;
Page *page = tabs[selected_tab].page.get();
std::string update_search_text = tab_associated_data[selected_tab].update_search_text;
- tab_associated_data[selected_tab].next_page_future = std::async(std::launch::async, [update_search_text, next_page, page]() {
+ tab_associated_data[selected_tab].next_page_future = [update_search_text, next_page, page]() {
BodyItems result_items;
if(page->get_page(update_search_text, next_page, result_items) != PluginResult::OK)
fprintf(stderr, "Failed to get next page (page %d)\n", next_page);
return result_items;
- });
+ };
}
} else if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) {
tabs[selected_tab].body->select_previous_item();
@@ -1415,11 +1410,11 @@ namespace QuickMedia {
tab_associated_data[selected_tab].fetch_type = FetchType::LAZY;
tab_associated_data[selected_tab].search_result_text.setString("Fetching page...");
LazyFetchPage *lazy_fetch_page = static_cast<LazyFetchPage*>(tabs[selected_tab].page.get());
- tab_associated_data[selected_tab].fetch_future = std::async(std::launch::async, [lazy_fetch_page]() {
+ tab_associated_data[selected_tab].fetch_future = [lazy_fetch_page]() {
FetchResult fetch_result;
fetch_result.result = lazy_fetch_page->lazy_fetch(fetch_result.body_items);
return fetch_result;
- });
+ };
}
for(size_t i = 0; i < tabs.size(); ++i) {
@@ -1427,7 +1422,7 @@ namespace QuickMedia {
tabs[i].page->update();
- if(associated_data.fetching_next_page_running && is_future_ready(associated_data.next_page_future)) {
+ if(associated_data.fetching_next_page_running && associated_data.next_page_future.ready()) {
BodyItems new_body_items = associated_data.next_page_future.get();
fprintf(stderr, "Finished fetching page %d, num new messages: %zu\n", associated_data.fetched_page + 1, new_body_items.size());
size_t num_new_messages = new_body_items.size();
@@ -1445,14 +1440,14 @@ namespace QuickMedia {
associated_data.fetch_type = FetchType::SEARCH;
associated_data.search_result_text.setString("Searching...");
Page *page = tabs[i].page.get();
- associated_data.fetch_future = std::async(std::launch::async, [update_search_text, page]() {
+ associated_data.fetch_future = [update_search_text, page]() {
FetchResult fetch_result;
fetch_result.result = search_result_to_plugin_result(page->search(update_search_text, fetch_result.body_items));
return fetch_result;
- });
+ };
}
- if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::SEARCH && is_future_ready(associated_data.fetch_future)) {
+ if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::SEARCH && associated_data.fetch_future.ready()) {
if(!associated_data.search_text_updated) {
FetchResult fetch_result = associated_data.fetch_future.get();
tabs[i].body->items = std::move(fetch_result.body_items);
@@ -1470,7 +1465,7 @@ namespace QuickMedia {
associated_data.fetch_status = FetchStatus::NONE;
}
- if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::LAZY && is_future_ready(associated_data.fetch_future)) {
+ if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::LAZY && associated_data.fetch_future.ready()) {
associated_data.lazy_fetch_finished = true;
FetchResult fetch_result = associated_data.fetch_future.get();
tabs[i].body->items = std::move(fetch_result.body_items);
@@ -1496,14 +1491,7 @@ namespace QuickMedia {
}
page_end:
- // TODO: This is needed, because you cant terminate futures without causing an exception to be thrown and its not safe anyways.
- // Need a way to solve this, we dont want to wait for a search to finish when navigating backwards
- for(TabAssociatedData &associated_data : tab_associated_data) {
- if(associated_data.next_page_future.valid())
- associated_data.next_page_future.get();
- if(associated_data.fetch_future.valid())
- associated_data.fetch_future.get();
- }
+ {}
}
static bool youtube_url_extract_id(const std::string &youtube_url, std::string &youtube_video_id) {
@@ -1672,11 +1660,7 @@ namespace QuickMedia {
TaskResult Program::run_task_with_loading_screen(std::function<bool()> callback) {
assert(std::this_thread::get_id() == main_thread_id);
- std::promise<bool> result_promise;
- std::future<bool> future = result_promise.get_future();
- std::thread task_thread([](std::promise<bool> &&promise, std::function<bool()> callback) {
- promise.set_value(callback());
- }, std::move(result_promise), std::move(callback));
+ AsyncTask<bool> task = callback;
window_size.x = window.getSize().x;
window_size.y = window.getSize().y;
@@ -1684,9 +1668,7 @@ namespace QuickMedia {
while(window.isOpen()) {
while(window.pollEvent(event)) {
if(event.type == sf::Event::Closed) {
- program_kill_in_thread(task_thread.get_id());
- task_thread.join();
- future.get();
+ task.cancel();
current_page = PageType::EXIT;
window.close();
return TaskResult::CANCEL;
@@ -1696,18 +1678,16 @@ namespace QuickMedia {
sf::FloatRect visible_area(0, 0, window_size.x, window_size.y);
window.setView(sf::View(visible_area));
} else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
- program_kill_in_thread(task_thread.get_id());
- task_thread.join();
- future.get();
+ task.cancel();
return TaskResult::CANCEL;
}
}
- if(is_future_ready(future)) {
- task_thread.join();
- if(!future.get())
+ if(task.ready()) {
+ if(task.get())
+ return TaskResult::TRUE;
+ else
return TaskResult::FALSE;
- break;
}
window.clear(back_color);
@@ -1765,7 +1745,7 @@ namespace QuickMedia {
return;
}
- TaskResult download_file_result = run_task_with_loading_screen([this, &video_path, video_url]() {
+ TaskResult download_file_result = run_task_with_loading_screen([&video_path, video_url]() {
return download_to_file(video_url, video_path.data, {}, true) == DownloadResult::OK;
});
switch(download_file_result) {
@@ -2209,15 +2189,12 @@ namespace QuickMedia {
return;
downloading_chapter_url = images_page->get_url();
- if(image_download_future.valid()) {
- // TODO: Cancel download instead of waiting for the last page to finish
- image_download_cancel = true;
- image_download_future.get();
- image_download_cancel = false;
- }
+ image_download_cancel = true;
+ image_download_future.cancel();
+ image_download_cancel = false;
Path content_cache_dir_ = content_cache_dir;
- image_download_future = std::async(std::launch::async, [images_page, content_cache_dir_, this]() {
+ image_download_future = [images_page, content_cache_dir_, this]() {
// TODO: Download images in parallel
int page = 1;
images_page->for_each_page_in_chapter([content_cache_dir_, &page, images_page, this](const std::string &url) {
@@ -2326,7 +2303,7 @@ namespace QuickMedia {
return true;
});
- });
+ };
}
int Program::image_page(MangaImagesPage *images_page, Body *chapters_body) {
@@ -2532,11 +2509,8 @@ namespace QuickMedia {
end_of_images_page:
if(current_page != PageType::IMAGES && current_page != PageType::IMAGES_CONTINUOUS) {
image_download_cancel = true;
- if(image_download_future.valid()) {
- // TODO: Cancel download instead of waiting for the last page to finish
- image_download_future.get();
- image_download_cancel = false;
- }
+ image_download_future.cancel();
+ image_download_cancel = false;
images_to_upscale_queue.clear();
image_upscale_status.clear();
}
@@ -2623,11 +2597,8 @@ namespace QuickMedia {
if(current_page != PageType::IMAGES && current_page != PageType::IMAGES_CONTINUOUS) {
image_download_cancel = true;
- if(image_download_future.valid()) {
- // TODO: Cancel download instead of waiting for the last page to finish
- image_download_future.get();
- image_download_cancel = false;
- }
+ image_download_future.cancel();
+ image_download_cancel = false;
images_to_upscale_queue.clear();
image_upscale_status.clear();
}
@@ -2655,10 +2626,10 @@ namespace QuickMedia {
};
NavigationStage navigation_stage = NavigationStage::VIEWING_COMMENTS;
- std::future<bool> captcha_request_future;
- std::future<bool> captcha_post_solution_future;
- std::future<bool> post_comment_future;
- std::future<std::string> load_image_future;
+ AsyncTask<bool> captcha_request_future;
+ AsyncTask<bool> captcha_post_solution_future;
+ AsyncTask<bool> post_comment_future;
+ AsyncTask<std::string> load_image_future;
bool downloading_image = false;
sf::Texture captcha_texture;
sf::Sprite captcha_sprite;
@@ -2692,7 +2663,7 @@ namespace QuickMedia {
// TODO: Make google captcha images load texture in the main thread, otherwise high cpu usage. I guess its fine right now because of only 1 image?
// TODO: Make this work with other sites than 4chan
- auto request_google_captcha_image = [this, &captcha_texture, &captcha_image_mutex, &navigation_stage, &captcha_sprite, &challenge_description_text](GoogleCaptchaChallengeInfo &challenge_info) {
+ auto request_google_captcha_image = [&captcha_texture, &captcha_image_mutex, &navigation_stage, &captcha_sprite, &challenge_description_text](GoogleCaptchaChallengeInfo &challenge_info) {
std::string payload_image_data;
DownloadResult download_image_result = download_to_string(challenge_info.payload_url, payload_image_data, {});
if(download_image_result == DownloadResult::OK) {
@@ -2712,7 +2683,7 @@ namespace QuickMedia {
}
};
- auto request_new_google_captcha_challenge = [this, &selected_captcha_images, &navigation_stage, &captcha_request_future, &request_google_captcha_image, &challenge_info]() {
+ auto request_new_google_captcha_challenge = [&selected_captcha_images, &navigation_stage, &captcha_request_future, &request_google_captcha_image, &challenge_info]() {
fprintf(stderr, "Solving captcha!\n");
navigation_stage = NavigationStage::SOLVING_POST_CAPTCHA;
for(size_t i = 0; i < selected_captcha_images.size(); ++i) {
@@ -2776,17 +2747,17 @@ namespace QuickMedia {
comment_to_post = std::move(text);
if(!captcha_post_id.empty() && captcha_solved_time.getElapsedTime().asSeconds() < 120) {
- post_comment_future = std::async(std::launch::async, [&post_comment]() -> bool {
+ post_comment_future = [&post_comment]() -> bool {
post_comment();
return true;
- });
+ };
} else if(thread_page->get_pass_id().empty()) {
request_new_google_captcha_challenge();
} else if(!thread_page->get_pass_id().empty()) {
- post_comment_future = std::async(std::launch::async, [&post_comment]() -> bool {
+ post_comment_future = [&post_comment]() -> bool {
post_comment();
return true;
- });
+ };
}
return true;
};
@@ -2872,11 +2843,10 @@ namespace QuickMedia {
video_content_page(thread_page, thread_page, "", true, next_items, 0);
redraw = true;
} else {
- if(downloading_image && load_image_future.valid())
- load_image_future.get();
+ load_image_future.cancel();
downloading_image = true;
navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE;
- load_image_future = std::async(std::launch::async, [this, &thread_body]() {
+ load_image_future = [&thread_body]() {
std::string image_data;
BodyItem *selected_item = thread_body->get_selected();
if(!selected_item || selected_item->attached_content_url.empty()) {
@@ -2887,7 +2857,7 @@ namespace QuickMedia {
image_data.clear();
}
return image_data;
- });
+ };
}
}
}
@@ -3071,7 +3041,7 @@ namespace QuickMedia {
} else if(navigation_stage == NavigationStage::VIEWING_ATTACHED_IMAGE) {
// TODO: Use image instead of data with string. texture->loadFromMemory creates a temporary image anyways that parses the string.
std::string image_data;
- if(downloading_image && is_future_ready(load_image_future)) {
+ if(downloading_image && load_image_future.ready()) {
downloading_image = false;
image_data = load_image_future.get();
@@ -3124,16 +3094,6 @@ namespace QuickMedia {
}
window.display();
}
-
- // TODO: Instead of waiting for them, kill them somehow
- if(captcha_request_future.valid())
- captcha_request_future.get();
- if(captcha_post_solution_future.valid())
- captcha_post_solution_future.get();
- if(post_comment_future.valid())
- post_comment_future.get();
- if(load_image_future.valid())
- load_image_future.get();
}
void Program::chat_login_page() {
@@ -3232,7 +3192,7 @@ namespace QuickMedia {
struct ChatTab {
std::unique_ptr<Body> body;
- std::future<BodyItems> future;
+ AsyncTask<BodyItems> future;
sf::Text text;
};