aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-02-03 21:09:26 +0100
committerdec05eba <dec05eba@protonmail.com>2022-02-03 21:09:26 +0100
commit9f12d8a7f6e4cdf0cb95130b69da2b368cc9cbb5 (patch)
tree6b0c85fca617329a94fc94c0f4a247ff7ecd38fd
parentf7f45ddc492b992cc49a92f620e37316e4d1fed4 (diff)
Add thumbnail to manga history page. Start on new manga history format (one json file)
-rw-r--r--include/QuickMedia.hpp4
-rw-r--r--plugins/Manga.hpp4
-rw-r--r--plugins/MangaGeneric.hpp4
-rw-r--r--plugins/Mangadex.hpp4
-rw-r--r--plugins/Manganelo.hpp3
-rw-r--r--src/QuickMedia.cpp180
-rw-r--r--src/plugins/Manga.cpp1
-rw-r--r--src/plugins/MangaCombined.cpp1
-rw-r--r--src/plugins/MangaGeneric.cpp2
-rw-r--r--src/plugins/Mangadex.cpp2
-rw-r--r--src/plugins/Manganelo.cpp2
-rw-r--r--src/plugins/Page.cpp33
12 files changed, 158 insertions, 82 deletions
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index f476396..ce63fbb 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -99,7 +99,7 @@ namespace QuickMedia {
void manga_get_watch_history(const char *plugin_name, BodyItems &history_items);
void youtube_get_watch_history(BodyItems &history_items);
- Json::Value load_video_history_json();
+ Json::Value load_history_json();
void set_clipboard(const std::string &str);
private:
@@ -121,6 +121,7 @@ namespace QuickMedia {
bool video_download_if_non_streamable(std::string &video_url, std::string &audio_url, bool &is_audio_only, bool &has_embedded_audio, PageType previous_page);
int video_get_max_height();
void video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, int play_index, int *parent_body_page = nullptr, const std::string &parent_page_search = "");
+ void update_manga_history(const std::string &manga_id, const std::string &thumbnail_url);
void save_manga_progress(MangaImagesPage *images_page, Json::Value &json_chapters, Json::Value &json_chapter, int &latest_read);
// Returns -1 to go to previous chapter, 0 to stay on same chapter and 1 to go to next chapter
int image_page(MangaImagesPage *images_page, Body *chapters_body);
@@ -167,6 +168,7 @@ namespace QuickMedia {
int image_index;
Path content_storage_file;
Path content_cache_dir;
+ std::string manga_id;
std::string manga_id_base64;
Json::Value content_storage_json;
bool content_storage_file_modified = false;
diff --git a/plugins/Manga.hpp b/plugins/Manga.hpp
index a298c11..7b67e13 100644
--- a/plugins/Manga.hpp
+++ b/plugins/Manga.hpp
@@ -20,7 +20,8 @@ namespace QuickMedia {
class MangaImagesPage : public Page {
public:
- MangaImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url) : Page(program), manga_name(std::move(manga_name)), chapter_name(std::move(chapter_name)), url(std::move(url)), chapter_num_pages(-1) {}
+ MangaImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url, std::string thumbnail_url) :
+ Page(program), manga_name(std::move(manga_name)), thumbnail_url(std::move(thumbnail_url)), chapter_name(std::move(chapter_name)), url(std::move(url)), chapter_num_pages(-1) {}
virtual ~MangaImagesPage() = default;
const char* get_title() const override { return chapter_name.c_str(); }
PageTypez get_type() const override { return PageTypez::MANGA_IMAGES; }
@@ -46,6 +47,7 @@ namespace QuickMedia {
virtual const char* get_website_url() const = 0;
const std::string manga_name;
+ const std::string thumbnail_url;
protected:
std::string chapter_name;
std::string url;
diff --git a/plugins/MangaGeneric.hpp b/plugins/MangaGeneric.hpp
index bb7eb65..439fc19 100644
--- a/plugins/MangaGeneric.hpp
+++ b/plugins/MangaGeneric.hpp
@@ -183,8 +183,8 @@ namespace QuickMedia {
class MangaGenericImagesPage : public MangaImagesPage {
public:
- MangaGenericImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url, const char *service_name, const std::string &website_url, const ListPageQuery *list_page_query, bool fail_on_http_error) :
- MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(url)), service_name(service_name), website_url(website_url), list_page_query(list_page_query), fail_on_http_error(fail_on_http_error) {}
+ MangaGenericImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url, const char *service_name, const std::string &website_url, const ListPageQuery *list_page_query, bool fail_on_http_error, std::string thumbnail_url) :
+ MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(url), std::move(thumbnail_url)), service_name(service_name), website_url(website_url), list_page_query(list_page_query), fail_on_http_error(fail_on_http_error) {}
ImageResult get_number_of_images(int &num_images) override;
ImageResult for_each_page_in_chapter(PageCallback callback) override;
const char* get_service_name() const override { return service_name; }
diff --git a/plugins/Mangadex.hpp b/plugins/Mangadex.hpp
index 2741f0c..d6206cc 100644
--- a/plugins/Mangadex.hpp
+++ b/plugins/Mangadex.hpp
@@ -51,8 +51,8 @@ namespace QuickMedia {
class MangadexImagesPage : public MangaImagesPage {
public:
- MangadexImagesPage(Program *program, MangadexSearchPage *search_page, std::string manga_name, std::string chapter_id, std::string chapter_name) :
- MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(chapter_id)), search_page(search_page) {}
+ MangadexImagesPage(Program *program, MangadexSearchPage *search_page, std::string manga_name, std::string chapter_id, std::string chapter_name, std::string thumbnail_url) :
+ MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(chapter_id), std::move(thumbnail_url)), search_page(search_page) {}
ImageResult get_number_of_images(int &num_images) override;
ImageResult for_each_page_in_chapter(PageCallback callback) override;
const char* get_service_name() const override { return "mangadex"; }
diff --git a/plugins/Manganelo.hpp b/plugins/Manganelo.hpp
index a96d0c9..8946447 100644
--- a/plugins/Manganelo.hpp
+++ b/plugins/Manganelo.hpp
@@ -35,7 +35,8 @@ 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)) {}
+ ManganeloImagesPage(Program *program, std::string manga_name, std::string chapter_name, std::string url, std::string thumbnail_url) :
+ MangaImagesPage(program, std::move(manga_name), std::move(chapter_name), std::move(url), std::move(thumbnail_url)) {}
ImageResult get_number_of_images(int &num_images) override;
ImageResult for_each_page_in_chapter(PageCallback callback) override;
const char* get_service_name() const override { return "manganelo"; }
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index cbe8ea4..a5c423d 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -204,6 +204,7 @@ namespace QuickMedia {
LazyFetchPage(program), search_page(search_page), history_type(history_type) {}
const char* get_title() const override { return "History"; }
PluginResult submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) override {
+ search_page->submit_body_item = submit_body_item;
return search_page->submit(title, url, result_tabs);
}
PluginResult lazy_fetch(BodyItems &result_items) override {
@@ -1064,7 +1065,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1075,7 +1076,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1086,7 +1087,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1097,7 +1098,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1108,7 +1109,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1119,7 +1120,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1130,7 +1131,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1330,27 +1331,41 @@ namespace QuickMedia {
LOGIN
};
- static void fill_history_items_from_json(const Json::Value &history_json, BodyItems &history_items) {
+ static void fill_youtube_history_items_from_json(const Json::Value &history_json, BodyItems &history_items) {
assert(history_json.isArray());
- time_t time_now = time(NULL);
+ std::vector<const Json::Value*> history_json_items;
for(const Json::Value &item : history_json) {
if(!item.isObject())
continue;
- const Json::Value &video_id = item["id"];
+ const Json::Value &timestamp = item["timestamp"];
+ if(!timestamp.isNumeric())
+ continue;
+
+ history_json_items.push_back(&item);
+ }
+
+ std::sort(history_json_items.begin(), history_json_items.end(), [](const Json::Value *val1, const Json::Value *val2) {
+ const Json::Value &timestamp1 = (*val1)["timestamp"];
+ const Json::Value &timestamp2 = (*val2)["timestamp"];
+ return timestamp1.asInt64() > timestamp2.asInt64();
+ });
+
+ time_t time_now = time(NULL);
+ for(const Json::Value *item : history_json_items) {
+ const Json::Value &video_id = (*item)["id"];
if(!video_id.isString())
continue;
- std::string video_id_str = video_id.asString();
- const Json::Value &title = item["title"];
+ const Json::Value &title = (*item)["title"];
if(!title.isString())
continue;
- std::string title_str = title.asString();
- const Json::Value &timestamp = item["timestamp"];
- if(!timestamp.isNumeric())
- continue;
+ const Json::Value &timestamp = (*item)["timestamp"];
+
+ std::string title_str = title.asString();
+ std::string video_id_str = video_id.asString();
auto body_item = BodyItem::create(std::move(title_str));
body_item->url = "https://www.youtube.com/watch?v=" + video_id_str;
@@ -1360,32 +1375,28 @@ namespace QuickMedia {
body_item->thumbnail_size = mgl::vec2i(192, 108);
history_items.push_back(std::move(body_item));
}
-
- std::reverse(history_items.begin(), history_items.end());
}
- static Path get_video_history_filepath(const char *plugin_name) {
- Path video_history_dir = get_storage_dir().join("history");
- if(create_directory_recursive(video_history_dir) != 0) {
- std::string err_msg = "Failed to create video history directory ";
- err_msg += video_history_dir.data;
- show_notification("QuickMedia", err_msg.c_str(), Urgency::CRITICAL);
+ static Path get_history_filepath(const char *plugin_name) {
+ Path history_dir = get_storage_dir().join("history");
+ if(create_directory_recursive(history_dir) != 0) {
+ show_notification("QuickMedia", "Failed to create history directory " + history_dir.data, Urgency::CRITICAL);
exit(1);
}
- Path video_history_filepath = video_history_dir;
- return video_history_filepath.join(plugin_name).append(".json");
+ Path history_filepath = history_dir;
+ return history_filepath.join(plugin_name).append(".json");
}
// This is not cached because we could have multiple instances of QuickMedia running the same plugin!
// TODO: Find a way to optimize this
- Json::Value Program::load_video_history_json() {
- Path video_history_filepath = get_video_history_filepath(plugin_name);
+ Json::Value Program::load_history_json() {
+ Path history_filepath = get_history_filepath(plugin_name);
Json::Value json_result;
- FileType file_type = get_file_type(video_history_filepath);
+ FileType file_type = get_file_type(history_filepath);
if(file_type == FileType::REGULAR) {
- if(!read_file_as_json(video_history_filepath, json_result) || !json_result.isArray()) {
- show_notification("QuickMedia", "Failed to read " + video_history_filepath.data, Urgency::CRITICAL);
+ if(!read_file_as_json(history_filepath, json_result) || !json_result.isArray()) {
+ show_notification("QuickMedia", "Failed to read " + history_filepath.data, Urgency::CRITICAL);
abort();
}
} else {
@@ -1406,8 +1417,21 @@ namespace QuickMedia {
abort();
}
+ Json::Value history_json = load_history_json();
+ std::unordered_map<std::string, std::string> manga_id_to_thumbnail_url_map;
+ for(const Json::Value &history_item : history_json) {
+ const Json::Value &id = history_item["id"];
+ const Json::Value &thumbnail_url = history_item["thumbnail_url"];
+
+ if(!id.isString() || !thumbnail_url.isString())
+ continue;
+
+ manga_id_to_thumbnail_url_map[id.asString()] = thumbnail_url.asString();
+ }
+
+ // TODO: Remove this once manga history file has been in use for a few months and is filled with history
time_t now = time(NULL);
- for_files_in_dir_sort_last_modified(content_storage_dir, [&history_items, plugin_name, now](const Path &filepath) {
+ for_files_in_dir_sort_last_modified(content_storage_dir, [&history_items, plugin_name, &manga_id_to_thumbnail_url_map, now](const Path &filepath) {
// This can happen when QuickMedia crashes/is killed while writing to storage.
// In that case, the storage wont be corrupt but there will be .tmp files.
// TODO: Remove these .tmp files if they exist during startup
@@ -1423,6 +1447,7 @@ namespace QuickMedia {
// TODO: Manga combined
const char *filename = filepath.filename();
+ std::string manga_id = base64_url_decode(filename);
const Json::Value &manga_name = body["name"];
if(!manga_name.isString())
@@ -1436,20 +1461,26 @@ namespace QuickMedia {
body_item->set_description("Last read " + seconds_to_relative_time_str(now - last_modified_time));
body_item->set_description_color(get_theme().faded_text_color);
+ auto thumbnail_it = manga_id_to_thumbnail_url_map.find(manga_id);
+ if(thumbnail_it != manga_id_to_thumbnail_url_map.end()) {
+ body_item->thumbnail_url = thumbnail_it->second;
+ body_item->thumbnail_size = {101, 141};
+ }
+
if(strcmp(plugin_name, "manganelo") == 0)
- body_item->url = "https://manganelo.com/manga/" + base64_url_decode(filename);
+ body_item->url = "https://manganelo.com/manga/" + manga_id;
else if(strcmp(plugin_name, "manganelos") == 0)
- body_item->url = "http://manganelos.com/manga/" + base64_url_decode(filename);
+ body_item->url = "http://manganelos.com/manga/" + manga_id;
else if(strcmp(plugin_name, "mangadex") == 0)
- body_item->url = base64_url_decode(filename);
+ body_item->url = manga_id;
else if(strcmp(plugin_name, "mangatown") == 0)
- body_item->url = "https://mangatown.com/manga/" + base64_url_decode(filename);
+ body_item->url = "https://mangatown.com/manga/" + manga_id;
else if(strcmp(plugin_name, "mangakatana") == 0)
- body_item->url = "https://mangakatana.com/manga/" + base64_url_decode(filename);
+ body_item->url = "https://mangakatana.com/manga/" + manga_id;
else if(strcmp(plugin_name, "onimanga") == 0)
- body_item->url = "https://onimanga.com/" + base64_url_decode(filename);
+ body_item->url = "https://onimanga.com/" + manga_id;
else if(strcmp(plugin_name, "readm") == 0)
- body_item->url = "https://readm.org/manga/" + base64_url_decode(filename);
+ body_item->url = "https://readm.org/manga/" + manga_id;
else
fprintf(stderr, "Error: Not implemented: filename to manga chapter list\n");
@@ -1459,7 +1490,7 @@ namespace QuickMedia {
}
void Program::youtube_get_watch_history(BodyItems &history_items) {
- fill_history_items_from_json(load_video_history_json(), history_items);
+ fill_youtube_history_items_from_json(load_history_json(), history_items);
}
static void get_body_dimensions(const mgl::vec2i &window_size, SearchBar *search_bar, mgl::vec2f &body_pos, mgl::vec2f &body_size, bool has_tabs = false) {
@@ -1494,6 +1525,7 @@ namespace QuickMedia {
bool Program::load_manga_content_storage(const char *service_name, const std::string &manga_title, const std::string &manga_url, const std::string &manga_id) {
Path content_storage_dir = get_storage_dir().join(service_name);
+ this->manga_id = manga_id;
manga_id_base64 = base64_url_encode(manga_id);
content_storage_file = content_storage_dir.join(manga_id_base64);
content_storage_json.clear();
@@ -1629,7 +1661,9 @@ namespace QuickMedia {
}
std::string bookmark_title = body_item->get_title();
- if(bookmark_title.empty()) bookmark_title = body_item->get_author();
+ if(bookmark_title.empty())
+ bookmark_title = body_item->get_author();
+
show_notification("QuickMedia", "Removed " + bookmark_title + " from bookmarks");
removed = true;
return true;
@@ -2357,12 +2391,10 @@ namespace QuickMedia {
return false;
}
- static int watch_history_get_item_by_id(const Json::Value &video_history_json, const char *id) {
- assert(video_history_json.isArray());
+ static Json::Value* history_get_item_by_id(Json::Value &history_json, const char *id) {
+ assert(history_json.isArray());
- int index = -1;
- for(const Json::Value &item : video_history_json) {
- ++index;
+ for(Json::Value &item : history_json) {
if(!item.isObject())
continue;
@@ -2371,10 +2403,10 @@ namespace QuickMedia {
continue;
if(strcmp(id, id_json.asCString()) == 0)
- return index;
+ return &item;
}
- return -1;
+ return nullptr;
}
enum class WindowFullscreenState {
@@ -2924,25 +2956,21 @@ namespace QuickMedia {
return;
}
- Json::Value video_history_json = load_video_history_json();
+ Json::Value video_history_json = load_history_json();
- int existing_index = watch_history_get_item_by_id(video_history_json, video_id.c_str());
- if(existing_index != -1) {
- Json::Value removed;
- /* TODO: Optimize. This is slow */
- video_history_json.removeIndex(existing_index, &removed);
- }
-
time_t time_now = time(NULL);
+ Json::Value *json_item = history_get_item_by_id(video_history_json, video_id.c_str());
+ if(json_item) {
+ (*json_item)["timestamp"] = (Json::Int64)time_now;
+ } else {
+ Json::Value new_content_object(Json::objectValue);
+ new_content_object["id"] = video_id;
+ new_content_object["title"] = video_title;
+ new_content_object["timestamp"] = (Json::Int64)time_now;
+ video_history_json.append(std::move(new_content_object));
+ }
- Json::Value new_content_object(Json::objectValue);
- new_content_object["id"] = video_id;
- new_content_object["title"] = video_title;
- new_content_object["timestamp"] = (Json::Int64)time_now;
-
- video_history_json.append(std::move(new_content_object));
-
- Path video_history_filepath = get_video_history_filepath(plugin_name);
+ Path video_history_filepath = get_history_filepath(plugin_name);
save_json_to_file_atomic(video_history_filepath, video_history_json);
}
};
@@ -3483,6 +3511,28 @@ namespace QuickMedia {
}
}
+ void Program::update_manga_history(const std::string &manga_id, const std::string &thumbnail_url) {
+ Json::Value history_json = load_history_json();
+ const time_t time_now = time(NULL);
+
+ Json::Value *json_item = history_get_item_by_id(history_json, manga_id.c_str());
+ if(json_item) {
+ (*json_item)["timestamp"] = (Json::Int64)time_now;
+ if(!thumbnail_url.empty())
+ (*json_item)["thumbnail_url"] = thumbnail_url;
+ } else {
+ Json::Value new_content_object(Json::objectValue);
+ new_content_object["id"] = manga_id;
+ new_content_object["timestamp"] = (Json::Int64)time_now;
+ if(!thumbnail_url.empty())
+ new_content_object["thumbnail_url"] = thumbnail_url;
+ history_json.append(std::move(new_content_object));
+ }
+
+ Path history_filepath = get_history_filepath(plugin_name);
+ save_json_to_file_atomic(history_filepath, history_json);
+ }
+
void Program::save_manga_progress(MangaImagesPage *images_page, Json::Value &json_chapters, Json::Value &json_chapter, int &latest_read) {
image_index = std::max(0, std::min(image_index, num_manga_pages));
@@ -3540,6 +3590,7 @@ namespace QuickMedia {
Json::Value json_chapter;
int latest_read;
save_manga_progress(images_page, json_chapters, json_chapter, latest_read);
+ update_manga_history(manga_id, images_page->thumbnail_url);
if(image_index < num_manga_pages) {
std::string error_msg;
@@ -3751,6 +3802,7 @@ namespace QuickMedia {
if(!save_json_to_file_atomic(content_storage_file, content_storage_json)) {
show_notification("QuickMedia", "Failed to save manga progress", Urgency::CRITICAL);
}
+ update_manga_history(manga_id, images_page->thumbnail_url);
}
}
diff --git a/src/plugins/Manga.cpp b/src/plugins/Manga.cpp
index 5f0aae7..4401974 100644
--- a/src/plugins/Manga.cpp
+++ b/src/plugins/Manga.cpp
@@ -24,6 +24,7 @@ namespace QuickMedia {
std::shared_ptr<BodyItem> MangaChaptersPage::get_bookmark_body_item() {
auto body_item = BodyItem::create(content_title);
body_item->url = content_url;
+ body_item->thumbnail_url = thumbnail_url;
return body_item;
}
} \ No newline at end of file
diff --git a/src/plugins/MangaCombined.cpp b/src/plugins/MangaCombined.cpp
index 0742562..f678e57 100644
--- a/src/plugins/MangaCombined.cpp
+++ b/src/plugins/MangaCombined.cpp
@@ -122,6 +122,7 @@ namespace QuickMedia {
PluginResult MangaCombinedSearchPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
Page *page = (Page*)submit_body_item->userdata;
if(!page) return PluginResult::OK;
+ page->submit_body_item = submit_body_item;
return page->submit(title, url, result_tabs);
}
diff --git a/src/plugins/MangaGeneric.cpp b/src/plugins/MangaGeneric.cpp
index e25fd96..0c1ee37 100644
--- a/src/plugins/MangaGeneric.cpp
+++ b/src/plugins/MangaGeneric.cpp
@@ -380,7 +380,7 @@ namespace QuickMedia {
}
PluginResult MangaGenericChaptersPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
- result_tabs.push_back(Tab{nullptr, std::make_unique<MangaGenericImagesPage>(program, content_title, title, url, service_name, website_url, list_page_query, fail_on_http_error), nullptr});
+ result_tabs.push_back(Tab{nullptr, std::make_unique<MangaGenericImagesPage>(program, content_title, title, url, service_name, website_url, list_page_query, fail_on_http_error, thumbnail_url), nullptr});
return PluginResult::OK;
}
diff --git a/src/plugins/Mangadex.cpp b/src/plugins/Mangadex.cpp
index 8dd6822..88f4c18 100644
--- a/src/plugins/Mangadex.cpp
+++ b/src/plugins/Mangadex.cpp
@@ -377,7 +377,7 @@ namespace QuickMedia {
}
PluginResult MangadexChaptersPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
- result_tabs.push_back(Tab{nullptr, std::make_unique<MangadexImagesPage>(program, search_page, content_title, url, title), nullptr});
+ result_tabs.push_back(Tab{nullptr, std::make_unique<MangadexImagesPage>(program, search_page, content_title, url, title, thumbnail_url), nullptr});
return PluginResult::OK;
}
diff --git a/src/plugins/Manganelo.cpp b/src/plugins/Manganelo.cpp
index e5c5719..55f0467 100644
--- a/src/plugins/Manganelo.cpp
+++ b/src/plugins/Manganelo.cpp
@@ -154,7 +154,7 @@ namespace QuickMedia {
}
PluginResult ManganeloChaptersPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
- result_tabs.push_back(Tab{nullptr, std::make_unique<ManganeloImagesPage>(program, content_title, title, url), nullptr});
+ result_tabs.push_back(Tab{nullptr, std::make_unique<ManganeloImagesPage>(program, content_title, title, url, thumbnail_url), nullptr});
return PluginResult::OK;
}
diff --git a/src/plugins/Page.cpp b/src/plugins/Page.cpp
index 34ca889..2e3caf4 100644
--- a/src/plugins/Page.cpp
+++ b/src/plugins/Page.cpp
@@ -85,16 +85,34 @@ namespace QuickMedia {
if(!read_file_as_json(bookmark_path, json_root) || !json_root.isArray())
return PluginResult::OK;
+ std::vector<const Json::Value*> bookmark_json_items;
+ for(const Json::Value &item : json_root) {
+ if(!item.isObject())
+ continue;
+
+ const Json::Value &timestamp = item["timestamp"];
+ if(!timestamp.isInt64())
+ continue;
+
+ bookmark_json_items.push_back(&item);
+ }
+
+ std::sort(bookmark_json_items.begin(), bookmark_json_items.end(), [](const Json::Value *val1, const Json::Value *val2) {
+ const Json::Value &timestamp1 = (*val1)["timestamp"];
+ const Json::Value &timestamp2 = (*val2)["timestamp"];
+ return timestamp1.asInt64() > timestamp2.asInt64();
+ });
+
const time_t time_now = time(nullptr);
- for(const Json::Value &item_json : json_root) {
- if(!item_json.isObject())
+ for(const Json::Value *item_json : bookmark_json_items) {
+ if(!item_json->isObject())
continue;
- const Json::Value &title_json = item_json["title"];
- const Json::Value &author_json = item_json["author"];
- const Json::Value &url_json = item_json["url"];
- const Json::Value &thumbnail_url_json = item_json["thumbnail_url"];
- const Json::Value &timestamp_json = item_json["timestamp"];
+ const Json::Value &title_json = (*item_json)["title"];
+ const Json::Value &author_json = (*item_json)["author"];
+ const Json::Value &url_json = (*item_json)["url"];
+ const Json::Value &thumbnail_url_json = (*item_json)["thumbnail_url"];
+ const Json::Value &timestamp_json = (*item_json)["timestamp"];
auto body_item = BodyItem::create(title_json.isString() ? title_json.asString() : "");
if(author_json.isString())
@@ -116,7 +134,6 @@ namespace QuickMedia {
result_items.push_back(std::move(body_item));
}
- std::reverse(result_items.begin(), result_items.end());
return PluginResult::OK;
}
} \ No newline at end of file