#include "../../plugins/Mangatown.hpp" #include "../../include/Notification.hpp" #include "../../include/StringUtils.hpp" #include "../../include/NetUtils.hpp" #include static const std::string mangatown_url = "https://www.mangatown.com"; namespace QuickMedia { static bool is_number_with_zero_fill(const char *str) { while(*str == '0') { ++str; } return atoi(str) != 0; } static SearchResult search_page(const std::string &str, int page, BodyItems &result_items) { std::string url = "https://www.mangatown.com/search?name="; url += url_param_encode(str); url += "&page=" + std::to_string(page); std::string website_data; if(download_to_string(url, website_data, {}, true) != DownloadResult::OK) return SearchResult::NET_ERR; if(website_data.empty()) return SearchResult::OK; QuickMediaHtmlSearch html_search; int result = quickmedia_html_search_init(&html_search, website_data.c_str()); if(result != 0) goto cleanup; result = quickmedia_html_find_nodes_xpath(&html_search, "//p[class='title']/a", [](QuickMediaHtmlNode *node, void *userdata) { auto *item_data = (BodyItems*)userdata; const char *href = quickmedia_html_node_get_attribute_value(node, "href"); const char *title = quickmedia_html_node_get_attribute_value(node, "title"); if(href && title && strncmp(href, "/manga/", 7) == 0) { auto item = BodyItem::create(strip(title)); item->url = mangatown_url + href; item_data->push_back(std::move(item)); } }, &result_items); BodyItemImageContext body_item_image_context; body_item_image_context.body_items = &result_items; body_item_image_context.index = 0; result = quickmedia_html_find_nodes_xpath(&html_search, "//a[class='manga_cover']/img", [](QuickMediaHtmlNode *node, void *userdata) { auto *item_data = (BodyItemImageContext*)userdata; const char *src = quickmedia_html_node_get_attribute_value(node, "src"); if(src && item_data->index < item_data->body_items->size()) { (*item_data->body_items)[item_data->index]->thumbnail_url = src; item_data->index++; } }, &body_item_image_context); cleanup: quickmedia_html_search_deinit(&html_search); return SearchResult::OK; } SearchResult MangatownSearchPage::search(const std::string &str, BodyItems &result_items) { return search_page(str, 1, result_items); } PluginResult MangatownSearchPage::get_page(const std::string &str, int page, BodyItems &result_items) { return search_result_to_plugin_result(search_page(str, 1 + page, result_items)); } PluginResult MangatownSearchPage::submit(const std::string &title, const std::string &url, std::vector &result_tabs) { BodyItems chapters_items; std::string website_data; if(download_to_string(url, website_data, {}, true) != DownloadResult::OK) return PluginResult::NET_ERR; QuickMediaHtmlSearch html_search; int result = quickmedia_html_search_init(&html_search, website_data.c_str()); if(result != 0) goto cleanup; result = quickmedia_html_find_nodes_xpath(&html_search, "//ul[class='chapter_list']//a", [](QuickMediaHtmlNode *node, void *userdata) { auto *item_data = (BodyItems*)userdata; const char *href = quickmedia_html_node_get_attribute_value(node, "href"); const char *text = quickmedia_html_node_get_text(node); if(href && text && strncmp(href, "/manga/", 7) == 0) { auto item = BodyItem::create(strip(text)); item->url = mangatown_url + href; item_data->push_back(std::move(item)); } }, &chapters_items); cleanup: quickmedia_html_search_deinit(&html_search); if(result != 0) return PluginResult::ERR; int chapter_num = chapters_items.size(); for(auto &body_item : chapters_items) { body_item->set_title("Ch. " + std::to_string(chapter_num)); chapter_num--; } auto body = create_body(); body->items = std::move(chapters_items); result_tabs.push_back(Tab{std::move(body), std::make_unique(program, title, url), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); return PluginResult::OK; } PluginResult MangatownChaptersPage::submit(const std::string &title, const std::string &url, std::vector &result_tabs) { result_tabs.push_back(Tab{nullptr, std::make_unique(program, content_title, title, url), nullptr}); return PluginResult::OK; } bool MangatownChaptersPage::extract_id_from_url(const std::string &url, std::string &manga_id) const { size_t start_index = url.find("/manga/"); if(start_index == std::string::npos) return false; start_index += 7; size_t end_index = url.find("/", start_index); if(end_index == std::string::npos) { manga_id = url.substr(start_index); return true; } manga_id = url.substr(start_index, end_index - start_index); return true; } ImageResult MangatownImagesPage::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 MangatownImagesPage::for_each_page_in_chapter(PageCallback callback) { int num_pages; ImageResult image_result = get_number_of_images(num_pages); if(image_result != ImageResult::OK) return image_result; for(const std::string &full_url : chapter_image_urls) { std::string image_src; std::string website_data; if(download_to_string_cache(full_url, website_data, {}, true) != DownloadResult::OK) break; QuickMediaHtmlSearch html_search; int result = quickmedia_html_search_init(&html_search, website_data.c_str()); if(result != 0) goto cleanup; result = quickmedia_html_find_nodes_xpath(&html_search, "//div[id='viewer']//img", [](QuickMediaHtmlNode *node, void *userdata) { std::string *image_src = (std::string*)userdata; const char *src = quickmedia_html_node_get_attribute_value(node, "src"); if(src && strstr(src, "/store/manga/")) { if(strncmp(src, "//", 2) == 0) *image_src = src + 2; else *image_src = src; } }, &image_src); cleanup: quickmedia_html_search_deinit(&html_search); if(result != 0) return ImageResult::ERR; if(image_src.empty()) break; if(!callback(image_src)) break; } return ImageResult::OK; } ImageResult MangatownImagesPage::get_image_urls_for_chapter(const std::string &url) { if(!chapter_image_urls.empty()) return ImageResult::OK; int num_pages = 0; std::string website_data; if(download_to_string(url, website_data, {}, true) != DownloadResult::OK) return ImageResult::NET_ERR; QuickMediaHtmlSearch html_search; int result = quickmedia_html_search_init(&html_search, website_data.c_str()); if(result != 0) goto cleanup; result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='page_select']//option", [](QuickMediaHtmlNode *node, void *userdata) { int *last_num_pages = (int*)userdata; const char *value = quickmedia_html_node_get_attribute_value(node, "value"); const char *text = quickmedia_html_node_get_text(node); if(value && strncmp(value, "/manga/", 7) == 0) { if(is_number_with_zero_fill(text)) { (*last_num_pages)++; } } }, &num_pages); num_pages /= 2; cleanup: quickmedia_html_search_deinit(&html_search); if(result != 0) { chapter_image_urls.clear(); return ImageResult::ERR; } if(num_pages == 0) return ImageResult::ERR; for(int i = 0; i < num_pages; ++i) { chapter_image_urls.push_back(url + std::to_string(1 + i) + ".html"); } return ImageResult::OK; } }