#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 get_images_in_manga(const Path &directory) { std::vector 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 get_chapters_in_manga(const Path &directory) { std::vector chapter_list; auto callback = [&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; }; if(get_config().local_manga_sort_chapters_by_name) for_files_in_dir_sort_name(directory, std::move(callback), FileSortDirection::DESC); else for_files_in_dir_sort_last_modified(directory, std::move(callback)); return chapter_list; } static std::vector get_manga_in_directory(const Path &directory) { std::vector manga_list; auto callback = [&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; }; if(get_config().local_manga_sort_by_name) for_files_in_dir_sort_name(directory, std::move(callback), FileSortDirection::ASC); else for_files_in_dir_sort_last_modified(directory, std::move(callback)); return manga_list; } static std::shared_ptr 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 &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 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(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 &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 pages = get_images_in_manga(chapter_url); result_tabs.push_back(Tab{nullptr, std::make_unique(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; } }