diff options
author | dec05eba <dec05eba@protonmail.com> | 2022-02-11 00:42:21 +0100 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2022-02-11 00:42:21 +0100 |
commit | 1f74222bf4cfadead768b095c6b3f8d422ebf84c (patch) | |
tree | 39035288edb79852cef6237f0d7ab8ea146cf218 /src/plugins | |
parent | 404ac476a213164a041f0f53be30855df815aa6a (diff) |
Add local-manga plugin to read local manga
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/LocalManga.cpp | 195 | ||||
-rw-r--r-- | src/plugins/MangaCombined.cpp | 15 | ||||
-rw-r--r-- | src/plugins/Page.cpp | 1 |
3 files changed, 210 insertions, 1 deletions
diff --git a/src/plugins/LocalManga.cpp b/src/plugins/LocalManga.cpp new file mode 100644 index 0000000..06b5cf0 --- /dev/null +++ b/src/plugins/LocalManga.cpp @@ -0,0 +1,195 @@ +#include "../../plugins/LocalManga.hpp" +#include "../../include/Notification.hpp" +#include "../../include/Config.hpp" +#include "../../include/Theme.hpp" +#include "../../include/StringUtils.hpp" +#include "../../include/Storage.hpp" + +namespace QuickMedia { + // Pages are sorted from 1.png to n.png + static std::vector<LocalMangaPage> get_images_in_manga(const Path &directory) { + std::vector<LocalMangaPage> page_list; + for_files_in_dir(directory, [&page_list](const Path &filepath) -> bool { + if(get_file_type(filepath) != FileType::REGULAR) + return true; + + std::string filname_no_ext = filepath.filename_no_ext(); + int page_number = 0; + if(!to_num(filname_no_ext.c_str(), filname_no_ext.size(), page_number) || filepath.ext()[0] == '\0') + return true; + + LocalMangaPage local_manga_page; + local_manga_page.path = filepath; + local_manga_page.number = page_number; + page_list.push_back(std::move(local_manga_page)); + return true; + }); + + std::sort(page_list.begin(), page_list.end(), [](const LocalMangaPage &manga_page1, const LocalMangaPage &manga_page2) { + return manga_page1.number < manga_page2.number; + }); + return page_list; + } + + static std::vector<LocalMangaChapter> get_chapters_in_manga(const Path &directory) { + std::vector<LocalMangaChapter> chapter_list; + for_files_in_dir_sort_last_modified(directory, [&chapter_list](const Path &filepath) -> bool { + if(get_file_type(filepath) != FileType::DIRECTORY) + return true; + + LocalMangaChapter local_manga_chapter; + local_manga_chapter.name = filepath.filename(); + local_manga_chapter.pages = get_images_in_manga(filepath); + if(local_manga_chapter.pages.empty() || !file_get_last_modified_time_seconds(filepath.data.c_str(), &local_manga_chapter.modified_time_seconds)) + return true; + + chapter_list.push_back(std::move(local_manga_chapter)); + return true; + }); + return chapter_list; + } + + static std::vector<LocalManga> get_manga_in_directory(const Path &directory) { + std::vector<LocalManga> manga_list; + for_files_in_dir_sort_last_modified(directory, [&manga_list](const Path &filepath) -> bool { + if(get_file_type(filepath) != FileType::DIRECTORY) + return true; + + LocalManga local_manga; + local_manga.name = filepath.filename(); + local_manga.chapters = get_chapters_in_manga(filepath); + if(local_manga.chapters.empty() || !file_get_last_modified_time_seconds(filepath.data.c_str(), &local_manga.modified_time_seconds)) + return true; + + manga_list.push_back(std::move(local_manga)); + return true; + }); + 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); + 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); + body_item->thumbnail_url = local_manga.chapters.back().pages.front().path.data; + body_item->thumbnail_is_local = true; + body_item->thumbnail_size = {101, 141}; + return body_item; + } + + 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)); + } + return SearchResult::OK; + } + + PluginResult LocalMangaSearchPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) { + if(get_config().local_manga_directory.empty()) { + show_notification("QuickMedia", "local_manga_directory config is not set", Urgency::CRITICAL); + return PluginResult::OK; + } + + 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 PluginResult::OK; + } + + Path manga_url = Path(get_config().local_manga_directory).join(args.url); + std::vector<LocalMangaChapter> chapters = get_chapters_in_manga(manga_url); + + const time_t time_now = time(nullptr); + BodyItems chapters_items; + + for(const LocalMangaChapter &local_manga_chapter : chapters) { + auto body_item = BodyItem::create(local_manga_chapter.name); + body_item->url = local_manga_chapter.name; + body_item->set_description("Updated " + seconds_to_relative_time_str(time_now - local_manga_chapter.modified_time_seconds)); + body_item->set_description_color(get_theme().faded_text_color); + chapters_items.push_back(std::move(body_item)); + } + + 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); + 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(); + + if(get_config().local_manga_directory.empty()) { + show_notification("QuickMedia", "local_manga_directory config is not set", Urgency::CRITICAL); + return PluginResult::OK; + } + + 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 PluginResult::OK; + } + + manga_list = get_manga_in_directory(get_config().local_manga_directory); + + 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)); + } + + return PluginResult::OK; + } + + PluginResult LocalMangaChaptersPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) { + if(get_config().local_manga_directory.empty()) { + show_notification("QuickMedia", "local_manga_directory config is not set", Urgency::CRITICAL); + return PluginResult::OK; + } + + 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 PluginResult::OK; + } + + Path chapter_url = Path(get_config().local_manga_directory).join(content_url).join(args.url); + std::vector<LocalMangaPage> pages = get_images_in_manga(chapter_url); + result_tabs.push_back(Tab{nullptr, std::make_unique<LocalMangaImagesPage>(program, content_title, args.title, args.url, thumbnail_url, std::move(pages)), nullptr}); + return PluginResult::OK; + } + + bool LocalMangaChaptersPage::extract_id_from_url(const std::string &url, std::string &manga_id) const { + manga_id = url; + return true; + } + + ImageResult LocalMangaImagesPage::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 LocalMangaImagesPage::for_each_page_in_chapter(PageCallback callback) { + ImageResult image_result = get_image_urls_for_chapter(url); + if(image_result != ImageResult::OK) return image_result; + for(const std::string &url : chapter_image_urls) { + if(!callback(url)) + break; + } + return ImageResult::OK; + } + + ImageResult LocalMangaImagesPage::get_image_urls_for_chapter(const std::string&) { + if(!chapter_image_urls.empty()) + return ImageResult::OK; + + for(const LocalMangaPage &local_manga_page : pages) { + chapter_image_urls.push_back(local_manga_page.path.data); + } + return ImageResult::OK; + } +}
\ No newline at end of file diff --git a/src/plugins/MangaCombined.cpp b/src/plugins/MangaCombined.cpp index bc4043f..fca5705 100644 --- a/src/plugins/MangaCombined.cpp +++ b/src/plugins/MangaCombined.cpp @@ -4,7 +4,7 @@ namespace QuickMedia { static const int SEARCH_TIMEOUT_MILLISECONDS = 5000; MangaCombinedSearchPage::MangaCombinedSearchPage(Program *program, std::vector<MangaPlugin> search_pages) : - Page(program), search_pages(std::move(search_pages)) + LazyFetchPage(program), search_pages(std::move(search_pages)) { } @@ -125,6 +125,19 @@ namespace QuickMedia { return page->submit(args, result_tabs); } + PluginResult MangaCombinedSearchPage::lazy_fetch(BodyItems&) { + for(MangaPlugin &manga_plugin : search_pages) { + if(manga_plugin.local_manga) { + LazyFetchPage *lazy_fetch_page = dynamic_cast<LazyFetchPage*>(manga_plugin.page.get()); + if(lazy_fetch_page) { + BodyItems dummy_body_items; + lazy_fetch_page->lazy_fetch(dummy_body_items); + } + } + } + return PluginResult::OK; + } + void MangaCombinedSearchPage::cancel_operation() { for(auto &search_thread : search_threads) { search_thread.second.cancel(); diff --git a/src/plugins/Page.cpp b/src/plugins/Page.cpp index 26c795c..c2e8060 100644 --- a/src/plugins/Page.cpp +++ b/src/plugins/Page.cpp @@ -124,6 +124,7 @@ namespace QuickMedia { if(thumbnail_url_json.isString()) { body_item->thumbnail_url = thumbnail_url_json.asString(); body_item->thumbnail_size = {101, 141}; + body_item->thumbnail_is_local = local_thumbnail; } if(timestamp_json.isInt64()) { |