From 88d3dbbd12d17056323f40effe00ce8ab8180691 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 16 Apr 2021 20:09:02 +0200 Subject: Add mangakatana --- src/plugins/MangaGeneric.cpp | 166 +++++++++++++++++++++++++++---------------- 1 file changed, 103 insertions(+), 63 deletions(-) (limited to 'src/plugins/MangaGeneric.cpp') diff --git a/src/plugins/MangaGeneric.cpp b/src/plugins/MangaGeneric.cpp index a359698..607488f 100644 --- a/src/plugins/MangaGeneric.cpp +++ b/src/plugins/MangaGeneric.cpp @@ -98,7 +98,8 @@ namespace QuickMedia { }, page_image_userdata); } - MangaGenericSearchPage::MangaGenericSearchPage(Program *program, const char *service_name, const char *website_url) : Page(program), service_name(service_name), website_url(website_url ? website_url : "") + MangaGenericSearchPage::MangaGenericSearchPage(Program *program, const char *service_name, const char *website_url, bool fail_on_http_error) : + Page(program), service_name(service_name), website_url(website_url ? website_url : ""), fail_on_http_error(fail_on_http_error) { if(!this->website_url.empty()) { if(this->website_url.back() != '/') @@ -111,7 +112,7 @@ namespace QuickMedia { } PluginResult MangaGenericSearchPage::get_page(const std::string &str, int page, BodyItems &result_items) { - if(!search_query.search_prefix || !search_query.page_prefix || !text_query.html_query || !text_query.title_field || !text_query.url_field) { + if(!search_query.search_template || !text_query.html_query || !text_query.title_field || !text_query.url_field) { assert(false); return PluginResult::ERR; } @@ -122,12 +123,12 @@ namespace QuickMedia { search_userdata.field2 = text_query.url_field; search_userdata.field2_contains = text_query.url_contains; - std::string url = search_query.search_prefix; - url += url_param_encode(str); - url += search_query.page_prefix + std::to_string(search_query.page_start + page); + std::string url = search_query.search_template; + string_replace_all(url, "%s", url_param_encode(str)); + string_replace_all(url, "%p", std::to_string(search_query.page_start + page)); std::string website_data; - if(download_to_string(url, website_data, {}, true) != DownloadResult::OK) + if(download_to_string(url, website_data, {}, true, fail_on_http_error) != DownloadResult::OK) return PluginResult::NET_ERR; if(website_data.empty()) @@ -189,7 +190,7 @@ namespace QuickMedia { search_userdata.field2_contains = list_chapters_query.url_contains; std::string website_data; - if(download_to_string(url, website_data, {}, true) != DownloadResult::OK) + if(download_to_string(url, website_data, {}, true, fail_on_http_error) != DownloadResult::OK) return PluginResult::NET_ERR; QuickMediaHtmlSearch html_search; @@ -231,12 +232,12 @@ namespace QuickMedia { auto body = create_body(); body->items = std::move(chapters_items); - result_tabs.push_back(Tab{std::move(body), std::make_unique(program, title, url, manga_id_extractor, service_name, website_url, &list_page_query), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); + result_tabs.push_back(Tab{std::move(body), std::make_unique(program, title, url, manga_id_extractor, service_name, website_url, &list_page_query, fail_on_http_error), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); return PluginResult::OK; } PluginResult MangaGenericChaptersPage::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, service_name, website_url, list_page_query), nullptr}); + result_tabs.push_back(Tab{nullptr, std::make_unique(program, content_title, title, url, service_name, website_url, list_page_query, fail_on_http_error), nullptr}); return PluginResult::OK; } @@ -245,12 +246,12 @@ namespace QuickMedia { if(start_index == std::string::npos) return false; + start_index += strlen(manga_id_extractor.prefix); if(!manga_id_extractor.end) { manga_id = url.substr(start_index); return true; } - start_index += strlen(manga_id_extractor.prefix); size_t end_index = url.find(manga_id_extractor.end, start_index); if(end_index == std::string::npos) { manga_id = url.substr(start_index); @@ -261,9 +262,18 @@ namespace QuickMedia { return true; } + static bool is_number(const char *str) { + while(*str) { + char c = *str; + if(c < '0' || c > '9') + return false; + ++str; + } + return true; + } + ImageResult MangaGenericImagesPage::get_number_of_images(int &num_images) { num_images = 0; - chapter_num_pages = -1; switch(list_page_query->type) { case ListPageQueryType::IMAGES: { ImageResult result = get_page_image_urls(); @@ -292,7 +302,7 @@ namespace QuickMedia { HtmlPageCountUserdata page_count_userdata; page_count_userdata.num_pages = 0; page_count_userdata.field_name = list_page_pagination_query->pages_field_name; - page_count_userdata.field_contains = list_page_pagination_query->pages_field_contains; + page_count_userdata.field_contains = nullptr; HtmlPageImageUserdata page_image_userdata; page_image_userdata.url = ¤t_image_url; @@ -305,7 +315,7 @@ namespace QuickMedia { next_page_userdata.field_contains = list_page_pagination_query->next_page_field_contains; std::string website_data; - if(download_to_string(url, website_data, {}, true) != DownloadResult::OK) + if(download_to_string(url, website_data, {}, true, fail_on_http_error) != DownloadResult::OK) return ImageResult::NET_ERR; QuickMediaHtmlSearch html_search; @@ -317,15 +327,13 @@ namespace QuickMedia { [](QuickMediaHtmlNode *node, void *userdata) { HtmlPageCountUserdata *page_count_userdata = (HtmlPageCountUserdata*)userdata; const char *field1_value = html_attr_or_inner_text(node, page_count_userdata->field_name); - if(field1_value && (!page_count_userdata->field_contains || strstr(field1_value, page_count_userdata->field_contains))) { - page_count_userdata->num_pages++; + if(field1_value) { + std::string field_value_stripped = strip(field1_value); + if(is_number(field_value_stripped.c_str())) + page_count_userdata->num_pages = strtol(field_value_stripped.c_str(), nullptr, 10); } }, &page_count_userdata); - if(result == 0 && list_page_pagination_query->pages_post_handler) { - page_count_userdata.num_pages = list_page_pagination_query->pages_post_handler(page_count_userdata.num_pages); - } - if(result != 0 || page_count_userdata.num_pages == 0) { result = -1; goto cleanup; @@ -358,6 +366,12 @@ namespace QuickMedia { chapter_num_pages = num_images; return ImageResult::OK; } + case ListPageQueryType::CUSTOM: { + ImageResult result = get_page_image_urls(); + if(result != ImageResult::OK) return result; + num_images = chapter_image_urls.size(); + return ImageResult::OK; + } } return ImageResult::OK; } @@ -406,7 +420,10 @@ namespace QuickMedia { std::string image_src; std::string website_data; - if(download_to_string_cache(full_url, website_data, {}, true) != DownloadResult::OK) + DownloadErrorHandler error_callback = [](std::string&){ return true; }; + if(fail_on_http_error) + error_callback = nullptr; + if(download_to_string_cache(full_url, website_data, {}, true, error_callback) != DownloadResult::OK) return ImageResult::ERR; QuickMediaHtmlSearch html_search; @@ -431,46 +448,73 @@ namespace QuickMedia { return ImageResult::OK; } + case ListPageQueryType::CUSTOM: { + ImageResult result = get_page_image_urls(); + if(result != ImageResult::OK) return result; + for(const std::string &url : chapter_image_urls) { + if(!callback(url)) + break; + } + return ImageResult::OK; + } } return ImageResult::OK; } ImageResult MangaGenericImagesPage::get_page_image_urls() { - if(!prev_chapter_url.empty()) + if(!chapter_image_urls.empty()) return ImageResult::OK; - assert(list_page_query->type == ListPageQueryType::IMAGES); - const ListPageImagesQuery *list_page_images_query = &list_page_query->images_query; - if(!list_page_images_query->html_query || !list_page_images_query->field_name) { - assert(false); - return ImageResult::ERR; - } - - HtmlListPageImagesUserdata list_page_images_userdata; - list_page_images_userdata.urls = &chapter_image_urls; - list_page_images_userdata.field_name = list_page_images_query->field_name; - list_page_images_userdata.field_contains = list_page_images_query->field_contains; - std::string website_data; - if(download_to_string(url, website_data, {}, true) != DownloadResult::OK) + if(download_to_string(url, website_data, {}, true, fail_on_http_error) != 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; + if(list_page_query->type == ListPageQueryType::IMAGES) { + const ListPageImagesQuery *list_page_images_query = &list_page_query->images_query; + if(!list_page_images_query->html_query || !list_page_images_query->field_name) { + assert(false); + return ImageResult::ERR; + } - result = quickmedia_html_find_nodes_xpath(&html_search, list_page_images_query->html_query, - [](QuickMediaHtmlNode *node, void *userdata) { - HtmlListPageImagesUserdata *list_page_images_userdata = (HtmlListPageImagesUserdata*)userdata; - const char *field1_value = html_attr_or_inner_text(node, list_page_images_userdata->field_name); - if(field1_value && (!list_page_images_userdata->field_contains || strstr(field1_value, list_page_images_userdata->field_contains))) { - list_page_images_userdata->urls->push_back(strip(field1_value)); - } - }, &list_page_images_userdata); + HtmlListPageImagesUserdata list_page_images_userdata; + list_page_images_userdata.urls = &chapter_image_urls; + list_page_images_userdata.field_name = list_page_images_query->field_name; + list_page_images_userdata.field_contains = list_page_images_query->field_contains; + + 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, list_page_images_query->html_query, + [](QuickMediaHtmlNode *node, void *userdata) { + HtmlListPageImagesUserdata *list_page_images_userdata = (HtmlListPageImagesUserdata*)userdata; + const char *field1_value = html_attr_or_inner_text(node, list_page_images_userdata->field_name); + if(field1_value && (!list_page_images_userdata->field_contains || strstr(field1_value, list_page_images_userdata->field_contains))) { + list_page_images_userdata->urls->push_back(strip(field1_value)); + } + }, &list_page_images_userdata); - if(result == 0 && !chapter_image_urls.empty() && list_page_images_query->post_handler) - list_page_images_query->post_handler(chapter_image_urls); + if(result == 0 && !chapter_image_urls.empty() && list_page_images_query->post_handler) + list_page_images_query->post_handler(chapter_image_urls); + + cleanup: + quickmedia_html_search_deinit(&html_search); + if(result != 0 || chapter_image_urls.empty()) { + chapter_image_urls.clear(); + return ImageResult::ERR; + } + } else if(list_page_query->type == ListPageQueryType::CUSTOM) { + const ListPageCustomQuery *list_page_custom_query = &list_page_query->custom_query; + if(!list_page_custom_query->handler) { + assert(false); + return ImageResult::ERR; + } + chapter_image_urls = list_page_custom_query->handler(website_data); + } else { + assert(false); + return ImageResult::ERR; + } for(std::string &url : chapter_image_urls) { if(starts_with(url, "//")) @@ -479,21 +523,13 @@ namespace QuickMedia { url = website_url + url.substr(1); } - cleanup: - quickmedia_html_search_deinit(&html_search); - if(result != 0 || chapter_image_urls.empty()) { - chapter_image_urls.clear(); - return ImageResult::ERR; - } - - prev_chapter_url = url; + chapter_num_pages = chapter_image_urls.size(); return ImageResult::OK; } - MangaGenericSearchPage& MangaGenericSearchPage::search_handler(const char *search_prefix, const char *page_prefix, int page_start) { - search_query.search_prefix = search_prefix; - search_query.page_prefix = page_prefix; + MangaGenericSearchPage& MangaGenericSearchPage::search_handler(const char *search_template, int page_start) { + search_query.search_template = search_template; search_query.page_start = page_start; return *this; } @@ -538,16 +574,13 @@ namespace QuickMedia { } MangaGenericSearchPage& MangaGenericSearchPage::list_page_images_pagination_handler( - const char *pages_html_query, const char *pages_field_name, const char *pages_field_contains, ListPagePaginationPagesPost pages_post_handler, + const char *pages_html_query, const char *pages_field_name, const char *image_html_query, const char *image_field_name, const char *image_field_contains, const char *next_page_html_query, const char *next_page_field_name, const char *next_page_field_contains) { - assert(pages_post_handler); list_page_query.type = ListPageQueryType::PAGINATION; list_page_query.pagination_query.pages_html_query = pages_html_query; list_page_query.pagination_query.pages_field_name = pages_field_name; - list_page_query.pagination_query.pages_field_contains = pages_field_contains; - list_page_query.pagination_query.pages_post_handler = pages_post_handler; list_page_query.pagination_query.image_html_query = image_html_query; list_page_query.pagination_query.image_field_name = image_field_name; @@ -559,6 +592,13 @@ namespace QuickMedia { return *this; } + MangaGenericSearchPage& MangaGenericSearchPage::list_page_images_custom_handler(ListPageCustomHandler handler) { + assert(handler); + list_page_query.type = ListPageQueryType::CUSTOM; + list_page_query.custom_query.handler = handler; + return *this; + } + MangaGenericSearchPage& MangaGenericSearchPage::manga_id_handler(const char *prefix, const char *end) { manga_id_extractor.prefix = prefix; manga_id_extractor.end = end; -- cgit v1.2.3