copymanga.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import {Spider} from "./spider.js";
  2. import {BookDetail, BookShort} from "../lib/book.js";
  3. import {Crypto} from "../lib/cat.js";
  4. class CopyManhuaSpider extends Spider {
  5. constructor() {
  6. super();
  7. this.siteUrl = 'https://www.copymanga.tv';
  8. }
  9. getName() {
  10. return "🧑‍🎨|拷贝漫画|🧑‍🎨"
  11. }
  12. getAppName() {
  13. return "拷贝漫画"
  14. }
  15. getJSName() {
  16. return "copymanga"
  17. }
  18. getType() {
  19. return 20
  20. }
  21. async setClasses() {
  22. this.classes.push(this.getTypeDic("全部", "c1"))
  23. }
  24. async getFilter($) {
  25. let region = {
  26. key: 'region', name: '地區', init: '',
  27. };
  28. let regionValues = [];
  29. regionValues.push({n: '全部', v: ''});
  30. regionValues.push({n: '日漫', v: '0'});
  31. regionValues.push({n: '韓漫', v: '1'});
  32. regionValues.push({n: '美漫', v: '2'});
  33. region['value'] = regionValues;
  34. let ordering = {
  35. key: 'ordering', name: '排序', init: '-datetime_updated',
  36. };
  37. let orderingValues = [];
  38. orderingValues.push({n: '更新時間↓', v: '-datetime_updated'});
  39. orderingValues.push({n: '更新時間↑', v: 'datetime_updated'});
  40. orderingValues.push({n: '熱門↓', v: '-popular'});
  41. orderingValues.push({n: '熱門↑', v: 'popular'});
  42. ordering['value'] = orderingValues;
  43. let status = {
  44. key: 'sort', name: '狀態', init: '',
  45. };
  46. let statusValues = [];
  47. statusValues.push({n: '全部', v: ''});
  48. statusValues.push({n: '連載中', v: '0'});
  49. statusValues.push({n: '已完結', v: '1'});
  50. statusValues.push({n: '短篇', v: '2'});
  51. status['value'] = statusValues;
  52. let extend_list = []
  53. let themeValues = [{n: '全部', v: ''}];
  54. for (const a of $('div.classify-right>a[href*="theme="]')) {
  55. themeValues.push({
  56. n: $(a).text().trim(), v: a.attribs.href.match(/.*?theme=(.*)&/)[1],
  57. });
  58. }
  59. extend_list.push({
  60. key: 'theme', name: '', init: '', wrap: 1, value: themeValues,
  61. });
  62. extend_list.push(region);
  63. extend_list.push(status);
  64. extend_list.push(ordering);
  65. return extend_list
  66. }
  67. async setFilterObj() {
  68. let $ = await this.getHtml(this.siteUrl + '/comics');
  69. this.filterObj['c1'] = await this.getFilter($);
  70. }
  71. parseVodShortFromJson(obj) {
  72. let bookShort = new BookShort()
  73. bookShort.book_id = obj["path_word"]
  74. bookShort.book_name = obj["name"]
  75. bookShort.book_pic = obj["cover"]
  76. bookShort.book_remarks = obj.author ? obj.author[0].name : '';
  77. return bookShort
  78. }
  79. async parseVodShortListFromDocByCategory($) {
  80. const list = eval($('div[class="row exemptComic-box"]')[0].attribs.list);
  81. let books = [];
  82. for (const book of list) {
  83. let bookShort = this.parseVodShortFromJson(book)
  84. books.push(bookShort)
  85. }
  86. return books
  87. }
  88. async parseVodShortListFromDoc($) {
  89. let vodElements = $("[class=\"container edit\"]").find("[class=\"col-auto\"]")
  90. let books = []
  91. for (const vodElement of vodElements) {
  92. let bookShort = new BookShort()
  93. bookShort.book_id = $(vodElement).find("a")[0].attribs.href.split("/comic/")[1]
  94. bookShort.book_pic = $(vodElement).find("img")[0].attribs["data-src"]
  95. bookShort.book_name = $($(vodElement).find("p")).text()
  96. books.push(bookShort)
  97. }
  98. return books
  99. }
  100. async parseVodDetailFromDoc($, id) {
  101. let bookDetail = new BookDetail()
  102. bookDetail.book_pic = $("[class=\"comicParticulars-left-img loadingIcon\"]").find("img")[0].attribs["data-src"]
  103. bookDetail.book_name = $('h6').text().trim()
  104. bookDetail.book_director = $('span.comicParticulars-right-txt>a[href*="/author/"]')
  105. .map((_, a) => $(a).text().trim())
  106. .get()
  107. .join('/')
  108. bookDetail.book_content = $('p.intro').text().trim()
  109. let data = JSON.parse(await this.fetch(this.siteUrl + `/comicdetail/${id}/chapters`, null, this.getHeader()))["results"]
  110. let key = Crypto.enc.Utf8.parse('xxxmanga.woo.key');
  111. let iv = Crypto.enc.Utf8.parse(data.substr(0, 16));
  112. let src = Crypto.enc.Hex.parse(data.substr(16));
  113. let dst = Crypto.AES.decrypt({ciphertext: src}, key, {iv: iv, padding: Crypto.pad.Pkcs7});
  114. dst = Crypto.enc.Utf8.stringify(dst);
  115. const groups = JSON.parse(dst).groups;
  116. let urls = groups.default["chapters"]
  117. .map((c) => {
  118. return c.name + '$' + id + '|' + c.id;
  119. })
  120. .join('#');
  121. bookDetail.volumes = '默認';
  122. bookDetail.urls = urls;
  123. bookDetail.book_id = id
  124. return bookDetail
  125. }
  126. async parseVodShortListFromJson(obj) {
  127. const books = [];
  128. for (const book of obj) {
  129. books.push(this.parseVodShortFromJson(book))
  130. }
  131. return books
  132. }
  133. async setHomeVod() {
  134. let $ = await this.getHtml(this.siteUrl)
  135. this.homeVodList = await this.parseVodShortListFromDoc($)
  136. }
  137. async setCategory(tid, pg, filter, extend) {
  138. let page = pg || 1;
  139. if (page === 0) page = 1;
  140. let link = this.siteUrl + `/comics?theme=${extend.theme || ''}&region=${extend.region || ''}&status=${extend.status || ''}&ordering=${extend.ordering || '-datetime_updated'}`;
  141. if (page > 1) {
  142. link += '&offset=' + (page - 1) * 50 + '&limit=50';
  143. }
  144. let $ = await this.getHtml(link)
  145. this.vodList = await this.parseVodShortListFromDocByCategory($)
  146. }
  147. async setDetail(id) {
  148. let $ = await this.getHtml(this.siteUrl + `/comic/${id}`)
  149. this.vodDetail = await this.parseVodDetailFromDoc($, id)
  150. }
  151. async setPlay(flag, id, flags) {
  152. let info = id.split('|');
  153. let $ = await this.getHtml(this.siteUrl + `/comic/${info[0]}/chapter/${info[1]}`);
  154. const data = $('div.imageData')[0].attribs["contentkey"];
  155. let key = Crypto.enc.Utf8.parse('xxxmanga.woo.key');
  156. let iv = Crypto.enc.Utf8.parse(data.substr(0, 16));
  157. let src = Crypto.enc.Hex.parse(data.substr(16));
  158. let dst = Crypto.AES.decrypt({ciphertext: src}, key, {iv: iv, padding: Crypto.pad.Pkcs7});
  159. dst = Crypto.enc.Utf8.stringify(dst);
  160. const list = JSON.parse(dst);
  161. let content = [];
  162. for (let index = 0; index < list.length; index++) {
  163. const element = list[index];
  164. content[index] = element.url;
  165. }
  166. this.playUrl = {
  167. "content": content,
  168. }
  169. }
  170. async setSearch(wd, quick) {
  171. let page = 1
  172. const link = `${this.siteUrl}/api/kb/web/searcha/comics?offset=${page > 1 ? ((page - 1) * 12).toString() : ''}&platform=2&limit=12&q=${wd}&q_type=`;
  173. let list = JSON.parse(await this.fetch(link, null, this.getHeader()))["results"]["list"]
  174. this.vodList = await this.parseVodShortListFromJson(list)
  175. }
  176. }
  177. let spider = new CopyManhuaSpider()
  178. async function init(cfg) {
  179. await spider.init(cfg)
  180. }
  181. async function home(filter) {
  182. return await spider.home(filter)
  183. }
  184. async function homeVod() {
  185. return await spider.homeVod()
  186. }
  187. async function category(tid, pg, filter, extend) {
  188. return await spider.category(tid, pg, filter, extend)
  189. }
  190. async function detail(id) {
  191. return await spider.detail(id)
  192. }
  193. async function play(flag, id, flags) {
  194. return await spider.play(flag, id, flags)
  195. }
  196. async function search(wd, quick) {
  197. return await spider.search(wd, quick)
  198. }
  199. export function __jsEvalReturn() {
  200. return {
  201. init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
  202. };
  203. }
  204. export {spider}