aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-04-14 19:52:21 +0200
committerdec05eba <dec05eba@protonmail.com>2021-04-14 23:45:11 +0200
commitab36fbffef977b99cc03d0b77ac37bdc1b5705dc (patch)
tree17207a364eecee330a26ac117a1384ad0b4a1e97
parent4db0876f45533d3b55ee79df2d2bc78b789b5a28 (diff)
Rework manga plugins downloading.. preparing for parallel downloads
-rw-r--r--include/AsyncTask.hpp22
-rw-r--r--include/ImageViewer.hpp4
-rw-r--r--include/QuickMedia.hpp4
-rw-r--r--plugins/Manga.hpp9
-rw-r--r--plugins/Mangadex.hpp7
-rw-r--r--plugins/Manganelo.hpp8
-rw-r--r--plugins/Manganelos.hpp7
-rw-r--r--plugins/Mangatown.hpp7
-rw-r--r--src/DownloadUtils.cpp6
-rw-r--r--src/ImageViewer.cpp10
-rw-r--r--src/Program.cpp2
-rw-r--r--src/QuickMedia.cpp218
-rw-r--r--src/SearchBar.cpp1
-rw-r--r--src/plugins/Mangadex.cpp48
-rw-r--r--src/plugins/Manganelo.cpp41
-rw-r--r--src/plugins/Manganelos.cpp41
-rw-r--r--src/plugins/Mangatown.cpp104
17 files changed, 207 insertions, 332 deletions
diff --git a/include/AsyncTask.hpp b/include/AsyncTask.hpp
index abdc0f8..6148923 100644
--- a/include/AsyncTask.hpp
+++ b/include/AsyncTask.hpp
@@ -5,25 +5,17 @@
#include <future>
namespace QuickMedia {
- template <typename T>
+ template <class T, class... Args>
class AsyncTask {
public:
- using CallbackFunc = std::function<T()>;
+ using CallbackFunc = std::function<T(Args&&... args)>;
AsyncTask() = default;
- AsyncTask(CallbackFunc callback_func) {
+ AsyncTask(CallbackFunc callback_func, Args&&... args) {
std::promise<T> promise;
future = promise.get_future();
- thread = std::thread(&AsyncTask::thread_handler, this, std::move(promise), std::move(callback_func));
- }
-
- AsyncTask& operator=(CallbackFunc callback_func) {
- cancel();
- std::promise<T> promise;
- future = promise.get_future();
- thread = std::thread(&AsyncTask::thread_handler, this, std::move(promise), std::move(callback_func));
- return *this;
+ thread = std::thread(&AsyncTask::thread_handler, this, std::move(promise), std::move(callback_func), std::forward<Args>(args)...);
}
AsyncTask(AsyncTask &&other) {
@@ -63,12 +55,12 @@ namespace QuickMedia {
}
}
private:
- void thread_handler(std::promise<T> &&promise, CallbackFunc callback_func) {
+ void thread_handler(std::promise<T> &&promise, CallbackFunc callback_func, Args&&... args) {
if constexpr(std::is_same<T, void>::value) {
- callback_func();
+ callback_func(std::forward<Args>(args)...);
promise.set_value();
} else {
- promise.set_value(callback_func());
+ promise.set_value(callback_func(std::forward<Args>(args)...));
}
}
private:
diff --git a/include/ImageViewer.hpp b/include/ImageViewer.hpp
index cd1bc8f..c29ea13 100644
--- a/include/ImageViewer.hpp
+++ b/include/ImageViewer.hpp
@@ -15,8 +15,6 @@ namespace sf {
}
namespace QuickMedia {
- class MangaImagesPage;
-
enum class ImageStatus {
WAITING,
LOADING,
@@ -46,7 +44,7 @@ namespace QuickMedia {
class ImageViewer {
public:
- ImageViewer(sf::RenderWindow *window, MangaImagesPage *manga_images_page, const std::string &content_title, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir);
+ ImageViewer(sf::RenderWindow *window, int num_pages, const std::string &content_title, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir);
~ImageViewer();
ImageViewerAction draw();
// Returns page as 1 indexed
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index 2877e0c..b387968 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -160,12 +160,14 @@ namespace QuickMedia {
std::unordered_set<std::string> watched_videos;
AsyncTask<BodyItems> search_suggestion_future;
AsyncTask<std::string> autocomplete_future;
- AsyncTask<void> image_download_future;
+ AsyncTask<void, std::promise<int>> image_download_future;
std::thread image_upscale_thead;
MessageQueue<CopyOp> images_to_upscale_queue;
std::vector<char> image_upscale_status;
std::string downloading_chapter_url;
bool image_download_cancel = false;
+ std::future<int> num_manga_pages_future;
+ int num_manga_pages = 0;
int exit_code = 0;
std::string resources_root;
sf::Shader circle_mask_shader;
diff --git a/plugins/Manga.hpp b/plugins/Manga.hpp
index 23e8dd0..60e739b 100644
--- a/plugins/Manga.hpp
+++ b/plugins/Manga.hpp
@@ -19,15 +19,11 @@ namespace QuickMedia {
const char* get_title() const override { return chapter_name.c_str(); }
PageTypez get_type() const override { return PageTypez::MANGA_IMAGES; }
- virtual ImageResult get_number_of_images(int &num_images) = 0;
- virtual ImageResult for_each_page_in_chapter(PageCallback callback) = 0;
+ virtual ImageResult get_page_image_urls(std::vector<std::string> &urls) = 0;
virtual void change_chapter(std::string new_chapter_name, std::string new_url) {
chapter_name = std::move(new_chapter_name);
- if(url != new_url) {
- url = std::move(new_url);
- chapter_image_urls.clear();
- }
+ url = std::move(new_url);
}
const std::string& get_chapter_name() const { return chapter_name; }
@@ -39,7 +35,6 @@ namespace QuickMedia {
protected:
std::string chapter_name;
std::string url;
- std::vector<std::string> chapter_image_urls;
};
class MangaChaptersPage : public TrackablePage {
diff --git a/plugins/Mangadex.hpp b/plugins/Mangadex.hpp
index 00da59c..e4a64d8 100644
--- a/plugins/Mangadex.hpp
+++ b/plugins/Mangadex.hpp
@@ -31,14 +31,9 @@ namespace QuickMedia {
class MangadexImagesPage : public MangaImagesPage {
public:
MangadexImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url) : MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(url)) {}
-
- ImageResult get_number_of_images(int &num_images) override;
- ImageResult for_each_page_in_chapter(PageCallback callback) override;
-
+ ImageResult get_page_image_urls(std::vector<std::string> &urls) override;
const char* get_service_name() const override { return "mangadex"; }
private:
- // Cached
- ImageResult get_image_urls_for_chapter(const std::string &url);
bool save_mangadex_cookies(const std::string &url, const std::string &cookie_filepath);
};
} \ No newline at end of file
diff --git a/plugins/Manganelo.hpp b/plugins/Manganelo.hpp
index e2b602f..4a7bfc3 100644
--- a/plugins/Manganelo.hpp
+++ b/plugins/Manganelo.hpp
@@ -37,13 +37,7 @@ namespace QuickMedia {
class ManganeloImagesPage : public MangaImagesPage {
public:
ManganeloImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url) : MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(url)) {}
-
- ImageResult get_number_of_images(int &num_images) override;
- ImageResult for_each_page_in_chapter(PageCallback callback) override;
-
+ ImageResult get_page_image_urls(std::vector<std::string> &urls) override;
const char* get_service_name() const override { return "manganelo"; }
- private:
- // Cached
- ImageResult get_image_urls_for_chapter(const std::string &url);
};
} \ No newline at end of file
diff --git a/plugins/Manganelos.hpp b/plugins/Manganelos.hpp
index ede2712..4df0c3f 100644
--- a/plugins/Manganelos.hpp
+++ b/plugins/Manganelos.hpp
@@ -27,12 +27,7 @@ namespace QuickMedia {
class ManganelosImagesPage : public MangaImagesPage {
public:
ManganelosImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url) : MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(url)) {}
-
- ImageResult get_number_of_images(int &num_images) override;
- ImageResult for_each_page_in_chapter(PageCallback callback) override;
-
+ ImageResult get_page_image_urls(std::vector<std::string> &urls) override;
const char* get_service_name() const override { return "manganelos"; }
- private:
- ImageResult get_image_urls_for_chapter(const std::string &url);
};
} \ No newline at end of file
diff --git a/plugins/Mangatown.hpp b/plugins/Mangatown.hpp
index 66d9d2a..0b6c1c4 100644
--- a/plugins/Mangatown.hpp
+++ b/plugins/Mangatown.hpp
@@ -27,12 +27,7 @@ namespace QuickMedia {
class MangatownImagesPage : public MangaImagesPage {
public:
MangatownImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url) : MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(url)) {}
-
- ImageResult get_number_of_images(int &num_images) override;
- ImageResult for_each_page_in_chapter(PageCallback callback) override;
-
+ ImageResult get_page_image_urls(std::vector<std::string> &urls) override;
const char* get_service_name() const override { return "mangatown"; }
- private:
- ImageResult get_image_urls_for_chapter(const std::string &url);
};
} \ No newline at end of file
diff --git a/src/DownloadUtils.cpp b/src/DownloadUtils.cpp
index 4a35640..336c154 100644
--- a/src/DownloadUtils.cpp
+++ b/src/DownloadUtils.cpp
@@ -142,6 +142,12 @@ namespace QuickMedia {
return DownloadResult::NET_ERR;
FILE *file = fdopen(read_program.read_fd, "rb");
+ if(!file) {
+ program_clear_current_thread();
+ wait_program(read_program.pid);
+ return DownloadResult::ERR;
+ }
+
char read_buffer[8192];
rapidjson::FileReadStream is(file, read_buffer, sizeof(read_buffer));
rapidjson::ParseResult parse_result = result.ParseStream(is);
diff --git a/src/ImageViewer.cpp b/src/ImageViewer.cpp
index 85db360..271d69f 100644
--- a/src/ImageViewer.cpp
+++ b/src/ImageViewer.cpp
@@ -3,7 +3,6 @@
#include "../include/Storage.hpp"
#include "../include/SfmlFixes.hpp"
#include "../include/ResourceLoader.hpp"
-#include "../plugins/Manga.hpp"
#include <cmath>
#include <malloc.h>
#include <SFML/Window/Event.hpp>
@@ -11,10 +10,10 @@
#include <SFML/Graphics/RectangleShape.hpp>
namespace QuickMedia {
- ImageViewer::ImageViewer(sf::RenderWindow *window, MangaImagesPage *manga_images_page, const std::string &content_title, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir) :
+ ImageViewer::ImageViewer(sf::RenderWindow *window, int num_pages, const std::string &content_title, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir) :
window(window),
current_page(current_page),
- num_pages(0),
+ num_pages(num_pages),
content_title(content_title),
chapter_title(chapter_title),
chapter_cache_dir(chapter_cache_dir),
@@ -22,10 +21,6 @@ namespace QuickMedia {
font(FontLoader::get_font(FontLoader::FontType::LATIN)),
page_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 14)
{
- if(manga_images_page->get_number_of_images(num_pages) != ImageResult::OK) {
- show_notification("QuickMedia", "Failed to get number of images", Urgency::CRITICAL);
- return;
- }
current_page = std::min(current_page, num_pages);
image_data.resize(num_pages);
page_size.resize(num_pages);
@@ -148,6 +143,7 @@ namespace QuickMedia {
Path image_path = chapter_cache_dir;
image_path.join(page_str);
+ // TODO: Add delay before checking again?
if(get_file_type(image_path) == FileType::REGULAR) {
fprintf(stderr, "ImageViewer: Loaded page %d\n", 1 + page);
diff --git a/src/Program.cpp b/src/Program.cpp
index dc92f16..feb543f 100644
--- a/src/Program.cpp
+++ b/src/Program.cpp
@@ -153,7 +153,6 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void
break;
}
}
- program_clear_current_thread();
if(result != 0)
kill(read_program.pid, SIGTERM);
@@ -184,6 +183,7 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void
}
cleanup:
+ program_clear_current_thread();
close(read_program.read_fd);
return result;
}
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 053c97f..5bdc67e 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -1265,7 +1265,8 @@ namespace QuickMedia {
MangaImagesPage *manga_images_page = static_cast<MangaImagesPage*>(new_tabs[0].page.get());
window.setKeyRepeatEnabled(false);
downloading_chapter_url.clear();
- while(true) {
+
+ while(window.isOpen() && (current_page == PageType::IMAGES || current_page == PageType::IMAGES_CONTINUOUS)) {
if(current_page == PageType::IMAGES) {
window.setFramerateLimit(20);
while(current_page == PageType::IMAGES) {
@@ -1287,10 +1288,14 @@ namespace QuickMedia {
idle = true;
} else if(current_page == PageType::IMAGES_CONTINUOUS) {
image_continuous_page(manga_images_page);
- } else {
- break;
}
}
+
+ image_download_cancel = true;
+ image_download_future.cancel();
+ image_download_cancel = false;
+ images_to_upscale_queue.clear();
+ num_manga_pages = 0;
window.setKeyRepeatEnabled(true);
malloc_trim(0);
} else if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::IMAGE_BOARD_THREAD) {
@@ -1436,12 +1441,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 = [update_search_text, next_page, page]() {
+ tab_associated_data[selected_tab].next_page_future = AsyncTask<BodyItems>([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();
@@ -1528,11 +1533,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 = [lazy_fetch_page]() {
+ tab_associated_data[selected_tab].fetch_future = AsyncTask<FetchResult>([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) {
@@ -1558,11 +1563,11 @@ 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 = [update_search_text, page]() {
+ associated_data.fetch_future = AsyncTask<FetchResult>([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 && associated_data.fetch_future.ready()) {
@@ -2276,7 +2281,7 @@ namespace QuickMedia {
if(upscale_image_action != UpscaleImageAction::NO) {
Path image_filepath_upcaled = image_path;
image_filepath_upcaled.append(".upscaled");
- if(get_file_type(image_filepath_upcaled) == FileType::FILE_NOT_FOUND && image_upscale_status[image_index] == 0)
+ if(get_file_type(image_filepath_upcaled) == FileType::FILE_NOT_FOUND && image_index < (int)image_upscale_status.size() && image_upscale_status[image_index] == 0)
upscaled_ok = false;
}
@@ -2311,13 +2316,29 @@ namespace QuickMedia {
image_download_future.cancel();
image_download_cancel = false;
+ std::promise<int> num_manga_pages_promise;
+ num_manga_pages_future = num_manga_pages_promise.get_future();
+
Path content_cache_dir_ = content_cache_dir;
- image_download_future = [images_page, content_cache_dir_, this]() {
+ image_download_future = AsyncTask<void, std::promise<int>>([images_page, content_cache_dir_, this](std::promise<int> num_manga_pages_promise) {
+ std::vector<std::string> page_image_urls;
+ if(images_page->get_page_image_urls(page_image_urls) != ImageResult::OK) {
+ num_manga_pages_promise.set_value(0);
+ if(!image_download_cancel) show_notification("QuickMedia", "Failed to fetch page images", Urgency::CRITICAL);
+ return;
+ } else {
+ num_manga_pages_promise.set_value(page_image_urls.size());
+ image_upscale_status.resize(page_image_urls.size(), 0);
+ }
+
+ if(page_image_urls.empty())
+ return;
+
// 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) {
+ for(const std::string &url : page_image_urls) {
if(image_download_cancel)
- return false;
+ return;
int image_index = page - 1;
@@ -2334,7 +2355,7 @@ namespace QuickMedia {
}
if(get_file_type(image_filepath) != FileType::FILE_NOT_FOUND && upscaled_ok)
- return true;
+ continue;
std::vector<CommandArg> extra_args;
const bool is_manganelo = (strcmp(images_page->get_service_name(), "manganelo") == 0);
@@ -2355,7 +2376,7 @@ namespace QuickMedia {
size_t file_size = 0;
if(download_to_file(url, image_filepath_tmp.data, extra_args, true) != DownloadResult::OK || (is_manganelo && file_get_size(image_filepath_tmp, &file_size) == 0 && file_size < 255)) {
if(!image_download_cancel) show_notification("QuickMedia", "Failed to download image: " + url, Urgency::CRITICAL);
- return true;
+ continue;
}
bool rename_immediately = true;
@@ -2391,26 +2412,45 @@ namespace QuickMedia {
if(rename(image_filepath_tmp.data.c_str(), image_filepath.data.c_str()) != 0) {
perror(image_filepath_tmp.data.c_str());
show_notification("QuickMedia", "Failed to save image to file: " + image_filepath.data, Urgency::CRITICAL);
- return true;
+ continue;
}
}
+ }
+ }, std::move(num_manga_pages_promise));
- return true;
- });
- };
+ sf::Event event;
+ PageType current_manga_page = current_page;
+
+ while (current_page == current_manga_page && window.isOpen()) {
+ while(window.pollEvent(event)) {
+ if(event.type == sf::Event::Resized) {
+ window_size.x = event.size.width;
+ window_size.y = event.size.height;
+ 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) {
+ current_page = pop_page_stack();
+ }
+ }
+ handle_window_close();
+
+ if(num_manga_pages_future.valid() && num_manga_pages_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
+ num_manga_pages = num_manga_pages_future.get();
+ break;
+ }
+
+ window.clear(back_color);
+ load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f);
+ load_sprite.setRotation(load_sprite_timer.getElapsedTime().asSeconds() * 400.0);
+ window.draw(load_sprite);
+ window.display();
+ }
}
int Program::image_page(MangaImagesPage *images_page, Body *chapters_body) {
int page_navigation = 0;
image_download_cancel = false;
- sf::Texture image_texture;
- sf::Sprite image;
- sf::Text error_message("", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(30 * get_ui_scale()));
- error_message.setFillColor(sf::Color::White);
-
- bool download_in_progress = false;
-
content_cache_dir = get_cache_dir().join(images_page->get_service_name()).join(manga_id_base64).join(base64_encode(images_page->get_chapter_name()));
if(create_directory_recursive(content_cache_dir) != 0) {
show_notification("QuickMedia", "Failed to create directory: " + content_cache_dir.data, Urgency::CRITICAL);
@@ -2418,20 +2458,21 @@ namespace QuickMedia {
return 0;
}
- int num_images = 0;
- if(images_page->get_number_of_images(num_images) != ImageResult::OK) {
- show_notification("QuickMedia", "Failed to get number of images", Urgency::CRITICAL);
- current_page = pop_page_stack();
- return 0;
- }
- image_index = std::min(image_index, num_images);
-
- if(num_images != (int)image_upscale_status.size())
- image_upscale_status.resize(num_images);
+ sf::Texture image_texture;
+ sf::Sprite image;
+ sf::Text error_message("", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(30 * get_ui_scale()));
+ error_message.setFillColor(sf::Color::White);
+
+ bool download_in_progress = false;
+ sf::Event event;
download_chapter_images_if_needed(images_page);
+ if(current_page != PageType::IMAGES || !window.isOpen())
+ return 0;
+
+ image_index = std::min(image_index, num_manga_pages);
- if(image_index < num_images) {
+ if(image_index < num_manga_pages) {
sf::String error_msg;
LoadImageResult load_image_result = load_image_by_index(image_index, image_texture, error_msg);
if(load_image_result == LoadImageResult::OK)
@@ -2439,7 +2480,7 @@ namespace QuickMedia {
else if(load_image_result == LoadImageResult::DOWNLOAD_IN_PROGRESS)
download_in_progress = true;
error_message.setString(error_msg);
- } else if(image_index == num_images) {
+ } else if(image_index == num_manga_pages) {
error_message.setString("End of " + images_page->get_chapter_name());
}
@@ -2460,8 +2501,8 @@ namespace QuickMedia {
json_chapters = Json::Value(Json::objectValue);
json_chapter = Json::Value(Json::objectValue);
}
- json_chapter["current"] = std::min(latest_read, num_images);
- json_chapter["total"] = num_images;
+ json_chapter["current"] = std::min(latest_read, num_manga_pages);
+ json_chapter["total"] = num_manga_pages;
json_chapters[images_page->get_chapter_name()] = json_chapter;
if(!save_json_to_file_atomic(content_storage_file, content_storage_json)) {
show_notification("QuickMedia", "Failed to save manga progress", Urgency::CRITICAL);
@@ -2470,8 +2511,8 @@ namespace QuickMedia {
bool error = !error_message.getString().isEmpty();
bool redraw = true;
- sf::Text chapter_text(images_page->manga_name + " | " + images_page->get_chapter_name() + " | Page " + std::to_string(image_index + 1) + "/" + std::to_string(num_images), *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14 * get_ui_scale()));
- if(image_index == num_images)
+ sf::Text chapter_text(images_page->manga_name + " | " + images_page->get_chapter_name() + " | Page " + std::to_string(image_index + 1) + "/" + std::to_string(num_manga_pages), *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14 * get_ui_scale()));
+ if(image_index == num_manga_pages)
chapter_text.setString(images_page->manga_name + " | " + images_page->get_chapter_name() + " | End");
chapter_text.setFillColor(sf::Color::White);
sf::RectangleShape chapter_text_background;
@@ -2487,10 +2528,6 @@ namespace QuickMedia {
sf::Clock check_downloaded_timer;
const sf::Int32 check_downloaded_timeout_ms = 500;
- sf::Event event;
- // Consume events sent during above call to get_number_of_images which sends a request to server which may take a while. We dont want pages to be skipped when pressing arrow up/down
- while(window.pollEvent(event)) {}
-
malloc_trim(0);
// TODO: Show to user if a certain page is missing (by checking page name (number) and checking if some are skipped)
@@ -2514,10 +2551,10 @@ namespace QuickMedia {
goto end_of_images_page;
}
} else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) {
- if(image_index < num_images) {
+ if(image_index < num_manga_pages) {
++image_index;
goto end_of_images_page;
- } else if(image_index == num_images && chapters_body->get_selected_item() > 0) {
+ } else if(image_index == num_manga_pages && chapters_body->get_selected_item() > 0) {
page_navigation = 1;
goto end_of_images_page;
}
@@ -2601,13 +2638,6 @@ namespace QuickMedia {
}
end_of_images_page:
- if(current_page != PageType::IMAGES && current_page != PageType::IMAGES_CONTINUOUS) {
- image_download_cancel = true;
- image_download_future.cancel();
- image_download_cancel = false;
- images_to_upscale_queue.clear();
- image_upscale_status.clear();
- }
return page_navigation;
}
@@ -2621,17 +2651,9 @@ namespace QuickMedia {
return;
}
- int num_images = 0;
- if(images_page->get_number_of_images(num_images) != ImageResult::OK) {
- show_notification("QuickMedia", "Failed to get number of images", Urgency::CRITICAL);
- current_page = pop_page_stack();
- return;
- }
-
- if(num_images != (int)image_upscale_status.size())
- image_upscale_status.resize(num_images);
-
download_chapter_images_if_needed(images_page);
+ if(current_page != PageType::IMAGES_CONTINUOUS || !window.isOpen())
+ return;
Json::Value &json_chapters = content_storage_json["chapters"];
Json::Value json_chapter;
@@ -2650,7 +2672,7 @@ namespace QuickMedia {
json_chapter = Json::Value(Json::objectValue);
}
- ImageViewer image_viewer(&window, images_page, images_page->manga_name, images_page->get_chapter_name(), image_index, content_cache_dir);
+ ImageViewer image_viewer(&window, num_manga_pages, images_page->manga_name, images_page->get_chapter_name(), image_index, content_cache_dir);
json_chapter["current"] = std::min(latest_read, image_viewer.get_num_pages());
json_chapter["total"] = image_viewer.get_num_pages();
@@ -2690,14 +2712,6 @@ namespace QuickMedia {
}
}
- if(current_page != PageType::IMAGES && current_page != PageType::IMAGES_CONTINUOUS) {
- image_download_cancel = true;
- image_download_future.cancel();
- image_download_cancel = false;
- images_to_upscale_queue.clear();
- image_upscale_status.clear();
- }
-
window_size.x = window.getSize().x;
window_size.y = window.getSize().y;
@@ -2844,17 +2858,17 @@ namespace QuickMedia {
comment_to_post = std::move(text);
if(!captcha_post_id.empty() && captcha_solved_time.getElapsedTime().asSeconds() < 120) {
- post_comment_future = [&post_comment]() -> bool {
+ post_comment_future = AsyncTask<bool>([&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 = [&post_comment]() -> bool {
+ post_comment_future = AsyncTask<bool>([&post_comment]() -> bool {
post_comment();
return true;
- };
+ });
}
return true;
};
@@ -2940,7 +2954,7 @@ namespace QuickMedia {
load_image_future.cancel();
downloading_image = true;
navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE;
- load_image_future = [&thread_body]() {
+ load_image_future = AsyncTask<std::string>([&thread_body]() {
std::string image_data;
BodyItem *selected_item = thread_body->get_selected();
if(!selected_item || selected_item->attached_content_url.empty()) {
@@ -2951,7 +2965,7 @@ namespace QuickMedia {
image_data.clear();
}
return image_data;
- };
+ });
}
}
}
@@ -4025,9 +4039,9 @@ namespace QuickMedia {
body_item->embedded_item_status = FetchStatus::LOADING;
fetch_message_tab = MESSAGES_TAB_INDEX;
// TODO: Check if the message is already cached before calling async? is this needed? is async creation expensive?
- fetch_message_future = [this, &current_room, message_event_id]() {
+ fetch_message_future = AsyncTask<FetchMessageResult>([this, &current_room, message_event_id]() {
return FetchMessageResult{FetchMessageType::MESSAGE, matrix->get_message_by_id(current_room, message_event_id)};
- };
+ });
return;
}
@@ -4056,9 +4070,9 @@ namespace QuickMedia {
event_data->status = FetchStatus::LOADING;
fetch_message_tab = PINNED_TAB_INDEX;
// TODO: Check if the message is already cached before calling async? is this needed? is async creation expensive?
- fetch_message_future = [this, &current_room, message_event_id]() {
+ fetch_message_future = AsyncTask<FetchMessageResult>([this, &current_room, message_event_id]() {
return FetchMessageResult{FetchMessageType::MESSAGE, matrix->get_message_by_id(current_room, message_event_id)};
- };
+ });
};
// TODO: How about instead fetching all messages we have, not only the visible ones? also fetch with multiple threads.
@@ -4080,11 +4094,11 @@ namespace QuickMedia {
RoomData *room = current_room;
std::string event_id = read_message->event_id;
int64_t event_timestamp = read_message->timestamp;
- set_read_marker_future = [this, room, event_id, event_timestamp]() mutable {
+ set_read_marker_future = AsyncTask<void>([this, room, event_id, event_timestamp]() mutable {
if(matrix->set_read_marker(room, event_id, event_timestamp) != PluginResult::OK) {
fprintf(stderr, "Warning: failed to set read marker to %s\n", event_id.c_str());
}
- };
+ });
}
}
@@ -4093,10 +4107,10 @@ namespace QuickMedia {
fetch_message = message;
message->user->resolve_state = UserResolveState::RESOLVING;
std::string user_id = message->user->user_id;
- fetch_message_future = [this, &current_room, user_id]() {
+ fetch_message_future = AsyncTask<FetchMessageResult>([this, &current_room, user_id]() {
matrix->update_user_with_latest_state(current_room, user_id);
return FetchMessageResult{FetchMessageType::USER_UPDATE, nullptr};
- };
+ });
return;
} else if(message->user->resolve_state == UserResolveState::RESOLVING) {
return;
@@ -4119,9 +4133,9 @@ namespace QuickMedia {
body_item->embedded_item_status = FetchStatus::LOADING;
fetch_message_tab = MESSAGES_TAB_INDEX;
// TODO: Check if the message is already cached before calling async? is this needed? is async creation expensive?
- fetch_message_future = [this, &current_room, message_event_id]() {
+ fetch_message_future = AsyncTask<FetchMessageResult>([this, &current_room, message_event_id]() {
return FetchMessageResult{FetchMessageType::MESSAGE, matrix->get_message_by_id(current_room, message_event_id)};
- };
+ });
};
tabs[MESSAGES_TAB_INDEX].body->body_item_merge_handler = [](BodyItem *prev_item, BodyItem *this_item) {
@@ -4164,23 +4178,23 @@ namespace QuickMedia {
auto fetch_more_previous_messages_if_needed = [this, &tabs, &current_room, &fetched_enough_messages, &previous_messages_future]() {
if(!fetched_enough_messages && !previous_messages_future.valid()) {
if(tabs[MESSAGES_TAB_INDEX].body->items.size() < 30) {
- previous_messages_future = [this, &current_room]() {
+ previous_messages_future = AsyncTask<Messages>([this, &current_room]() {
Messages messages;
if(matrix->get_previous_room_messages(current_room, messages) != PluginResult::OK)
fprintf(stderr, "Failed to get previous matrix messages in room: %s\n", current_room->id.c_str());
return messages;
- };
+ });
}
}
};
if(!matrix->is_initial_sync_finished()) {
- previous_messages_future = [this, &current_room]() {
+ previous_messages_future = AsyncTask<Messages>([this, &current_room]() {
Messages messages;
if(matrix->get_previous_room_messages(current_room, messages, true) != PluginResult::OK)
fprintf(stderr, "Failed to get previous matrix messages in room: %s\n", current_room->id.c_str());
return messages;
- };
+ });
}
sf::RectangleShape more_messages_below_rect;
@@ -4428,10 +4442,10 @@ namespace QuickMedia {
//update_
} else {
// TODO: Race condition? maybe use matrix /members instead which has a since parameter to make the members list match current sync
- fetch_users_future = [this, &current_room]() {
+ fetch_users_future = AsyncTask<bool>([this, &current_room]() {
matrix->update_room_users(current_room);
return true;
- };
+ });
}
float tab_shade_height = 0.0f;
@@ -4531,12 +4545,12 @@ namespace QuickMedia {
}
if(hit_top && !previous_messages_future.valid() && selected_tab == MESSAGES_TAB_INDEX && current_room) {
gradient_inc = 0;
- previous_messages_future = [this, &current_room]() {
+ previous_messages_future = AsyncTask<Messages>([this, &current_room]() {
Messages messages;
if(matrix->get_previous_room_messages(current_room, messages) != PluginResult::OK)
fprintf(stderr, "Failed to get previous matrix messages in room: %s\n", current_room->id.c_str());
return messages;
- };
+ });
}
} else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) {
tabs[selected_tab].body.get()->select_next_item();
@@ -5057,7 +5071,9 @@ namespace QuickMedia {
const float margin = 5.0f;
const float replying_to_text_height = replying_to_text.getLocalBounds().height + margin;
- const float item_height = std::min(body_size.y - replying_to_text_height - margin, tabs[MESSAGES_TAB_INDEX].body->get_item_height(currently_operating_on_item.get(), body_size.x) + margin);
+ float item_height = std::min(body_size.y - replying_to_text_height - margin, tabs[MESSAGES_TAB_INDEX].body->get_item_height(currently_operating_on_item.get(), body_size.x) + margin);
+ if(item_height < 0.0f)
+ item_height = 0.0f;
sf::RectangleShape overlay(sf::Vector2f(window_size.x, window_size.y - chat_input_height_full));
overlay.setFillColor(sf::Color(0, 0, 0, 240));
@@ -5066,8 +5082,8 @@ namespace QuickMedia {
sf::Vector2f body_item_pos(body_pos.x, window_size.y - chat_input_height_full - item_height);
sf::Vector2f body_item_size(body_size.x, item_height);
- sf::RectangleShape item_background(sf::Vector2f(window_size.x, body_item_size.y + replying_to_text_height + margin + body_item_size.y));
- item_background.setPosition(sf::Vector2f(0.0f, body_item_pos.y - replying_to_text_height - margin));
+ sf::RectangleShape item_background(sf::Vector2f(window_size.x, body_item_size.y + chat_input_height_full + replying_to_text_height + margin));
+ item_background.setPosition(sf::Vector2f(0.0f, window_size.y - (body_item_size.y + chat_input_height_full + replying_to_text_height + margin)));
item_background.setFillColor(back_color);
window.draw(item_background);
diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp
index 7c20568..b20c3b0 100644
--- a/src/SearchBar.cpp
+++ b/src/SearchBar.cpp
@@ -130,7 +130,6 @@ namespace QuickMedia {
if(backspace_pressed)
timeout = 750;
if(updated_search && elapsed_time >= timeout) {
- fprintf(stderr, "update search!\n");
updated_search = false;
auto u8 = text.getString().toUtf8();
std::string *u8_str = (std::string*)&u8;
diff --git a/src/plugins/Mangadex.cpp b/src/plugins/Mangadex.cpp
index 59126cb..44c9762 100644
--- a/src/plugins/Mangadex.cpp
+++ b/src/plugins/Mangadex.cpp
@@ -219,29 +219,7 @@ namespace QuickMedia {
return true;
}
- ImageResult MangadexImagesPage::get_number_of_images(int &num_images) {
- num_images = 0;
- ImageResult image_result = get_image_urls_for_chapter(url);
- if(image_result != ImageResult::OK)
- return image_result;
-
- num_images = chapter_image_urls.size();
- return ImageResult::OK;
- }
-
- bool MangadexImagesPage::save_mangadex_cookies(const std::string &url, const std::string &cookie_filepath) {
- CommandArg cookie_arg = { "-c", cookie_filepath };
- std::string server_response;
- if(download_to_string(url, server_response, {std::move(cookie_arg)}, true) != DownloadResult::OK)
- return false;
-
- return true;
- }
-
- ImageResult MangadexImagesPage::get_image_urls_for_chapter(const std::string &url) {
- if(!chapter_image_urls.empty())
- return ImageResult::OK;
-
+ ImageResult MangadexImagesPage::get_page_image_urls(std::vector<std::string> &urls) {
std::string cookie_filepath;
if(!get_cookie_filepath(cookie_filepath))
return ImageResult::ERR;
@@ -283,28 +261,22 @@ namespace QuickMedia {
continue;
std::string image_url = server + chapter_hash_str + "/" + image_name.asCString();
- chapter_image_urls.push_back(std::move(image_url));
+ urls.push_back(std::move(image_url));
}
}
- if(chapter_image_urls.empty())
+ if(urls.empty())
return ImageResult::ERR;
+
return ImageResult::OK;
}
- ImageResult MangadexImagesPage::for_each_page_in_chapter(PageCallback callback) {
- std::vector<std::string> image_urls;
- ImageResult image_result = get_image_urls_for_chapter(url);
- if(image_result != ImageResult::OK)
- return image_result;
-
- image_urls = chapter_image_urls;
-
- for(const std::string &url : image_urls) {
- if(!callback(url))
- break;
- }
+ bool MangadexImagesPage::save_mangadex_cookies(const std::string &url, const std::string &cookie_filepath) {
+ CommandArg cookie_arg = { "-c", cookie_filepath };
+ std::string server_response;
+ if(download_to_string(url, server_response, {std::move(cookie_arg)}, true) != DownloadResult::OK)
+ return false;
- return ImageResult::OK;
+ return true;
}
}
diff --git a/src/plugins/Manganelo.cpp b/src/plugins/Manganelo.cpp
index 8ce9e01..b67acb2 100644
--- a/src/plugins/Manganelo.cpp
+++ b/src/plugins/Manganelo.cpp
@@ -226,36 +226,7 @@ namespace QuickMedia {
return PluginResult::OK;
}
- ImageResult ManganeloImagesPage::get_number_of_images(int &num_images) {
- num_images = 0;
- ImageResult image_result = get_image_urls_for_chapter(url);
- if(image_result != ImageResult::OK)
- return image_result;
-
- num_images = chapter_image_urls.size();
- return ImageResult::OK;
- }
-
- ImageResult ManganeloImagesPage::for_each_page_in_chapter(PageCallback callback) {
- std::vector<std::string> image_urls;
- ImageResult image_result = get_image_urls_for_chapter(url);
- if(image_result != ImageResult::OK)
- return image_result;
-
- image_urls = chapter_image_urls;
-
- for(const std::string &url : image_urls) {
- if(!callback(url))
- break;
- }
-
- return ImageResult::OK;
- }
-
- ImageResult ManganeloImagesPage::get_image_urls_for_chapter(const std::string &url) {
- if(!chapter_image_urls.empty())
- return ImageResult::OK;
-
+ ImageResult ManganeloImagesPage::get_page_image_urls(std::vector<std::string> &urls) {
std::string website_data;
if(download_to_string(url, website_data, {}, true) != DownloadResult::OK)
return ImageResult::NET_ERR;
@@ -273,16 +244,16 @@ namespace QuickMedia {
std::string image_url = strip(src);
urls->push_back(std::move(image_url));
}
- }, &chapter_image_urls);
+ }, &urls);
cleanup:
quickmedia_html_search_deinit(&html_search);
- if(result != 0) {
- chapter_image_urls.clear();
+ if(result != 0)
return ImageResult::ERR;
- }
- if(chapter_image_urls.empty())
+
+ if(urls.empty())
return ImageResult::ERR;
+
return ImageResult::OK;
}
} \ No newline at end of file
diff --git a/src/plugins/Manganelos.cpp b/src/plugins/Manganelos.cpp
index 9ab0915..04d9ca2 100644
--- a/src/plugins/Manganelos.cpp
+++ b/src/plugins/Manganelos.cpp
@@ -117,36 +117,7 @@ namespace QuickMedia {
return true;
}
- ImageResult ManganelosImagesPage::get_number_of_images(int &num_images) {
- num_images = 0;
- ImageResult image_result = get_image_urls_for_chapter(url);
- if(image_result != ImageResult::OK)
- return image_result;
-
- num_images = chapter_image_urls.size();
- return ImageResult::OK;
- }
-
- ImageResult ManganelosImagesPage::for_each_page_in_chapter(PageCallback callback) {
- std::vector<std::string> image_urls;
- ImageResult image_result = get_image_urls_for_chapter(url);
- if(image_result != ImageResult::OK)
- return image_result;
-
- image_urls = chapter_image_urls;
-
- for(const std::string &url : image_urls) {
- if(!callback(url))
- break;
- }
-
- return ImageResult::OK;
- }
-
- ImageResult ManganelosImagesPage::get_image_urls_for_chapter(const std::string &url) {
- if(!chapter_image_urls.empty())
- return ImageResult::OK;
-
+ ImageResult ManganelosImagesPage::get_page_image_urls(std::vector<std::string> &urls) {
std::string website_data;
if(download_to_string(url, website_data, {}, true) != DownloadResult::OK)
return ImageResult::NET_ERR;
@@ -168,16 +139,16 @@ namespace QuickMedia {
return true;
});
}
- }, &chapter_image_urls);
+ }, &urls);
cleanup:
quickmedia_html_search_deinit(&html_search);
- if(result != 0) {
- chapter_image_urls.clear();
+ if(result != 0)
return ImageResult::ERR;
- }
- if(chapter_image_urls.empty())
+
+ if(urls.empty())
return ImageResult::ERR;
+
return ImageResult::OK;
}
} \ No newline at end of file
diff --git a/src/plugins/Mangatown.cpp b/src/plugins/Mangatown.cpp
index 04a1049..4db1ebc 100644
--- a/src/plugins/Mangatown.cpp
+++ b/src/plugins/Mangatown.cpp
@@ -139,65 +139,30 @@ namespace QuickMedia {
return true;
}
- ImageResult MangatownImagesPage::get_number_of_images(int &num_images) {
- num_images = 0;
- ImageResult image_result = get_image_urls_for_chapter(url);
- if(image_result != ImageResult::OK)
- return image_result;
-
- num_images = chapter_image_urls.size();
- return ImageResult::OK;
- }
-
- ImageResult MangatownImagesPage::for_each_page_in_chapter(PageCallback callback) {
- int num_pages;
- ImageResult image_result = get_number_of_images(num_pages);
- if(image_result != ImageResult::OK)
- return image_result;
-
- for(const std::string &full_url : chapter_image_urls) {
- std::string image_src;
- std::string website_data;
- if(download_to_string_cache(full_url, website_data, {}, true) != DownloadResult::OK)
- break;
-
- QuickMediaHtmlSearch html_search;
- int result = quickmedia_html_search_init(&html_search, website_data.c_str());
- if(result != 0)
- goto cleanup;
-
- result = quickmedia_html_find_nodes_xpath(&html_search, "//div[id='viewer']//img",
- [](QuickMediaHtmlNode *node, void *userdata) {
- std::string *image_src = (std::string*)userdata;
- const char *src = quickmedia_html_node_get_attribute_value(node, "src");
- if(src && strstr(src, "/store/manga/")) {
- if(strncmp(src, "//", 2) == 0)
- *image_src = strip(src + 2);
- else
- *image_src = strip(src);
- }
- }, &image_src);
-
- cleanup:
- quickmedia_html_search_deinit(&html_search);
-
- if(result != 0)
- return ImageResult::ERR;
-
- if(image_src.empty())
- break;
-
- if(!callback(image_src))
- break;
- }
-
- return ImageResult::OK;
+ // First page = 0
+ static std::string first_image_url_to_page_image_url(const std::string &image_url, int page) {
+ size_t slash_index = image_url.rfind('/');
+ if(slash_index == std::string::npos)
+ return "";
+
+ size_t dot_index = image_url.rfind('.');
+ if(dot_index == std::string::npos || dot_index <= slash_index)
+ return "";
+
+ std::string filename_without_ext = image_url.substr(slash_index + 1, dot_index - (slash_index + 1));
+ if(!filename_without_ext.empty() && filename_without_ext.back() == '1')
+ ++page;
+
+ std::string page_str = std::to_string(page);
+ if(page_str.size() > filename_without_ext.size())
+ return "";
+
+ filename_without_ext.replace(filename_without_ext.size() - page_str.size(), page_str.size(), page_str);
+ return image_url.substr(0, slash_index) + '/' + filename_without_ext + image_url.substr(dot_index);
}
- ImageResult MangatownImagesPage::get_image_urls_for_chapter(const std::string &url) {
- if(!chapter_image_urls.empty())
- return ImageResult::OK;
-
+ ImageResult MangatownImagesPage::get_page_image_urls(std::vector<std::string> &urls) {
+ std::string image_src;
int num_pages = 0;
std::string website_data;
@@ -209,6 +174,21 @@ namespace QuickMedia {
if(result != 0)
goto cleanup;
+ result = quickmedia_html_find_nodes_xpath(&html_search, "//div[id='viewer']//img",
+ [](QuickMediaHtmlNode *node, void *userdata) {
+ std::string *image_src = (std::string*)userdata;
+ const char *src = quickmedia_html_node_get_attribute_value(node, "src");
+ if(src && strstr(src, "/store/manga/")) {
+ if(strncmp(src, "//", 2) == 0)
+ *image_src = strip(src + 2);
+ else
+ *image_src = strip(src);
+ }
+ }, &image_src);
+
+ if(result != 0 || image_src.empty())
+ goto cleanup;
+
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='page_select']//option",
[](QuickMediaHtmlNode *node, void *userdata) {
int *last_num_pages = (int*)userdata;
@@ -225,15 +205,13 @@ namespace QuickMedia {
cleanup:
quickmedia_html_search_deinit(&html_search);
- if(result != 0) {
- chapter_image_urls.clear();
- return ImageResult::ERR;
- }
- if(num_pages == 0)
+ if(result != 0 || image_src.empty() || num_pages == 0)
return ImageResult::ERR;
+
for(int i = 0; i < num_pages; ++i) {
- chapter_image_urls.push_back(url + std::to_string(1 + i) + ".html");
+ urls.push_back(first_image_url_to_page_image_url(image_src, i));
}
+
return ImageResult::OK;
}
} \ No newline at end of file