aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-02-12 04:31:44 +0100
committerdec05eba <dec05eba@protonmail.com>2022-02-12 04:33:00 +0100
commit74b98bed98aad3e70e8abe51292767ea8a7d109a (patch)
treeea1558431137f8a1e52f4d550c0438e68e676f6f /src/plugins
parentcc445c60d4806fb462a3efc27bf8d727176f77da (diff)
Local-manga: show if the latest chapter of a manga has been read
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/LocalManga.cpp86
-rw-r--r--src/plugins/Manga.cpp2
2 files changed, 79 insertions, 9 deletions
diff --git a/src/plugins/LocalManga.cpp b/src/plugins/LocalManga.cpp
index c39752b..1807ca7 100644
--- a/src/plugins/LocalManga.cpp
+++ b/src/plugins/LocalManga.cpp
@@ -4,7 +4,8 @@
#include "../../include/Theme.hpp"
#include "../../include/StringUtils.hpp"
#include "../../include/Storage.hpp"
-#include <unordered_set>
+#include "../../external/cppcodec/base64_url.hpp"
+#include <json/value.h>
namespace QuickMedia {
// Pages are sorted from 1.png to n.png
@@ -80,8 +81,60 @@ namespace QuickMedia {
return manga_list;
}
- static std::shared_ptr<BodyItem> local_manga_to_body_item(const LocalManga &local_manga, time_t time_now) {
- auto body_item = BodyItem::create(local_manga.name);
+ static bool has_finished_reading_latest_chapter(const LocalManga &manga, const Json::Value &chapters_json) {
+ if(manga.chapters.empty())
+ return false;
+
+ const Json::Value &chapter_json = chapters_json[manga.chapters.front().name];
+ if(!chapter_json.isObject())
+ return false;
+
+ const Json::Value &current_json = chapter_json["current"];
+ const Json::Value &total_json = chapter_json["total"];
+ if(!current_json.isInt() || !total_json.isInt())
+ return false;
+
+ return current_json.asInt() >= total_json.asInt();
+ }
+
+ // TODO: Check if this is too slow with a lot of manga.
+ // In that case, only save latest read chapter (or newest chapter read)
+ // into one file with a list of all manga.
+ static std::unordered_set<std::string> get_manga_finished_reading(const std::vector<LocalManga> &manga_list) {
+ Path local_manga_config_dir = get_storage_dir().join("local-manga");
+ std::unordered_set<std::string> finished_reading;
+
+ for(const LocalManga &local_manga : manga_list) {
+ std::string manga_name_base64_url = cppcodec::base64_url::encode<std::string>(local_manga.name);
+ Path manga_progress_filepath = local_manga_config_dir;
+ manga_progress_filepath.join(manga_name_base64_url);
+
+ Json::Value json_root;
+ if(!read_file_as_json(manga_progress_filepath, json_root))
+ continue;
+
+ if(!json_root.isObject())
+ continue;
+
+ const Json::Value &chapters_json = json_root["chapters"];
+ if(!chapters_json.isObject())
+ continue;
+
+ if(has_finished_reading_latest_chapter(local_manga, chapters_json))
+ finished_reading.insert(local_manga.name);
+ }
+ return finished_reading;
+ }
+
+ static std::shared_ptr<BodyItem> local_manga_to_body_item(const LocalManga &local_manga, time_t time_now, bool has_finished_reading) {
+ std::string title;
+ if(has_finished_reading)
+ title = "[Finished reading] ";
+ title += local_manga.name;
+
+ auto body_item = BodyItem::create(std::move(title));
+ if(has_finished_reading)
+ body_item->set_title_color(mgl::Color(43, 255, 47));
body_item->url = local_manga.name;
body_item->set_description("Latest chapter: " + local_manga.chapters.front().name + "\nUpdated " + seconds_to_relative_time_str(time_now - local_manga.modified_time_seconds));
body_item->set_description_color(get_theme().faded_text_color);
@@ -94,8 +147,10 @@ namespace QuickMedia {
SearchResult LocalMangaSearchPage::search(const std::string &str, BodyItems &result_items) {
time_t time_now = time(nullptr);
for(const LocalManga &local_manga : manga_list) {
- if(string_find_fuzzy_case_insensitive(local_manga.name, str))
- result_items.push_back(local_manga_to_body_item(local_manga, time_now));
+ if(string_find_fuzzy_case_insensitive(local_manga.name, str)) {
+ const bool has_finished_reading = finished_reading_manga.find(local_manga.name) != finished_reading_manga.end();
+ result_items.push_back(local_manga_to_body_item(local_manga, time_now, has_finished_reading));
+ }
}
return SearchResult::OK;
}
@@ -128,13 +183,14 @@ namespace QuickMedia {
auto chapters_body = create_body();
chapters_body->set_items(std::move(chapters_items));
- auto chapters_page = std::make_unique<LocalMangaChaptersPage>(program, args.title, args.url, args.thumbnail_url);
+ auto chapters_page = std::make_unique<LocalMangaChaptersPage>(program, args.url, args.url, args.thumbnail_url);
result_tabs.push_back(Tab{std::move(chapters_body), std::move(chapters_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
return PluginResult::OK;
}
PluginResult LocalMangaSearchPage::lazy_fetch(BodyItems &result_items) {
manga_list.clear();
+ finished_reading_manga.clear();
if(get_config().local_manga_directory.empty()) {
show_notification("QuickMedia", "local_manga_directory config is not set", Urgency::CRITICAL);
@@ -148,14 +204,28 @@ namespace QuickMedia {
manga_list = get_manga_in_directory(get_config().local_manga_directory);
+ if(standalone)
+ finished_reading_manga = get_manga_finished_reading(manga_list);
+
const time_t time_now = time(nullptr);
for(const LocalManga &local_manga : manga_list) {
- result_items.push_back(local_manga_to_body_item(local_manga, time_now));
+ const bool has_finished_reading = finished_reading_manga.find(local_manga.name) != finished_reading_manga.end();
+ result_items.push_back(local_manga_to_body_item(local_manga, time_now, has_finished_reading));
}
return PluginResult::OK;
}
+ std::shared_ptr<BodyItem> LocalMangaSearchPage::get_bookmark_body_item(BodyItem *selected_item) {
+ if(!selected_item)
+ return nullptr;
+
+ auto body_item = BodyItem::create(selected_item->url);
+ body_item->url = selected_item->url;
+ body_item->thumbnail_url = selected_item->thumbnail_url;
+ return body_item;
+ }
+
static std::unordered_set<std::string> get_lines_in_file(const Path &filepath) {
std::unordered_set<std::string> lines;
@@ -208,7 +278,7 @@ namespace QuickMedia {
return PluginResult::OK;
}
- result_tabs.push_back(Tab{nullptr, std::make_unique<LocalMangaImagesPage>(program, content_title, args.title, args.url, thumbnail_url), nullptr});
+ result_tabs.push_back(Tab{nullptr, std::make_unique<LocalMangaImagesPage>(program, content_title, args.url, args.url, thumbnail_url), nullptr});
if(is_program_executable_by_name("automedia"))
append_seen_manga_to_automedia_seen(content_url + "/" + args.url);
diff --git a/src/plugins/Manga.cpp b/src/plugins/Manga.cpp
index 4401974..e4269fe 100644
--- a/src/plugins/Manga.cpp
+++ b/src/plugins/Manga.cpp
@@ -21,7 +21,7 @@ namespace QuickMedia {
load_manga_content_storage(get_service_name(), content_title, content_url, manga_id);
}
- std::shared_ptr<BodyItem> MangaChaptersPage::get_bookmark_body_item() {
+ std::shared_ptr<BodyItem> MangaChaptersPage::get_bookmark_body_item(BodyItem*) {
auto body_item = BodyItem::create(content_title);
body_item->url = content_url;
body_item->thumbnail_url = thumbnail_url;