UNPKG

cejs

Version:

A JavaScript module framework that is simple to use.

318 lines (258 loc) 11.3 kB
/** * @name CeL module for downloading hhcool comics. * * @fileoverview 本檔案包含了解析並處理、批量下載 汗汗酷漫 漫畫 的工具。 * * <code> CeL.hhcool(configuration, function(crawler) { start_crawler(crawler, typeof module === 'object' && module); }, function(crawler) { setup_crawler(crawler, typeof module === 'object' && module); }); </code> * * using zh-cmn-Hant-CN .aspx * * TODO: http://coco.hhxxee.com/ http://99.hhxxee.com/ http://99770.hhxxee.com/ * * @since 2019/4/25 5:6:7 模組化。 */ // More examples: // @see // https://github.com/kanasimi/work_crawler/blob/master/comic.cmn-Hans-CN/hhcool.js 'use strict'; // -------------------------------------------------------------------------------------------- // 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。 typeof CeL === 'function' && CeL.run({ // module name name : 'application.net.work_crawler.sites.hhcool', require : 'application.net.work_crawler.', // 設定不匯出的子函式。 no_extend : '*', // 為了方便格式化程式碼,因此將 module 函式主體另外抽出。 code : module_code }); function module_code(library_namespace) { // requiring // -------------------------------------------------------------------------------------------- /** * e.g., <code> // 汗汗酷漫 <li><a title='野生的最终BOSS出现了' href='/manhua/32449.html'><img src='http://img.94201314.net/comicui/32449.JPG'><br>野生的最终BOSS出现了</a></li> // 動漫伊甸園 <li><a title='樹鶯吟' target=_blank href='/comicinfo/37621.html'><img src='http://img.94201314.net/comicui/37621.JPG'><br>樹鶯吟</a></li> </code> */ var PATTERN_search = /<li><a title='([^<>'"]+)'[^<>]*? href='\/[^\/]+\/(\d+).html'>.+?<\/li>/g; /** * e.g., <code> <div class='cVolTag'>周刊杂志每周每月连载单集</div><ul class='cVolUl'><li>...</a></li></ul> <div class='cVolTag'>漫画正片外的剧情之番外篇</div><ul class='cVolUl'><li>...</a></li></ul> <li><a class='l_s' href='/cool282192/1.html?s=7' target='_blank' title='双星之阴阳师09卷'>双星之阴阳师09卷</a></li> </code> */ // matched: [all, part_title, url, title, inner] var PATTERN_chapter = /<div class='cVolTag'>([^<>]+)|<li><a [^<>]*?href='([^'<>]+)'[^<>]*? title='([^'<>]+)'[^<>]*>(.+?)<\/a>/g; var PATTERN_image = /<img (?:.*?) name="([^<>"]+)" (?:.*?)hdNextImg" value="([^<>"]+)"/; var default_configuration = { // 所有的子檔案要修訂註解說明時,應該都要順便更改在CeL.application.net.comic中Comic_site.prototype內的母comments,並以其為主體。 // 本站常常無法取得圖片,因此得多重新檢查。 // 當有多個分部的時候才重新檢查。 recheck : 'multi_parts_changed', // 當無法取得chapter資料時,直接嘗試下一章節。在手動+監視下recheck時可併用此項。 // skip_chapter_data_error : true, // 當圖像不存在 EOI (end of image) 標記,或是被偵測出非圖像時,依舊強制儲存檔案。 // allow image without EOI (end of image) mark. default:false allow_EOI_error : true, // 當圖像檔案過小,或是被偵測出非圖像(如不具有EOI)時,依舊強制儲存檔案。 // skip_error : true, // 最小容許圖案檔案大小 (bytes)。 // 對於極少出現錯誤的網站,可以設定一個比較小的數值,並且設定.allow_EOI_error=false。因為這類型的網站要不是無法取得檔案,要不就是能夠取得完整的檔案;要取得破損檔案,並且已通過EOI測試的機會比較少。 // 對於有些圖片只有一條細橫桿的情況。 MIN_LENGTH : 400, // one_by_one : true, // base_URL : '', // /manhua/ base_comic_path : 'manhua', // 解析 作品名稱 → 作品id get_work() search_URL : 'comic/?act=search&st=', parse_search_result : function(html) { html = html.between('<div class="cComicList">', '</div>'); var id_list = [], id_data = [], matched; while (matched = PATTERN_search.exec(html)) { id_list.push(+matched[2]); id_data.push(matched[1]); } return [ id_list, id_data ]; }, // 取得作品的章節資料。 get_work_data() work_URL : function(work_id) { // e.g., http://www.hhcool.com/manhua/32449.html return this.base_comic_path + '/' + work_id + '.html'; }, parse_work_data : function(html, get_label, extract_work_data) { html = html.between('<div id="about_kit">', '<div class="cVolList">'); html = html.between(null, '<div class="cInfoAct">') || html; var work_data = { // 必要屬性:須配合網站平台更改。 title : get_label(html.between('<h1>', '</h1>')) // 選擇性屬性:須配合網站平台更改。 // <meta property="og:novel:status" content="已完结"/> }; extract_work_data(work_data, html, /<li>([^:]+)(.+?)<\/li>/g); work_data.status = work_data.状态; work_data.last_update = work_data.更新; return work_data; }, get_chapter_list : function(work_data, html, get_label) { html = html.between('<div class="cVolList">', '<div id="foot">'); work_data.chapter_list = []; // 漫畫目錄名稱不須包含分部號碼。使章節目錄名稱不包含 part_NO。 // 將會在 function get_chapter_directory_name() 自動設定。 // work_data.chapter_list.add_part_NO = false; // 轉成由舊至新之順序。 work_data.inverted_order = true; var matched; while (matched = PATTERN_chapter.exec(html)) { // delete matched.input; // console.log(matched); if (matched[1]) { this.set_part(work_data, get_label(matched[1])); continue; } this.add_chapter(work_data, { title : get_label(matched[3].replace(work_data.title, '')), url : matched[2] }); } // console.log(work_data.chapter_list); }, pre_parse_chapter_data // 執行在解析章節資料 process_chapter_data() 之前的作業 (async)。 // 必須自行保證執行 callback(),不丟出異常、中斷。 : function(XMLHttp, work_data, callback, chapter_NO) { var html = XMLHttp.responseText; var chapter_list = [], URL = XMLHttp.responseURL, // 每一張圖片都得要從載入的頁面獲得資訊。 matched, PATTERN = /csel2\((\d{1,3})\)/g; while (matched = PATTERN.exec(html)) { chapter_list.push(matched[1]); } work_data.cache_directory = work_data.directory + this.cache_directory_name; library_namespace.create_directory(work_data.cache_directory); if (!work_data.image_list) { // image_list[chapter_NO] = [url, url, ...] work_data.image_list = []; } var _this = this, // this_image_list = work_data.image_list[chapter_NO] = []; function for_each_chapter(run_next, NO, index) { var url = URL.replace(/\/\d{1,3}\.html/, '/' + NO + '.html'), // save_to = work_data.cache_directory + chapter_NO.pad(work_data.chapter_NO_pad_digits || 3) + '-' + NO.pad(work_data.chapter_NO_pad_digits || 3) + '.html'; function for_each_image_page(html, error) { if (error) { library_namespace.error({ // gettext_config:{"id":"an-error-occurred-while-downloading-and-the-file-contents-could-not-be-obtained-smoothly"} T : '下載時發生錯誤,無法順利取得檔案內容!' }); library_namespace.error(error); _this.onerror(error); return; } var image_data = html.match(PATTERN_image); // decode chapter image url data image_data = [ unsuan(image_data[1]), unsuan(image_data[2]) ]; if (image_data[0] !== '\x00') { if (!this_image_list[index]) { this_image_list[index] = image_data[0]; } else if (this_image_list[index] !== image_data[0]) { _this.onerror([ { // gettext_config:{"id":"different-url-$1-≠-$2"} T : [ 'Different url: %1 ≠ %2', // this_image_list[index], image_data[0] ] }, '\n', { // gettext_config:{"id":"maybe-the-downloaded-file-has-an-error?-you-can-try-to-download-it-later-or-use-the-.recheck-option-to-ignore-the-cache-and-re-download-the-page-for-each-image"} T : '或許是下載的檔案出現錯誤?您可嘗試過段時間再下載,或選用 .recheck 選項來忽略快取、重新下載每個圖片的頁面。' } ]); run_next(); return; } } if (image_data[1] !== '\x00') { this_image_list[index + 1] = image_data[1]; } // console.log([ index, image_data ]) run_next(); } // 沒 cache 的話,每一次都要重新取得每個圖片的頁面,速度比較慢。 library_namespace.get_URL_cache(url, for_each_image_page, { get_URL_options : _this.get_URL_options, no_write_info : true, file_name : save_to, reget : _this.recheck }); } chapter_list.run_serial(for_each_chapter, callback); }, parse_chapter_data : function(html, work_data, get_label, chapter_NO) { var PATTERN = / id="hdDomain"(?:.*?) value="([^<>"]+)"/, // 不同作品放在不同的location。 matched = html.match(PATTERN); this.server_list = matched[1].split('|'); var chapter_data = work_data.chapter_list[chapter_NO - 1]; // console.log(work_data.image_list[chapter_NO]); chapter_data.image_list = work_data.image_list[chapter_NO] .map(function(url) { return encodeURI(library_namespace.HTML_to_Unicode(url)); }); return chapter_data; } }; function unsuan(s) { var x = s.substring(s.length - 1); var w = "abcdefghijklmnopqrstuvwxyz"; var xi = w.indexOf(x) + 1; var sk = s.substring(s.length - xi - 12, s.length - xi - 1); s = s.substring(0, s.length - xi - 12); var k = sk.substring(0, sk.length - 1); var f = sk.substring(sk.length - 1); for (var i = 0; i < k.length; i++) { eval("s=s.replace(/" + k.substring(i, i + 1) + "/g,'" + i + "')"); } var ss = s.split(f); s = ""; for (i = 0; i < ss.length; i++) { s += String.fromCharCode(ss[i]); } return s; } // -------------------------------------------------------------------------------------------- function new_hhcool_comics_crawler(configuration, callback, initializer) { configuration = configuration ? Object.assign(Object.create(null), default_configuration, configuration) : default_configuration; // 每次呼叫皆創建一個新的實體。 var crawler = new library_namespace.work_crawler(configuration); if (typeof initializer === 'function') { initializer(crawler); } var decode_filename = 'script/view.js', unsuan; library_namespace.get_URL_cache(crawler.base_URL + decode_filename, // function(contents, error) { if (false) { eval('unsuan=function' + contents.between('function unsuan', '\nvar')); } callback(crawler); }, crawler.main_directory + decode_filename.match(/[^\\\/]+$/)[0]); } return new_hhcool_comics_crawler; }