From a8d597d59e347a80b109060ec8c7a88827487f57 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 12 Feb 2022 13:42:04 +0100 Subject: Local manga: add ctrl+r to search page to mark manga as read/unread --- src/plugins/LocalManga.cpp | 113 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) (limited to 'src/plugins') diff --git a/src/plugins/LocalManga.cpp b/src/plugins/LocalManga.cpp index 1807ca7..8344527 100644 --- a/src/plugins/LocalManga.cpp +++ b/src/plugins/LocalManga.cpp @@ -5,9 +5,12 @@ #include "../../include/StringUtils.hpp" #include "../../include/Storage.hpp" #include "../../external/cppcodec/base64_url.hpp" +#include "../../include/QuickMedia.hpp" #include namespace QuickMedia { + static const mgl::Color finished_reading_color = mgl::Color(43, 255, 47); + // Pages are sorted from 1.png to n.png static std::vector get_images_in_manga(const Path &directory) { std::vector page_list; @@ -81,6 +84,94 @@ namespace QuickMedia { return manga_list; } + enum class ReadStatus { + READ, + UNREAD + }; + + // Returns the new read status + static bool toggle_read_save_to_file(Program *program, const std::string &manga_name, const std::string &thumbnail_url, ReadStatus &read_status) { + if(get_config().local_manga_directory.empty()) { + show_notification("QuickMedia", "local_manga_directory config is not set", Urgency::CRITICAL); + return false; + } + + if(get_file_type(get_config().local_manga_directory) != FileType::DIRECTORY) { + show_notification("QuickMedia", "local_manga_directory config is not set to a valid directory", Urgency::CRITICAL); + return false; + } + + Path manga_url = Path(get_config().local_manga_directory).join(manga_name); + std::vector chapters = get_chapters_in_manga(manga_url); + if(chapters.empty() || chapters.front().pages.empty()) + return false; + + Path content_storage_dir = get_storage_dir().join("local-manga"); + if(create_directory_recursive(content_storage_dir) != 0) { + show_notification("QuickMedia", "Failed to create directory: " + content_storage_dir.data, Urgency::CRITICAL); + return false; + } + + Path content_storage_file = content_storage_dir; + content_storage_file.join(cppcodec::base64_url::encode(manga_name)); + Json::Value content_storage_json; + + bool result = true; + FileType file_type = get_file_type(content_storage_file); + if(file_type == FileType::REGULAR) { + result = read_file_as_json(content_storage_file, content_storage_json) && content_storage_json.isObject(); + if(!result) { + show_notification("QuickMedia", "Failed to read " + content_storage_file.data, Urgency::CRITICAL); + return false; + } + } else { + result = true; + } + + if(!content_storage_json.isObject()) + content_storage_json = Json::Value(Json::objectValue); + + content_storage_json["name"] = manga_name; + content_storage_json["url"] = manga_name; + + Json::Value *chapters_json = &content_storage_json["chapters"]; + if(!chapters_json->isObject()) { + content_storage_json["chapters"] = Json::Value(Json::objectValue); + chapters_json = &content_storage_json["chapters"]; + } + + const LocalMangaChapter &latest_chapter = chapters.front(); + Json::Value *chapter_json = &(*chapters_json)[latest_chapter.name]; + if(!chapter_json->isObject()) { + (*chapters_json)[latest_chapter.name] = Json::Value(Json::objectValue); + chapters_json = &(*chapters_json)[latest_chapter.name]; + } + + bool read = false; + const Json::Value ¤t_json = (*chapter_json)["current"]; + const Json::Value &total_json = (*chapter_json)["total"]; + if(current_json.isInt() && total_json.isInt() && current_json.asInt() >= total_json.asInt()) { + chapters_json->removeMember(latest_chapter.name); + read = true; + } else { + (*chapter_json)["current"] = (int)latest_chapter.pages.size(); + (*chapter_json)["total"] = (int)latest_chapter.pages.size(); + (*chapter_json)["url"] = latest_chapter.name; + read = false; + } + + if(!save_json_to_file_atomic(content_storage_file, content_storage_json)) { + show_notification("QuickMedia", std::string("Failed to mark manga as ") + (read ? "unread" : "read"), Urgency::CRITICAL); + return false; + } + + read_status = (read ? ReadStatus::UNREAD : ReadStatus::READ); + if(read_status == ReadStatus::READ) + program->update_manga_history(manga_name, thumbnail_url); + + return true; + } + static bool has_finished_reading_latest_chapter(const LocalManga &manga, const Json::Value &chapters_json) { if(manga.chapters.empty()) return false; @@ -134,7 +225,7 @@ namespace QuickMedia { auto body_item = BodyItem::create(std::move(title)); if(has_finished_reading) - body_item->set_title_color(mgl::Color(43, 255, 47)); + body_item->set_title_color(finished_reading_color); 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); @@ -226,6 +317,26 @@ namespace QuickMedia { return body_item; } + void LocalMangaSearchPage::toggle_read(BodyItem *selected_item) { + if(!selected_item) + return; + + ReadStatus read_status; + if(!toggle_read_save_to_file(program, selected_item->url, selected_item->thumbnail_url, read_status)) + return; + + mgl::Color color = get_theme().text_color; + std::string title; + if(read_status == ReadStatus::READ) { + title = "[Finished reading] "; + color = finished_reading_color; + } + title += selected_item->url; + + selected_item->set_title(std::move(title)); + selected_item->set_title_color(color); + } + static std::unordered_set get_lines_in_file(const Path &filepath) { std::unordered_set lines; -- cgit v1.2.3