diff --git a/.gitignore b/.gitignore index 73fa35d75aa..49386fa0745 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ cache /redist/*.zip /test/*.log junit.xml -.idea/ \ No newline at end of file +.idea/ +.vs diff --git a/src/web/img/connectors/beautymanga b/src/web/img/connectors/beautymanga new file mode 100644 index 00000000000..72bb76c5088 Binary files /dev/null and b/src/web/img/connectors/beautymanga differ diff --git a/src/web/mjs/connectors/BeautyManga.mjs b/src/web/mjs/connectors/BeautyManga.mjs new file mode 100644 index 00000000000..1f1c97ba558 --- /dev/null +++ b/src/web/mjs/connectors/BeautyManga.mjs @@ -0,0 +1,53 @@ +import WordPressMadara from './templates/WordPressMadara.mjs'; +export default class BeautyManga extends WordPressMadara { + constructor() { + super(); + super.id = 'beautymanga'; + super.label = 'BeautyManga'; + this.tags = [ 'manga', 'webtoon', 'english' ]; + this.url = 'https://mangadex.today'; + this.path = '/popular-manga'; + this.queryMangas = 'div.item-thumb.hover-details.c-image-hover a'; + this.queryMangasPageCount = 'ul.pagination li:nth-last-of-type(2) a'; + this.pathMangas = '?page=%PAGE%'; + this.queryPages = 'p#arraydata'; + this.mangaTitleFilter = ''; + } + async _getMangasFromPage(page) { + let uri = new URL(this.path + this.pathMangas.replace('%PAGE%', page), this.url); + uri.pathname = uri.pathname.replace(/\/+/g, '/'); + let request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, this.queryMangas); + return data.map(element => { + return { + id: new URL(element.href, request.url).pathname, + title: element.title.replace(this.mangaTitleFilter, '').trim() + }; + }); + } + async _getChaptersAjaxOld(mangaID) { + const uri = new URL('/ajax-list-chapter?mangaID='+mangaID, this.url); + const request = new Request(uri, { + method: 'GET' + }); + const data = await this.fetchDOM(request, this.queryChapters); + if (data.length) { + return data; + } else { + throw new Error('No chapters found (new ajax endpoint)!'); + } + } + async _getPages(chapter) { + let uri = new URL(chapter.id, this.url); + let request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, this.queryPages); + let el = data[0].innerText.split(','); + return el.map(element => { + const uri = new URL(this.getAbsolutePath(element, request.url)); + return this.createConnectorURI({ + url: uri.href, + referer: uri.origin + }); + }); + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/BookWalker.mjs b/src/web/mjs/connectors/BookWalker.mjs new file mode 100644 index 00000000000..bb645386689 --- /dev/null +++ b/src/web/mjs/connectors/BookWalker.mjs @@ -0,0 +1,154 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; + +export default class BookWalker extends Connector { + + constructor() { + super(); + super.id = 'bookwalker'; + super.label = 'BookWalker'; + this.tags = [ 'manga', 'japanese' ]; + this.url = 'https://global.bookwalker.jp'; + } + + async _getMangaFromURI(uri) { + let id = uri.pathname; + let request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, 'div.detail-book-title h1[itemprop="name"]'); + let title = data[0].textContent.trim(); + return new Manga(this, id, title); + } + + async _getChapters(manga) { + const url = new URL(manga.id, this.url); + url.searchParams.set('sample', '2'); + return [{id : url.pathname+url.search, title : 'Free Sample',}]; + } + + async _getPages(chapter) { + const script = ` + new Promise((resolve, reject) => { + setTimeout(async () => { + + const a2f = NFBR.a2F ? new NFBR.a2F() : new NFBR.a2f(); + const params = new URL(window.location).searchParams; + const parameters = await a2f.a5W({ + contentId: params.get(NFBR.a5q.Key.CONTENT_ID), // Content ID => 'cid' + a6m: params.get(NFBR.a5q.Key.LICENSE_TOKEN), // License Token => 'lit' + preview: params.get(NFBR.a5q.Key.LOOK_INSIDE) === '1', // Look Inside => 'lin' + contentType: params.get(NFBR.a5q.Key.CONTENT_TYPE) || 1, // Content Type => 'cty' + title: params.get(NFBR.a5q.Key.CONTENT_TITLE), // Content Title => 'cti' + winWidth: 3840, + winHeight: 2160 + }); + + //Create a model + const model = new NFBR.a6G.Model({ + 'settings': new self.NFBR.Settings('NFBR.SettingData'), + 'viewerFontSize': self.NFBR.a0X.a3k, + 'viewerFontFace': self.NFBR.a0X.a3k, + 'viewerSpreadDouble': true, + 'viewerb5c': null, + 'viewerSpread': {}, + 'queryParamForContentUrl' : parameters.contentAppendParam, + }); + //Create the bookloader + const bl = new NFBR.a5n(); + bl.B0a = "normal_default"; + + //Create a "Content" that will be filled using the bookloader as5 async function + let data = new NFBR.a6i.Content(parameters.url); + let v_a6L = new NFBR.a6G.a6L(model); + await bl.a5s(data, "configuration", v_a6L); + + //console.log(data); + + //data is now our JSON with all the infos + const pages = data.configuration.contents.map((page, index) => { + + let mode = 'raw'; + let extension = '.jpeg'; + + //*****************/ + //GETTING PAGE URL + //*****************/ + //Create a Page + const fPage = new NFBR.a6i.Page(index, page.file, "0", extension, ""); + const realURL = v_a6L.getPageContentUrl_(data, fPage); + + + //*****************************/ + //GETTING IMAGE SCRAMBLE DATA + //*****************************/ + + //Fill infos in the page for a7b to work + const fileinfos = data.files[index].FileLinkInfo.PageLinkInfoList[0].Page; + fPage.width = fileinfos.Size.Width; + fPage.height = fileinfos.Size.Height; + fPage.info = fileinfos; + //Fill more infos needed for unscrambling + fPage.A3j(data); + + let blocks = []; + if (fileinfos.BlockHeight) //if we have a block size for the page, its a puzzle ! + { + mode = 'puzzle'; + blocks = window.NFBR.a6G.a5x.prototype.R8x(fPage, fPage.width, fPage.height) + } + + return { + mode: mode, + imageUrl: realURL, + encryption: { + blocks: JSON.stringify(blocks),//stringify the array greatly speed up createConnectorURI + } + }; + }); + + resolve(pages); + + + }, 1000); + }); + `; + const uri = new URL( chapter.id, this.url ); + const request = new Request( uri.href, this.requestOptions ); + const data = await Engine.Request.fetchUI(request, script); + return data.map(page => page.mode == 'raw' ? page.imageUrl : this.createConnectorURI(page)); + } + + async _handleConnectorURI(payload) { + const uri = new URL(payload.imageUrl, this.url); + const request = new Request(uri, this.requestOptions); + const response = await fetch(request); + switch (payload.mode) { + case 'puzzle': { + let data = await response.blob(); + data = await this._descrambleImage(data, payload.encryption.blocks); + return this._blobToBuffer(data); + } + default: { + let data = await response.blob(); + return this._blobToBuffer(data); + } + } + } + + async _descrambleImage(scrambled, blocks) { + let bitmap = await createImageBitmap(scrambled); + return new Promise(resolve => { + let canvas = document.createElement('canvas'); + canvas.width = bitmap.width; + canvas.height = bitmap.height; + var ctx = canvas.getContext('2d'); + const blockz = JSON.parse(blocks); + for (let q of blockz) { + ctx.drawImage(bitmap, q.destX, q.destY, q.width, q.height, q.srcX, q.srcY, q.width, q.height); + } + canvas.toBlob(data => { + resolve(data); + }, Engine.Settings.recompressionFormat.value, parseFloat(Engine.Settings.recompressionQuality.value)/100); + } ); + } + +} \ No newline at end of file diff --git a/src/web/mjs/connectors/Cuutruyen.mjs b/src/web/mjs/connectors/Cuutruyen.mjs new file mode 100644 index 00000000000..82a18084dce --- /dev/null +++ b/src/web/mjs/connectors/Cuutruyen.mjs @@ -0,0 +1,135 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; + +export default class CuuTruyen extends Connector { + constructor() { + super(); + super.id = 'cuutruyen'; + super.label = 'Cứu Truyện'; + this.tags = ['manga', 'vietnamese']; + this.url = 'https://cuutruyen.net'; + } + + async _getMangaFromURI(uri) { + const mangaid = uri.href.match(/\/mangas\/([0-9]+)/)[1]; + const req = new URL(`/api/v2/mangas/${mangaid}`, this.url); + const request = new Request(req, this.requestOptions); + const { data: { name } } = await this.fetchJSON(request); + return new Manga(this, mangaid, name.trim()); + } + + async _getMangas() { + const uri = new URL('/api/v2/mangas/recently_updated?page=1&per_page=30', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + const pages = data._metadata.total_pages; + + const mangaList = this._getMangasFromPage(data); + + for (let page = 2; page <= pages; page++) { + const uri = new URL(`/api/v2/mangas/recently_updated?page=${page}&per_page=30`, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + const mangas = this._getMangasFromPage(data); + mangaList.push(...mangas); + } + return mangaList; + } + + _getMangasFromPage(data) { + return data.data.map((c) => ({ + id: c.id, + title: c.name.trim(), + })); + } + + async _getChapters(manga) { + const uri = new URL(`/api/v2/mangas/${manga.id}/chapters`, this.url); + const request = new Request(uri, this.requestOptions); + const { data } = await this.fetchJSON(request); + return data + .filter((chapter) => chapter.status === 'processed') + .map((chapter) => { + let title = `Chapter ${chapter.number}`; + + if (chapter.name) { + title += `: ${chapter.name}`; + } + + return { id: chapter.id, title }; + }); + } + + async _getPages(chapter) { + const uri = new URL('/api/v2/chapters/' + chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + const { data: { pages } } = await this.fetchJSON(request); + + if (pages.some((image) => image.status !== 'processed')) { + throw new Error('This chapter is still processing, please try again later.'); + } + + return pages.map((image) => { + return this.createConnectorURI({ + url: image.image_url, + drmData: image.drm_data, + }); + }); + } + + async _handleConnectorURI(payload) { + const response = await fetch(payload.url, { + cache: 'no-cache', + referrer: `${this.url}/`, + headers: { + 'accept': 'image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5', + }, + }); + + if (!payload.drmData) { + return this._blobToBuffer(await response.blob()); + } + + const decryptedDrmData = this.decodeXorCipher(atob(payload.drmData), '3141592653589793'); + + if (!decryptedDrmData.startsWith('#v4|')) { + throw new Error(`Invalid DRM data (does not start with magic bytes): ${decryptedDrmData}`); + } + + const image = await createImageBitmap(await response.blob()); + const canvas = document.createElement('canvas'); + + canvas.width = image.width; + canvas.height = image.height; + + const ctx = canvas.getContext('2d'); + let sy = 0; + + for (const t of decryptedDrmData.split('|').slice(1)) { + const [dy, height] = t.split('-', 2).map(Number); + + ctx.drawImage(image, 0, sy, image.width, height, 0, dy, image.width, height); + sy += height; + } + + return this._blobToBuffer(await this._canvasToBlob(canvas)); + } + + _canvasToBlob(canvas) { + return new Promise(resolve => { + canvas.toBlob(data => { + resolve(data); + }, Engine.Settings.recompressionFormat.value, parseFloat(Engine.Settings.recompressionQuality.value) / 100); + }); + } + + decodeXorCipher(data, key) { + let output = ""; + + for (let i = 0; i < data.length; i++) { + output += String.fromCharCode(data.charCodeAt(i) ^ key.charCodeAt(i % key.length)); + } + + return output; + } +} diff --git a/src/web/mjs/connectors/INKR.mjs b/src/web/mjs/connectors/INKR.mjs new file mode 100644 index 00000000000..0b7bd7e0c11 --- /dev/null +++ b/src/web/mjs/connectors/INKR.mjs @@ -0,0 +1,402 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; + +export default class INKR extends Connector { + constructor() { + super(); + super.id = 'inkr'; + super.label = 'INKR'; + this.tags = [ 'manga', 'english' ]; + this.url = 'https://comics.inkr.com'; + this.nextInstance = 'b023df32bc9708e85db0b51983a2e76fce7be924'; + + } + + async _initializeConnector() { + const uri = new URL(this.url); + const request = new Request(uri.href, this.requestOptions); + this.nextInstance = await Engine.Request.fetchUI(request, `__NEXT_DATA__.buildId`); + } + + async _getMangaFromURI(uri) { + const id = uri.pathname.match(/\/title\/([^/]+)/)[1]; + const request = new Request(uri, this.requestOptions); + const data =await this.fetchDOM(request, 'meta[property="og:image:alt"]'); + const title = data[0].content.trim(); + return new Manga(this, id, title); + } + async _getMangas() { + const uri = new URL(`/_next/data/${this.nextInstance}/manga/list/all.json`, this.url); + const request = new Request(uri, this.requestOptions); + let data = await this.fetchJSON(request); + data = new LZSTRING().decompressFromBase64(data.pageProps._c); + data = JSON.parse(data); + const obj = unpacker.unpack(data[0], data[1]); + const mangas = obj.icd.filter(element => element[0].startsWith('ik-title')); + console.log(mangas); + return mangas.map(element => { + return { + id : element[0], + title : element[1].name.trim() + }; + }); + + } + async _getChapters(manga) { + //https://comics.inkr.com/_next/data/b023df32bc9708e85db0b51983a2e76fce7be924/title/1227-a-sign-of-affection/chapters.json + const mangaid = manga.id.match(/ik-title-(\d+)$/)[1]; + const uri = new URL(`/_next/data/${this.nextInstance}/title/${mangaid}/chapters.json`, this.url); + const request = new Request(uri, this.requestOptions); + let data = await this.fetchJSON(request); + data = new LZSTRING().decompressFromBase64(data.pageProps._c); + data = JSON.parse(data); + const obj = unpacker.unpack(data[0], data[1]); + + const chaptersIndexes = obj.icd.find(element => element[0].startsWith('ik-title'))[1].chapterList; + const chapters = obj.icd.filter(element => chaptersIndexes.includes(element[0])); + + return chapters + .sort(function(a, b) { + return a[1].order - b[1].order; + }) + .map(chapter => { + return{ + id: chapter[1].oid.match(/\d+$/), + title : chapter[1].name.trim() + }; + }); + + } + async _getPages(chapter) { + const url = new URL(`/title/${chapter.manga.id}/chapter/${chapter.id}`, this.url); + let data = await this._getNextData(url); + + data = new LZSTRING().decompressFromBase64(data.props.pageProps._c); + data = JSON.parse(data); + const obj = unpacker.unpack(data[0], data[1]); + + const ikchapter = 'ik-chapter-'+ chapter.id; + const chap = obj.icd.find(element=> element[0] == ikchapter); + return !chap[1].chapterPages ? [] : chap[1].chapterPages.map(page => this.createConnectorURI(page)); + } + + async _handleConnectorURI(payload) { + const response = await fetch(new Request(payload.url + '/w1600.ikc'), { + headers: { + 'ikc-platform': 'android', + 'cf-ipcountry': 'VN', + 'user-agent': 'okhttp/4.9.1', + }, + }); + let encryptedData = new Uint8Array(await response.arrayBuffer()); + + const iv = CryptoJS.lib.WordArray.create(encryptedData.slice(4, 20)); + const ciphertext = CryptoJS.lib.WordArray.create(encryptedData.slice(20)); + + const key = CryptoJS.enc.Hex.parse('454d514b6377597151746c4832394b7a535a73446f62484c316d48767a6f746c'); + + const decrypted = CryptoJS.AES.decrypt({ ciphertext }, key, { + iv, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.NoPadding, + }); + + const buffer = { + mimeType: response.headers.get('content-type'), + data : this.convertWordArrayToUint8Array(decrypted) + }; + + this._applyRealMime(buffer); + return buffer; + } + + convertWordArrayToUint8Array (wordArray) { + var len = wordArray.words.length, + u8_array = new Uint8Array(len << 2), + offset = 0, + word, + i; + for (i = 0; i < len; i++) { + word = wordArray.words[i]; + u8_array[offset++] = word >> 24; + u8_array[offset++] = word >> 16 & 255; + u8_array[offset++] = word >> 8 & 255; + u8_array[offset++] = word & 255; + } + return u8_array; + } + + async _getNextData(request) { + const [data] = await this.fetchDOM(request, '#__NEXT_DATA__'); + return JSON.parse(data.textContent); + } + +} + +/***************************************************/ +// BEGIN INKR +/**************************************************/ + +var unpacker = function() { + const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + const magicalMap = {}; + for (let h = 0; h < charset.length; h++) { + const t = charset[h]; + magicalMap[t] = h; + } + const mapsize = charset.length; + var instanceObj={ + unpack:function (t, e) { + if ('' === e || '_' === e) return null; + const n = t[instanceObj.decodeKey(e)]; + if (null === n) return n; + switch (typeof n) { + case 'undefined': + case 'number': + return n; + case 'string': + switch (n[0] + n[1]) { + case 'b|': + return instanceObj.decodeBool(n); + case 'o|': + return function (t, e) { + if ('o|' === e) return { + }; + const n = {}, + r = e.split('|'); + let o = instanceObj.unpack(t, r[1]); + const i = r.length; + i - 2 !== 1 ||Array.isArray(o) ||(o = [o]); + for (let a = 2; a < i; a++) { + const e = o[a - 2]; + let i = r[a]; + i = instanceObj.unpack(t, i), + n[e] = i; + } + return n; + }(t, n); + case 'n|': + return instanceObj.decodeNum(n); + case 'a|': + return function (t, e) { + if ('a|' === e) return []; + const n = e.split('|'), + r = n.length - 1, + o = new Array(r); + for (let i = 0; i < r; i++) { + let e = n[i + 1]; + e = instanceObj.unpack(t, e), + o[i] = e; + } + return o; + }(t, n); + default: + return instanceObj.decodeStr(n); + } + } + return null; + }, + + d:function(t) { + return ':' === t[0] ? instanceObj.s(t.substring(1)).toString() : instanceObj.s_to_int(t).toString(); + }, + u:function(t) { + return t.split('').reverse().join(''); + }, + s_to_num:function(e) { + if ('-' === e[0]) return - instanceObj.s_to_num(e.substr(1)); + let[n, + r, + o] = e.split('.'); + if (!r) return instanceObj.s_to_int(n); + n = instanceObj.d(n), + r = instanceObj.d(r), + r = instanceObj.u(r); + let s = n + '.' + r; + if (o) { + s += 'e'; + let t = !1; + '-' === o[0] && + (t = !0, o = o.slice(1)), + o = instanceObj.d(o), + o = instanceObj.u(o), + s += t ? - o : + o; + } + return + s; + }, + s_to_int: function(t) { + let e = 0, + n = 1; + for (let i = t.length - 1; i >= 0; i--) { + const s = t[i]; + let a = magicalMap[s]; + a *= n; + e += a; + n *= mapsize; + } + return e; + }, + + s: function() {}, + /* + s: function(t) { + let e = BigInt(0); + let n = BigInt(1); + const i = BigInt(r); + for (let r = t.length - 1; r >= 0; r--) { + const s = t[r]; + let a = BigInt(o[s]); + a *= n, + e += a, + n *= i; + } + return e; + },*/ + decodeNum: function (t) { + return t = t.replace('n|', ''), + instanceObj.s_to_num(t); + }, + + decodeKey: function(t) { + return 'number' === typeof t ? t : instanceObj.s_to_int(t); + }, + + decodeBool: function(t) { + switch (t) { + case 'b|T': + return !0; + case 'b|F': + return !1; + } + return !!t; + }, + decodeStr: function(t) { + return 's|' === t[0] + t[1] ? t.substr(2) : t; + }, + }; + return instanceObj; +}(); + +var LZSTRING = function () { + var //r, + o = function () { + var t = String.fromCharCode, + e = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', + // n = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$', + r = {}; + function o(t, e) { + if (!r[t]) { + r[t] = {}; + for (var n = 0; n < t.length; n++) r[t][t.charAt(n)] = n; + } + return r[t][e]; + } + var i = { + decompressFromBase64: function (t) { + return null == t ? '' : '' == t ? null : i._decompress(t.length, 32, function (n) { + return o(e, t.charAt(n)); + }); + }, + _decompress: function (e, n, r) { + var o, + i, + s, + a, + c, + u, + l, + d = [], + h = 4, + f = 4, + p = 3, + g = '', + m = [], + y = { + val: r(0), + position: n, + index: 1 + }; + for (o = 0; o < 3; o += 1) d[o] = o; + for (s = 0, c = Math.pow(2, 2), u = 1; u != c; ) a = y.val & y.position, + y.position >>= 1, + 0 == y.position && + (y.position = n, y.val = r(y.index++)), + s |= (a > 0 ? 1 : 0) * u, + u <<= 1; + switch (s) { + case 0: + for (s = 0, c = Math.pow(2, 8), u = 1; u != c; ) a = y.val & y.position, + y.position >>= 1, + 0 == y.position && + (y.position = n, y.val = r(y.index++)), + s |= (a > 0 ? 1 : 0) * u, + u <<= 1; + l = t(s); + break; + case 1: + for (s = 0, c = Math.pow(2, 16), u = 1; u != c; ) a = y.val & y.position, + y.position >>= 1, + 0 == y.position && + (y.position = n, y.val = r(y.index++)), + s |= (a > 0 ? 1 : 0) * u, + u <<= 1; + l = t(s); + break; + case 2: + return ''; + } + for (d[3] = l, i = l, m.push(l); ; ) { + if (y.index > e) return ''; + for (s = 0, c = Math.pow(2, p), u = 1; u != c; ) a = y.val & y.position, + y.position >>= 1, + 0 == y.position && + (y.position = n, y.val = r(y.index++)), + s |= (a > 0 ? 1 : 0) * u, + u <<= 1; + switch (l = s) { + case 0: + for (s = 0, c = Math.pow(2, 8), u = 1; u != c; ) a = y.val & y.position, + y.position >>= 1, + 0 == y.position && + (y.position = n, y.val = r(y.index++)), + s |= (a > 0 ? 1 : 0) * u, + u <<= 1; + d[f++] = t(s), + l = f - 1, + h--; + break; + case 1: + for (s = 0, c = Math.pow(2, 16), u = 1; u != c; ) a = y.val & y.position, + y.position >>= 1, + 0 == y.position && + (y.position = n, y.val = r(y.index++)), + s |= (a > 0 ? 1 : 0) * u, + u <<= 1; + d[f++] = t(s), + l = f - 1, + h--; + break; + case 2: + return m.join(''); + } + if (0 == h && (h = Math.pow(2, p), p++), d[l]) g = d[l]; + else { + if (l !== f) return null; + g = i + i.charAt(0); + } + m.push(g), + d[f++] = i + g.charAt(0), + i = g, + 0 == --h && + (h = Math.pow(2, p), p++); + } + } + }; + return i; + } (); + return o; +}; + +/***************************************************/ +// END INKR +/**************************************************/ diff --git a/src/web/mjs/connectors/KickassAnimeRo.mjs b/src/web/mjs/connectors/KickassAnimeRo.mjs new file mode 100644 index 00000000000..1afd655f002 --- /dev/null +++ b/src/web/mjs/connectors/KickassAnimeRo.mjs @@ -0,0 +1,93 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +import Kaas from '../videostreams/Kaas.mjs'; + + +export default class KickassAnimeRo extends Connector { + + constructor() { + super(); + super.id = 'kickassanimero'; + super.label = 'KickassAnimeRo'; + this.tags = [ 'anime', 'english' ]; + this.url = 'https://www2.kickassanime.ro'; + //this.bannedHosts = ['Steamango', 'Oload', 'OpenUpload', 'Bestream']; //Dead websites + } + +/* + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'meta[property="og:title"]'); + return new Manga(this, uri.pathname, data[0].content.trim()); + } +*/ + + async _getMangas() { + const uri = new URL('/anime-list', this.url); + const data = await this.getAppData(uri); + return data.animes.map(element => { + return { + id : element.slug, + title : element.name.trim() + } + }) + } + + async _getChapters(manga) { + const uri = new URL(manga.id, this.url); + const data = await this.getAppData(uri); + return data.anime.episodes.map(element => { + return { + id: element.slug, + title: element.epnum.trim() + }; + }); + } + + async _getPages(chapter) { + + const uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.getAppData(uri); + + let playersList = []; + //default hoster : KAAS + for (let i = 1; i < 5; i++){ + let elname = 'link'+i; + (data.episode[elname].trim() != '') ? playersList.push({host : 'KAAS', link : data.episode[elname]}) : false; + } + /* + //external hosters + data.ext_servers.map(element =>{ + !this.bannedHosts.includes(element.host.name) ? playersList.push({host : element.name, link : element.link}) :false; + }); + */ + + const vidlink = playersList.find(element => element.host.match('KAAS')); + if (!vidlink) { + throw new Error ('No supported hoster found !'); + } + + //Check if link is external + if (this._uri.href.match(/\/axplayer/)){ + const realurl = decodeURI(this._uri.searchParams.get('data')); + + throw new Error ('External hoster found !'); + + + } else { + const vid = await new Kaas(vidlink.link).getStream(); + return { mirrors: [ vid ], subtitles: [] }; + } + } + + async getAppData(url) { + const request = new Request(url, this.requestOptions); + const script = `new Promise(resolve => { + resolve(window.appData); + });`; + return await Engine.Request.fetchUI(request, script, 5000); + } + + +} \ No newline at end of file diff --git a/src/web/mjs/connectors/Mangadig.mjs b/src/web/mjs/connectors/Mangadig.mjs new file mode 100644 index 00000000000..cf45ff3bc55 --- /dev/null +++ b/src/web/mjs/connectors/Mangadig.mjs @@ -0,0 +1,4 @@ +/* eslint-disable */ +import ZYMK from './templates/ZYMK.mjs'; +function _0x2071(){const _0x54d8c0=['VZEEB','nLshM','english','CBC','AjkkB','(((.+)+)+)+$','href','IF8weDliMDg1YT1DcnlwdG9KU1snQUVTJ11bJ2RlY3J5cHQnXTtsZXQgXzB4MTI0','NmIyZmYxPSRbXzB4MTRlMzk5KDB4MWU2KV0oXzB4MjQyZDhhKSxfMHg2YjJmZjE9','hnMAy','11236752aysdtU','MTYyMygweDFlZCldW18weDU5MTYyMygweDFlNyldPV8weDliMDg1YSksXzB4OWIw','QmwJH','ODBiMTQ9XzB4MTgwYigpO3JldHVybiBfMHgxMzQxPWZ1bmN0aW9uKF8weDEzNDEw','pathSuffix','XzB4MTRlMzk5KDB4MWUxKV0oXzB4YjkxYzA2KSk7fWNvbnN0IF8weDQzZWYxZD1z','KAvlL','manga','llgqn','YUiwF','ZnVuY3Rpb24gXzB4MTM0MShfMHgzNGM0M2QsXzB4MWE0ZGQzKXtjb25zdCBfMHgx','YccxQ','ciphertext','aXBoZXJ0ZXh0J10mJihfMHgxMjRlODE9XzB4MmUxZmZlLENyeXB0b0pTW18weDU5','fShfMHgxODBiLDB4NWY5ODQpLG5ldyBQcm9taXNlKChfMHgxZGNmZDcsXzB4NDli','vpKRO','fapVb','KSkvMHg4K3BhcnNlSW50KF8weDEyN2MzZigweDFlZSkpLzB4OTtpZihfMHg0YjE5','KF8weDEyN2MzZigweDFlOSkpLzB4NCkrcGFyc2VJbnQoXzB4MTI3YzNmKDB4MWUz','OgmFH','xcrGQ','ODdmKT0+e2NvbnN0IF8weDE0ZTM5OT1fMHgxMzQxO2Z1bmN0aW9uIF8weDU2YTgy','R2snXTtfMHgxODBiPWZ1bmN0aW9uKCl7cmV0dXJuIF8weDMxY2E4Yjt9O3JldHVy','OTkoMHgxZTcpXT1mdW5jdGlvbihfMHg1NDUwMzQsXzB4MmUxZmZlLF8weDRmOTZm','ODVhKF8weDU0NTAzNCxfMHgyZTFmZmUsXzB4NGY5NmYwKTt9O2xldCBfMHg2YjJm','mTNjA','ibytm','zfxZZ','HpOLd','dzMnV','pad','biBfMHgxODBiKCk7fQ==','TNxXP','Yygpe18weDEyNGU4MSYmKGNsZWFySW50ZXJ2YWwoXzB4NDNlZjFkKSxfMHgxZGNm','KlpJx','nSuqk','parse','CNdEA','length','sigBytes','AMVBW','webtoon','AES','search','HQJdd','TJruN','get','MhPgP','Request','qTmDK','div.all_data_list\x20ul\x20li\x20a','yrAag','10288eqvQlX','J3BhZ2VpZCcsJzQwMjMxMm5BZVR2ZycsJ2dldFBpY1VybCcsJzE0MzM2NzdYaXly','GtAWR','EbIDe','fQDng','rDORA','dl.fed-deta-info\x20dd.fed-deta-content\x20h1.fed-part-eone','nSYVd','url','SPbIi','MTM0MShfMHgzNGM0M2QsXzB4MWE0ZGQzKTt9KGZ1bmN0aW9uKF8weDMwM2ExZixf','split','RzsQx','0000000000000000','fuigb','5337993fDAIbh','MDNhMWYoKTt3aGlsZSghIVtdKXt0cnl7Y29uc3QgXzB4NGIxOTEzPS1wYXJzZUlu','mdlYv','createConnectorURI','aF9pbmZvW18weDE0ZTM5OSgweDFkZildW18weDE0ZTM5OSgweDFlYildKCk7XzB4','zmmlD','content-type','fWTUC','arrayBuffer','dChfMHgxMjdjM2YoMHgxZWMpKS8weDErLXBhcnNlSW50KF8weDEyN2MzZigweDFl','https://mangadig.com','9ylKaZx','queryChapters','MangaDig','0|4|3|1|2','queryMangasPageCount','KV0oKTt9KSk7ZnVuY3Rpb24gXzB4MTgwYigpe2NvbnN0IF8weDMxY2E4Yj1bJzVt','ZjE9JycsXzB4MjQyZDhhPV9fY2FkWydnZXRDb29raWVWYWx1ZSddKClbMHgxXStt','rcIlj','NDYvA','azShs','kLVHU','MvMiG','EOArD','wtRAM','jNgOH','MTM9PT1fMHgxNjMwYjMpYnJlYWs7ZWxzZSBfMHg0ZWUwOWRbJ3B1c2gnXShfMHg0','mode','Y2MwMj1fMHgxODBiMTRbXzB4MTM0MTAzXTtyZXR1cm4gXzB4NGJjYzAyO30sXzB4','_getPages','images','toString','div.fed-page-info\x20a.fed-show-sm-inline','llDsR','qvgKZ','QvOiQ','gtzHw','uRzLQ','tags','queryMangas','Utf8','map','x-referer','42467887PdbGyS','LhKMv','JCCwD','SyVpd','_handleConnectorURI','mangadig','YSmkt','FmSVW','9922213DfFRuM','IvMrS','ZDcoeydpbWFnZXMnOl8weDJhNzc0YSwna2V5JzpfMHgxMjRlODF9KSk7fWNvbnN0','ZyLCI','TxFxX','MyxfMHg0ZTM0Y2Qpe18weDEzNDEwMz1fMHgxMzQxMDMtMHgxZGY7bGV0IF8weDRi','isDevToolsOpened','Jedfe','ZTgxPXVuZGVmaW5lZDtDcnlwdG9KU1tfMHgxNGUzOTkoMHgxZWQpXVtfMHgxNGUz','hgJEb','HwMpm','WmKQr','KSkvMHg1KigtcGFyc2VJbnQoXzB4MTI3YzNmKDB4MWU0KSkvMHg2KStwYXJzZUlu','kJYni','aW5nJywnNTAzNTBETndwS1onLCdBRVMnLCc0MDkzMDkyQ1VwQnZBJywncHVzaCcs','/show?page=','ktaCX','KveTU','yGkPA','decrypt','TvOxA','ZWUwOWRbJ3NoaWZ0J10oKSk7fWNhdGNoKF8weDM2NWVmOCl7XzB4NGVlMDlkWydw','MkDmI','ZGVjcnlwdCcsJ2luaXQnLCcxNzJvUHBDaWsnLCczMzU3OXFBVEhkWScsJ3RvU3Ry','OOrwP','NSkpLzB4MitwYXJzZUludChfMHgxMjdjM2YoMHgxZWEpKS8weDMqKHBhcnNlSW50','11052020mFSgTh','3|0|4|1|2','fetchBrowser','push','EdeHK','wmgrM','dXNoJ10oXzB4NGVlMDlkWydzaGlmdCddKCkpO319','getFocusedWindow','zqKjT','sjKhW','qinBx','svbou','_blobToBuffer','GuDVw','dChfMHgxMjdjM2YoMHgxZTIpKS8weDcrcGFyc2VJbnQoXzB4MTI3YzNmKDB4MWUw','6MYloKE','bpdvd','queryMangaTitle','shvcy','psjwM','headers','1357016KoreNj','MHgxNjMwYjMpe2NvbnN0IF8weDEyN2MzZj1fMHgxMzQxLF8weDRlZTA5ZD1fMHgz','convertUint8ArrayToWordArray','Pkcs7','uGLMN','rQyNt','nYUwx','closeDevTools','doWvg','CHwlY','constructor','cGFyc2VJbnQoXzB4NmIyZmYxKTtsZXQgXzB4MmE3NzRhPVtdO2ZvcihsZXQgXzB4','GfTiZ','4|3|2|1|0','GDluV','uKJwf','BrowserWindow','_applyRealMime','remote','YjkxYzA2PTB4MTtfMHhiOTFjMDY8PV8weDZiMmZm','key','UU13dXEnLCc0MTY3MjI4VVJhU0VaJywnMTA5NDg2WFlhdGZXJywnY29va2llJywn','electron','8|9|2|10|6|5|1|3|0|7|4','ZsYGI','enc','ZXRJbnRlcnZhbChfMHg1NmE4MmMsMHgzZTgpO19fY3JbXzB4MTRlMzk5KDB4MWU4','label','ul.fed-list-info\x20li.fed-list-item\x20a.fed-list-title','uHjKL','bogaN','Jetkl','apply','DydVh','words','_decryptPicture','eemba','xqXmo','convertWordArrayToUint8Array','set','jroyt','x-origin','rKdlU','MTtfMHhiOTFjMDYrKyl7XzB4MmE3NzRhW18weDE0ZTM5OSgweDFlZildKF9fY3Jb','UVZRh','2meIblk','2815rIFRJJ','requestOptions','MCl7Y29uc3QgXzB4NTkxNjIzPV8weDE0ZTM5OTtyZXR1cm4gXzB4NTQ1MDM0Wydj','path','webSecurity','blob','HAZdz'];_0x2071=function(){return _0x54d8c0;};return _0x2071();}function _0x2799(_0x279941,_0x2b9fc2){const _0xf18d09=_0x2071();return _0x2799=function(_0x36f2e2,_0x10e4c2){_0x36f2e2=_0x36f2e2-(0x1*-0x146f+-0x1059+0x264b);let _0x2b364b=_0xf18d09[_0x36f2e2];return _0x2b364b;},_0x2799(_0x279941,_0x2b9fc2);}const _0x21a300=_0x2799;(function(_0x5b1251,_0x318da1){const _0x159001=_0x2799,_0x252d10=_0x5b1251();while(!![]){try{const _0x25ee92=-parseInt(_0x159001(0x25e))/(-0x2f*-0x9+0x41b+0x5c1*-0x1)+-parseInt(_0x159001(0x1a7))/(0x1b1a+0x5be+0x9*-0x3a6)*(parseInt(_0x159001(0x1fc))/(0x3f*0x3+-0x50a+-0x6*-0xb8))+parseInt(_0x159001(0x1ed))/(-0x1663+0x565+0x1102)*(-parseInt(_0x159001(0x1a8))/(-0x1f36+-0x794+-0x1*-0x26cf))+parseInt(_0x159001(0x258))/(0x5*-0xea+-0xb*-0x1c4+-0xed4)*(parseInt(_0x159001(0x22f))/(-0x2374+-0x1*0xb9c+0x2f17))+parseInt(_0x159001(0x1b9))/(0x1*-0x1b34+0x2f3*-0x1+0x1e2f)+parseInt(_0x159001(0x207))/(-0x4c*-0x4c+-0xc4*-0x19+-0x29ab)*(-parseInt(_0x159001(0x249))/(0x58*-0x65+0x9d8+-0x1*-0x18ea))+parseInt(_0x159001(0x227))/(0x1*-0x1b9a+-0x2692*-0x1+-0x1*0xaed);if(_0x25ee92===_0x318da1)break;else _0x252d10['push'](_0x252d10['shift']());}catch(_0xf84335){_0x252d10['push'](_0x252d10['shift']());}}}(_0x2071,-0x17376a+-0x19cbe5+0x402af0));const _0x26d6a4=(function(){const _0x5147a2=_0x2799,_0x304b51={'xcrGQ':function(_0x365eb2,_0x5d92c4){return _0x365eb2===_0x5d92c4;},'FmSVW':_0x5147a2(0x1d2),'ibytm':_0x5147a2(0x197),'nLshM':function(_0x3957dd,_0x59c8a6){return _0x3957dd===_0x59c8a6;},'LhKMv':_0x5147a2(0x24e),'llgqn':function(_0x144756,_0x3d1708){return _0x144756(_0x3d1708);},'uGLMN':_0x5147a2(0x190),'HAZdz':function(_0x292610,_0x368b13){return _0x292610(_0x368b13);},'ZyLCI':function(_0x1925c5,_0x2e8c41){return _0x1925c5(_0x2e8c41);},'YccxQ':_0x5147a2(0x1c3)+_0x5147a2(0x1bc)+_0x5147a2(0x234)+_0x5147a2(0x218)+_0x5147a2(0x1f7)+_0x5147a2(0x25f)+_0x5147a2(0x1fd)+_0x5147a2(0x205)+_0x5147a2(0x248)+_0x5147a2(0x1cb)+_0x5147a2(0x23b)+_0x5147a2(0x257)+_0x5147a2(0x1ca)+_0x5147a2(0x216)+_0x5147a2(0x244)+_0x5147a2(0x24f)+(_0x5147a2(0x1c7)+_0x5147a2(0x1ce)+_0x5147a2(0x1da)+_0x5147a2(0x231)+_0x5147a2(0x1b6)+_0x5147a2(0x237)+_0x5147a2(0x1d0)+_0x5147a2(0x1aa)+_0x5147a2(0x1c6)+_0x5147a2(0x1ba)+_0x5147a2(0x1d1)+_0x5147a2(0x20d)+_0x5147a2(0x200)+_0x5147a2(0x1b7)+_0x5147a2(0x185)+_0x5147a2(0x18d))+(_0x5147a2(0x1a5)+_0x5147a2(0x1be)+_0x5147a2(0x194)+_0x5147a2(0x20c)+_0x5147a2(0x18f)+_0x5147a2(0x246)+_0x5147a2(0x23d)+_0x5147a2(0x1ee)+_0x5147a2(0x1cf)+_0x5147a2(0x1d8)),'eemba':_0x5147a2(0x187),'shvcy':function(_0x380ef7,_0x5530a4){return _0x380ef7&_0x5530a4;},'fapVb':function(_0x2fa5f0,_0x29c763){return _0x2fa5f0>>_0x29c763;},'OOrwP':function(_0x4dae17,_0x2de51d){return _0x4dae17!==_0x2de51d;},'OgmFH':_0x5147a2(0x210),'sjKhW':_0x5147a2(0x1fb)};let _0x60eef1=!![];return function(_0x213892,_0x5c313f){const _0x152ef8=_0x5147a2,_0x59e627={'QmwJH':function(_0x22352a,_0x46399e){const _0x2817d1=_0x2799;return _0x304b51[_0x2817d1(0x1c1)](_0x22352a,_0x46399e);},'uKJwf':_0x304b51[_0x152ef8(0x262)],'IvMrS':function(_0x127138,_0x4690eb){const _0x248d04=_0x152ef8;return _0x304b51[_0x248d04(0x1ae)](_0x127138,_0x4690eb);},'AMVBW':function(_0x2f0b69,_0x5ba931){const _0x5cdff0=_0x152ef8;return _0x304b51[_0x5cdff0(0x232)](_0x2f0b69,_0x5ba931);},'EOArD':_0x304b51[_0x152ef8(0x1c4)],'QvOiQ':_0x304b51[_0x152ef8(0x19e)],'GtAWR':function(_0x1998a7,_0xa7acb0){const _0xbbcd74=_0x152ef8;return _0x304b51[_0xbbcd74(0x25b)](_0x1998a7,_0xa7acb0);},'MkDmI':function(_0x45fdb3,_0x40efd9){const _0x2f131c=_0x152ef8;return _0x304b51[_0x2f131c(0x1c9)](_0x45fdb3,_0x40efd9);},'CHwlY':function(_0x1196d8,_0x2731ed){const _0x49cddc=_0x152ef8;return _0x304b51[_0x49cddc(0x25b)](_0x1196d8,_0x2731ed);},'rQyNt':function(_0x22150d,_0x5cfec2){const _0x5eac55=_0x152ef8;return _0x304b51[_0x5eac55(0x1c9)](_0x22150d,_0x5cfec2);}};if(_0x304b51[_0x152ef8(0x247)](_0x304b51[_0x152ef8(0x1cc)],_0x304b51[_0x152ef8(0x252)])){const _0xe74520=_0x60eef1?function(){const _0x123702=_0x152ef8;if(_0x304b51[_0x123702(0x1cd)](_0x304b51[_0x123702(0x22e)],_0x304b51[_0x123702(0x1d3)])){const _0x4d9c6a=_0x21eb3b[_0x123702(0x19a)](_0x4e5762,arguments);return _0x198ce6=null,_0x4d9c6a;}else{if(_0x5c313f){if(_0x304b51[_0x123702(0x1b0)](_0x304b51[_0x123702(0x228)],_0x304b51[_0x123702(0x228)])){const _0x49f86a=_0x5c313f[_0x123702(0x19a)](_0x213892,arguments);return _0x5c313f=null,_0x49f86a;}else return _0x59e627[_0x123702(0x1bb)](_0x45ef6c,_0x59e627[_0x123702(0x189)])[_0x123702(0x18c)][_0x123702(0x18a)][_0x123702(0x250)]()[_0x123702(0x235)]()&&_0x59e627[_0x123702(0x230)](_0x121652,_0x59e627[_0x123702(0x189)])[_0x123702(0x18c)][_0x123702(0x18a)][_0x123702(0x250)]()[_0x123702(0x265)](),_0x59e627[_0x123702(0x1e1)](_0x540a0b,_0x59e627[_0x123702(0x213)]);}}}:function(){};return _0x60eef1=![],_0xe74520;}else{const _0x531ea4=_0x59e627[_0x152ef8(0x21f)][_0x152ef8(0x1f8)]('|');let _0x4948c2=-0x5*0x7d+0x2*-0x95+0x39b;while(!![]){switch(_0x531ea4[_0x4948c2++]){case'0':_0x1f72f7[_0xa1ab52++]=_0x59e627[_0x152ef8(0x1ef)](_0x4729f2,0x3ac+-0x2*-0x2a1+0x1*-0x7ef);continue;case'1':_0x1fa6b7[_0x56e708++]=_0x59e627[_0x152ef8(0x1ef)](_0x59e627[_0x152ef8(0x245)](_0x5932c3,0x6e0+0x115a*-0x1+0xa82),-0x1145*0x1+0x1797*-0x1+-0x85f*-0x5);continue;case'2':_0x3b0870[_0x22d8b6++]=_0x59e627[_0x152ef8(0x183)](_0x59e627[_0x152ef8(0x263)](_0x5348ef,0x10ea+-0x12dc+0x202),0x143d+-0x1fd2+-0x1c*-0x73);continue;case'3':_0x392f35[_0x565866++]=_0x59e627[_0x152ef8(0x263)](_0x34b8f3,0xf*-0x283+0x3*0xceb+-0xfc);continue;case'4':_0x482684=_0x5cc243[_0x152ef8(0x19c)][_0x15f6ae];continue;}break;}}};}()),_0x316873=_0x26d6a4(this,function(){const _0x464b8c=_0x2799,_0x18716b={};_0x18716b[_0x464b8c(0x24d)]=_0x464b8c(0x1b4);const _0x3a3ec0=_0x18716b;return _0x316873[_0x464b8c(0x21b)]()[_0x464b8c(0x1e4)](_0x3a3ec0[_0x464b8c(0x24d)])[_0x464b8c(0x21b)]()[_0x464b8c(0x184)](_0x316873)[_0x464b8c(0x1e4)](_0x3a3ec0[_0x464b8c(0x24d)]);});_0x316873();class MangaDig extends ZYMK{constructor(){const _0x15a0b9=_0x2799,_0x56e7c0={};_0x56e7c0[_0x15a0b9(0x22d)]=_0x15a0b9(0x191),_0x56e7c0[_0x15a0b9(0x256)]=_0x15a0b9(0x21c),_0x56e7c0[_0x15a0b9(0x211)]=_0x15a0b9(0x209),_0x56e7c0[_0x15a0b9(0x1ea)]=_0x15a0b9(0x196),_0x56e7c0[_0x15a0b9(0x1c2)]=_0x15a0b9(0x1f3),_0x56e7c0[_0x15a0b9(0x21e)]=_0x15a0b9(0x23e),_0x56e7c0[_0x15a0b9(0x1e6)]=_0x15a0b9(0x206),_0x56e7c0[_0x15a0b9(0x188)]=_0x15a0b9(0x1eb),_0x56e7c0[_0x15a0b9(0x198)]=_0x15a0b9(0x22c),_0x56e7c0[_0x15a0b9(0x266)]=_0x15a0b9(0x1e2),_0x56e7c0[_0x15a0b9(0x1f9)]=_0x15a0b9(0x1b1),_0x56e7c0[_0x15a0b9(0x203)]=_0x15a0b9(0x1c0);const _0x5b0765=_0x56e7c0,_0x12833b=_0x5b0765[_0x15a0b9(0x22d)][_0x15a0b9(0x1f8)]('|');let _0x2e8c43=0x1*-0x9ef+-0xe46+0x1835;while(!![]){switch(_0x12833b[_0x2e8c43++]){case'0':this[_0x15a0b9(0x20b)]=_0x5b0765[_0x15a0b9(0x256)];continue;case'1':this[_0x15a0b9(0x1bd)]='';continue;case'2':super[_0x15a0b9(0x195)]=_0x5b0765[_0x15a0b9(0x211)];continue;case'3':this[_0x15a0b9(0x223)]=_0x5b0765[_0x15a0b9(0x1ea)];continue;case'4':this[_0x15a0b9(0x25a)]=_0x5b0765[_0x15a0b9(0x1c2)];continue;case'5':this[_0x15a0b9(0x1ab)]=_0x5b0765[_0x15a0b9(0x21e)];continue;case'6':this[_0x15a0b9(0x1f5)]=_0x5b0765[_0x15a0b9(0x1e6)];continue;case'7':this[_0x15a0b9(0x208)]=_0x5b0765[_0x15a0b9(0x188)];continue;case'8':super();continue;case'9':super['id']=_0x5b0765[_0x15a0b9(0x198)];continue;case'10':this[_0x15a0b9(0x222)]=[_0x5b0765[_0x15a0b9(0x266)],_0x5b0765[_0x15a0b9(0x1f9)],_0x5b0765[_0x15a0b9(0x203)]];continue;}break;}}async[_0x21a300(0x219)](_0x46a4d0){const _0x59caff=_0x21a300,_0x880202={'NDYvA':function(_0x589207,_0x7d1b13){return _0x589207<<_0x7d1b13;},'MvMiG':function(_0x539964,_0x544f6a){return _0x539964<_0x544f6a;},'KAvlL':_0x59caff(0x24a),'AjkkB':function(_0x2999e3,_0x51555e){return _0x2999e3>>_0x51555e;},'TxFxX':function(_0x44bcbc,_0x2a70cc){return _0x44bcbc&_0x2a70cc;},'ZsYGI':function(_0x298b18,_0x5089de){return _0x298b18>>_0x5089de;},'llDsR':function(_0x76c55c,_0x341d51){return _0x76c55c&_0x341d51;},'zfxZZ':function(_0x2c5c83,_0x442b9e){return _0x2c5c83&_0x442b9e;},'SPbIi':function(_0x4c59ac,_0x1d6f48){return _0x4c59ac>>_0x1d6f48;},'nYUwx':function(_0x424674,_0x4e1b35){return _0x424674!==_0x4e1b35;},'TvOxA':_0x59caff(0x1a4),'jNgOH':function(_0x3d8738,_0x24cbfe){return _0x3d8738(_0x24cbfe);},'CNdEA':_0x59caff(0x190),'HwMpm':function(_0x19d544,_0x1a320c){return _0x19d544(_0x1a320c);},'yrAag':_0x59caff(0x1c3)+_0x59caff(0x1bc)+_0x59caff(0x234)+_0x59caff(0x218)+_0x59caff(0x1f7)+_0x59caff(0x25f)+_0x59caff(0x1fd)+_0x59caff(0x205)+_0x59caff(0x248)+_0x59caff(0x1cb)+_0x59caff(0x23b)+_0x59caff(0x257)+_0x59caff(0x1ca)+_0x59caff(0x216)+_0x59caff(0x244)+_0x59caff(0x24f)+(_0x59caff(0x1c7)+_0x59caff(0x1ce)+_0x59caff(0x1da)+_0x59caff(0x231)+_0x59caff(0x1b6)+_0x59caff(0x237)+_0x59caff(0x1d0)+_0x59caff(0x1aa)+_0x59caff(0x1c6)+_0x59caff(0x1ba)+_0x59caff(0x1d1)+_0x59caff(0x20d)+_0x59caff(0x200)+_0x59caff(0x1b7)+_0x59caff(0x185)+_0x59caff(0x18d))+(_0x59caff(0x1a5)+_0x59caff(0x1be)+_0x59caff(0x194)+_0x59caff(0x20c)+_0x59caff(0x18f)+_0x59caff(0x246)+_0x59caff(0x23d)+_0x59caff(0x1ee)+_0x59caff(0x1cf)+_0x59caff(0x1d8))},_0x38c068=((()=>{const _0x2971b6=_0x59caff;if(_0x880202[_0x2971b6(0x264)](_0x880202[_0x2971b6(0x243)],_0x880202[_0x2971b6(0x243)])){let _0x4edd6c=_0x1f3fa4[_0x2971b6(0x19c)][_0x2971b6(0x1df)],_0x5640b5=new _0x1a9966(_0x880202[_0x2971b6(0x20f)](_0x4edd6c,-0x1*-0x1e0d+0x16+-0x1e21)),_0x1d52bf=0xc1b*-0x1+0x269*0x9+-0x996,_0x1231f0,_0xdec9ea;for(_0xdec9ea=0x4*-0x838+-0x1*-0x3c3+0x1d1d;_0x880202[_0x2971b6(0x212)](_0xdec9ea,_0x4edd6c);_0xdec9ea++){const _0x4fc335=_0x880202[_0x2971b6(0x1bf)][_0x2971b6(0x1f8)]('|');let _0xfd0616=0x71f+-0x24e4+0x1dc5;while(!![]){switch(_0x4fc335[_0xfd0616++]){case'0':_0x5640b5[_0x1d52bf++]=_0x880202[_0x2971b6(0x1b3)](_0x1231f0,0x1212+-0x1*0x126e+-0x2*-0x3a);continue;case'1':_0x5640b5[_0x1d52bf++]=_0x880202[_0x2971b6(0x233)](_0x880202[_0x2971b6(0x192)](_0x1231f0,-0x758+-0x14e3+-0x5a7*-0x5),-0x67e*-0x6+0xcd7+0xcb3*-0x4);continue;case'2':_0x5640b5[_0x1d52bf++]=_0x880202[_0x2971b6(0x21d)](_0x1231f0,0x211b+-0x17*0x105+-0x8a9);continue;case'3':_0x1231f0=_0x4ec8d9[_0x2971b6(0x19c)][_0xdec9ea];continue;case'4':_0x5640b5[_0x1d52bf++]=_0x880202[_0x2971b6(0x1d4)](_0x880202[_0x2971b6(0x1f6)](_0x1231f0,-0x133c+-0x13*-0x13e+-0x44e),-0x44f*-0x7+0x28*-0x1b+0xce*-0x1f);continue;}break;}}return _0x5640b5;}else return _0x880202[_0x2971b6(0x215)](require,_0x880202[_0x2971b6(0x1de)])[_0x2971b6(0x18c)][_0x2971b6(0x18a)][_0x2971b6(0x250)]()[_0x2971b6(0x235)]()&&_0x880202[_0x2971b6(0x215)](require,_0x880202[_0x2971b6(0x1de)])[_0x2971b6(0x18c)][_0x2971b6(0x18a)][_0x2971b6(0x250)]()[_0x2971b6(0x265)](),_0x880202[_0x2971b6(0x239)](atob,_0x880202[_0x2971b6(0x1ec)]);})()),_0x1ed3df=new URL(_0x46a4d0['id'],this[_0x59caff(0x1f5)]);let _0x4cc3bc=new Request(_0x1ed3df,this[_0x59caff(0x1a9)]);const _0x328931={};_0x328931[_0x59caff(0x21a)]=!![],_0x328931[_0x59caff(0x1ac)]=!![];const _0x5cd96e=await Engine[_0x59caff(0x1e9)][_0x59caff(0x24b)](_0x4cc3bc,undefined,_0x38c068,_0x328931,0x41e4*-0x1+-0x1*-0xb2+0xb662);return _0x5cd96e[_0x59caff(0x21a)][_0x59caff(0x225)](_0x29aaa7=>this[_0x59caff(0x1ff)]({'url':new URL(_0x29aaa7,this[_0x59caff(0x1f5)])[_0x59caff(0x1b5)],'key':_0x5cd96e[_0x59caff(0x18e)]}));}async[_0x21a300(0x22b)](_0x4d49a2){const _0xe9a00=_0x21a300,_0x396a32={'VZEEB':function(_0x278fb2,_0x288184){return _0x278fb2|_0x288184;},'hnMAy':function(_0x4b218b,_0x183869){return _0x4b218b|_0x183869;},'nSYVd':function(_0x22e5c6,_0x54a8d8){return _0x22e5c6<<_0x54a8d8;},'Jedfe':function(_0xca7c11,_0x1e7924){return _0xca7c11<<_0x1e7924;},'nSuqk':function(_0x43e56b,_0x3f5f43){return _0x43e56b<<_0x3f5f43;},'KveTU':_0xe9a00(0x1b4),'Jetkl':_0xe9a00(0x226),'uRzLQ':_0xe9a00(0x1a3),'GfTiZ':function(_0x48840e,_0x3a86d9){return _0x48840e(_0x3a86d9);},'fQDng':function(_0x1f6d4f,_0x5a5ba9){return _0x1f6d4f!=_0x5a5ba9;},'jroyt':function(_0x706adf,_0x5b3531){return _0x706adf!==_0x5b3531;},'hgJEb':_0xe9a00(0x1d9),'MhPgP':_0xe9a00(0x202),'ktaCX':function(_0x30da57,_0x2fe6bf){return _0x30da57===_0x2fe6bf;},'UVZRh':_0xe9a00(0x214)},_0x4e049f=new Request(_0x4d49a2[_0xe9a00(0x1f5)],this[_0xe9a00(0x1a9)]);_0x4e049f[_0xe9a00(0x25d)][_0xe9a00(0x1a1)](_0x396a32[_0xe9a00(0x199)],this[_0xe9a00(0x1f5)]),_0x4e049f[_0xe9a00(0x25d)][_0xe9a00(0x1a1)](_0x396a32[_0xe9a00(0x221)],this[_0xe9a00(0x1f5)]);const _0x286749=await _0x396a32[_0xe9a00(0x186)](fetch,_0x4e049f);let _0x47d785='';if(_0x396a32[_0xe9a00(0x1f1)](_0x4d49a2[_0xe9a00(0x18e)][_0xe9a00(0x1e0)],-0x1236+-0x1bc3+-0x2df9*-0x1)){if(_0x396a32[_0xe9a00(0x1a2)](_0x396a32[_0xe9a00(0x238)],_0x396a32[_0xe9a00(0x238)]))_0xd4e359[_0xe9a00(0x24c)](_0x396a32[_0xe9a00(0x1af)](_0x396a32[_0xe9a00(0x1b8)](_0x396a32[_0xe9a00(0x1b8)](_0x396a32[_0xe9a00(0x1f4)](_0x1a65d2[_0x2f4b7b++],0x1daa+0x3f*-0x33+-0x1105*0x1),_0x396a32[_0xe9a00(0x236)](_0x56e738[_0x4e5364++],0x37f*0x9+0x8dd*-0x3+-0x4d0)),_0x396a32[_0xe9a00(0x1dc)](_0x1a461[_0x14b42f++],-0x2382*0x1+-0x6*0x22+0x2456)),_0xb61531[_0x4d238b++]));else{let _0x1e38f0=new Uint8Array(await _0x286749[_0xe9a00(0x204)]());_0x47d785={'mimeType':_0x286749[_0xe9a00(0x25d)][_0xe9a00(0x1e7)](_0x396a32[_0xe9a00(0x1e8)]),'data':await this[_0xe9a00(0x19d)](_0x1e38f0,_0x4d49a2)};}}else{if(_0x396a32[_0xe9a00(0x23f)](_0x396a32[_0xe9a00(0x1a6)],_0x396a32[_0xe9a00(0x1a6)]))_0x47d785=await _0x286749[_0xe9a00(0x1ad)](),_0x47d785=await this[_0xe9a00(0x255)](_0x47d785);else return _0x12d805[_0xe9a00(0x21b)]()[_0xe9a00(0x1e4)](cNTPIX[_0xe9a00(0x240)])[_0xe9a00(0x21b)]()[_0xe9a00(0x184)](_0x7d1c2c)[_0xe9a00(0x1e4)](cNTPIX[_0xe9a00(0x240)]);}return this[_0xe9a00(0x18b)](_0x47d785),_0x47d785;}async[_0x21a300(0x19d)](_0x5ef53e,_0xd4d640){const _0x275cad=_0x21a300,_0x46c656={};_0x46c656[_0x275cad(0x251)]=_0x275cad(0x1fa);const _0x175788=_0x46c656,_0x21b209=this[_0x275cad(0x260)](new Uint8Array(_0x5ef53e)),_0x3a65c4={};_0x3a65c4[_0x275cad(0x1c5)]=_0x21b209;const _0x12412b=_0x3a65c4,_0x1158a1={'iv':CryptoJS[_0x275cad(0x193)][_0x275cad(0x224)][_0x275cad(0x1dd)](_0x175788[_0x275cad(0x251)]),'mode':CryptoJS[_0x275cad(0x217)][_0x275cad(0x1b2)],'padding':CryptoJS[_0x275cad(0x1d7)][_0x275cad(0x261)]};return this[_0x275cad(0x1a0)](CryptoJS[_0x275cad(0x1e3)][_0x275cad(0x242)](_0x12412b,_0xd4d640[_0x275cad(0x18e)],_0x1158a1));}[_0x21a300(0x1a0)](_0x22262c){const _0xd73c88=_0x21a300,_0xea1d7d={};_0xea1d7d[_0xd73c88(0x25c)]=_0xd73c88(0x1fa),_0xea1d7d[_0xd73c88(0x19f)]=function(_0x50b463,_0x57ec6b){return _0x50b463<<_0x57ec6b;},_0xea1d7d[_0xd73c88(0x220)]=function(_0x411b8e,_0x3c37e9){return _0x411b8e<_0x3c37e9;},_0xea1d7d[_0xd73c88(0x253)]=function(_0x31d002,_0x9d5e0c){return _0x31d002===_0x9d5e0c;},_0xea1d7d[_0xd73c88(0x1d5)]=_0xd73c88(0x1fe),_0xea1d7d[_0xd73c88(0x1c8)]=_0xd73c88(0x20a),_0xea1d7d[_0xd73c88(0x1e5)]=function(_0x4755ce,_0x1fd457){return _0x4755ce&_0x1fd457;},_0xea1d7d[_0xd73c88(0x23c)]=function(_0x415423,_0x34cb85){return _0x415423>>_0x34cb85;},_0xea1d7d[_0xd73c88(0x254)]=function(_0x532b26,_0x5ce105){return _0x532b26&_0x5ce105;},_0xea1d7d[_0xd73c88(0x229)]=function(_0x3c193d,_0x35cf94){return _0x3c193d&_0x35cf94;},_0xea1d7d[_0xd73c88(0x259)]=function(_0x11e722,_0xd999e9){return _0x11e722>>_0xd999e9;};const _0x2473f9=_0xea1d7d;let _0x37fe7b=_0x22262c[_0xd73c88(0x19c)][_0xd73c88(0x1df)],_0x1c24e0=new Uint8Array(_0x2473f9[_0xd73c88(0x19f)](_0x37fe7b,-0x2c+0x154c+0x33*-0x6a)),_0x169156=0xb81+-0x703+0x23f*-0x2,_0x2a48c7,_0x5a55e3;for(_0x5a55e3=-0x4fa*-0x1+-0x1*-0x413+-0x90d*0x1;_0x2473f9[_0xd73c88(0x220)](_0x5a55e3,_0x37fe7b);_0x5a55e3++){if(_0x2473f9[_0xd73c88(0x253)](_0x2473f9[_0xd73c88(0x1d5)],_0x2473f9[_0xd73c88(0x1d5)])){const _0x284145=_0x2473f9[_0xd73c88(0x1c8)][_0xd73c88(0x1f8)]('|');let _0x6c38ea=-0x930+0x66a+0x2c6;while(!![]){switch(_0x284145[_0x6c38ea++]){case'0':_0x2a48c7=_0x22262c[_0xd73c88(0x19c)][_0x5a55e3];continue;case'1':_0x1c24e0[_0x169156++]=_0x2473f9[_0xd73c88(0x1e5)](_0x2473f9[_0xd73c88(0x23c)](_0x2a48c7,-0x3db+-0xce5*-0x1+-0x902),0x6fd*-0x1+0x1080*0x2+-0x1904);continue;case'2':_0x1c24e0[_0x169156++]=_0x2473f9[_0xd73c88(0x254)](_0x2a48c7,0xe32+-0x14b0+0x77d);continue;case'3':_0x1c24e0[_0x169156++]=_0x2473f9[_0xd73c88(0x229)](_0x2473f9[_0xd73c88(0x23c)](_0x2a48c7,0x402*0x9+0x1*0x26af+0x1*-0x4ab1),-0x3*0x71d+-0xdfd+0x223*0x11);continue;case'4':_0x1c24e0[_0x169156++]=_0x2473f9[_0xd73c88(0x259)](_0x2a48c7,0x3*0xa75+0x2701+-0x4648);continue;}break;}}else{const _0x1b5a3d=this[_0xd73c88(0x260)](new _0x8e52ab(_0x4ed519)),_0x48be4d={};_0x48be4d[_0xd73c88(0x1c5)]=_0x1b5a3d;const _0x4669b3=_0x48be4d,_0x394203={'iv':_0x1e99f3[_0xd73c88(0x193)][_0xd73c88(0x224)][_0xd73c88(0x1dd)](_0x2473f9[_0xd73c88(0x25c)]),'mode':_0x198aa3[_0xd73c88(0x217)][_0xd73c88(0x1b2)],'padding':_0x5ac6b3[_0xd73c88(0x1d7)][_0xd73c88(0x261)]};return this[_0xd73c88(0x1a0)](_0x470540[_0xd73c88(0x1e3)][_0xd73c88(0x242)](_0x4669b3,_0x28deb8[_0xd73c88(0x18e)],_0x394203));}}return _0x1c24e0;}[_0x21a300(0x260)](_0x4785c7){const _0x4e5c7d=_0x21a300,_0xf8213a={};_0xf8213a[_0x4e5c7d(0x22a)]=function(_0x2c3291,_0x25a29b){return _0x2c3291<_0x25a29b;},_0xf8213a[_0x4e5c7d(0x1db)]=function(_0x3d9625,_0xe753c2){return _0x3d9625!==_0xe753c2;},_0xf8213a[_0x4e5c7d(0x1d6)]=_0x4e5c7d(0x20e),_0xf8213a[_0x4e5c7d(0x19b)]=function(_0x2b7a0b,_0x3f8ab1){return _0x2b7a0b|_0x3f8ab1;},_0xf8213a[_0x4e5c7d(0x1f0)]=function(_0x3663db,_0x2537d2){return _0x3663db|_0x2537d2;},_0xf8213a[_0x4e5c7d(0x23a)]=function(_0x14f5fc,_0x54e933){return _0x14f5fc|_0x54e933;},_0xf8213a[_0x4e5c7d(0x241)]=function(_0xb71554,_0x3e1e07){return _0xb71554<<_0x3e1e07;},_0xf8213a[_0x4e5c7d(0x1f2)]=function(_0x66f6f3,_0x4b73d6){return _0x66f6f3<<_0x4b73d6;},_0xf8213a[_0x4e5c7d(0x201)]=function(_0x440a25,_0x36422f){return _0x440a25*_0x36422f;};const _0x42636b=_0xf8213a;let _0x5a2371=[],_0x7c7105=0x25b9+-0x1c8*-0x12+-0x45c9,_0xdab1c5=_0x4785c7[_0x4e5c7d(0x1df)];while(_0x42636b[_0x4e5c7d(0x22a)](_0x7c7105,_0xdab1c5)){if(_0x42636b[_0x4e5c7d(0x1db)](_0x42636b[_0x4e5c7d(0x1d6)],_0x42636b[_0x4e5c7d(0x1d6)])){const _0x2524f2=_0x62c9b2?function(){const _0x4e5f88=_0x4e5c7d;if(_0x4c654d){const _0x3fb6f3=_0x5c6018[_0x4e5f88(0x19a)](_0x1e1e87,arguments);return _0x37a9d2=null,_0x3fb6f3;}}:function(){};return _0x21e2bf=![],_0x2524f2;}else _0x5a2371[_0x4e5c7d(0x24c)](_0x42636b[_0x4e5c7d(0x19b)](_0x42636b[_0x4e5c7d(0x1f0)](_0x42636b[_0x4e5c7d(0x23a)](_0x42636b[_0x4e5c7d(0x241)](_0x4785c7[_0x7c7105++],0x1*-0x1096+0x493*-0x1+0x1541),_0x42636b[_0x4e5c7d(0x241)](_0x4785c7[_0x7c7105++],-0xe2*0xb+-0x1*0x701+0x10c7)),_0x42636b[_0x4e5c7d(0x1f2)](_0x4785c7[_0x7c7105++],-0x14e*0x13+-0x18b+-0x11*-0x18d)),_0x4785c7[_0x7c7105++]));}return{'sigBytes':_0x42636b[_0x4e5c7d(0x201)](_0x5a2371[_0x4e5c7d(0x1df)],-0x10c*0xd+-0x14a7+0x2247),'words':_0x5a2371};}} +export default MangaDig; diff --git a/src/web/mjs/connectors/ReadNovelFull.mjs b/src/web/mjs/connectors/ReadNovelFull.mjs new file mode 100644 index 00000000000..197055fea9f --- /dev/null +++ b/src/web/mjs/connectors/ReadNovelFull.mjs @@ -0,0 +1,108 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; + +export default class ReadNovelFull extends Connector { + + constructor() { + super(); + super.id = 'readnovelfull'; + super.label = 'ReadNovelFull'; + this.tags = [ 'novel', 'english' ]; + this.url = 'https://readnovelfull.com'; + this.path = '/novel-list/latest-release-novel'; + this.queryMangas = 'h3.novel-title a'; + + this.novelContentQuery = 'div#chr-content'; + //this.novelObstaclesQuery = 'div.code-block'; + this.novelFormat = 'image/png'; + this.novelWidth = '56em'; // parseInt(1200 / window.devicePixelRatio) + 'px'; + this.novelPadding = '1.5em'; + } + + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'meta[property="og:title"]'); + return new Manga(this, uri.pathname, data[0].content.trim()); + } + + async _getMangas() { + let mangaList = []; + const uri = new URL(this.path, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'li.last a'); + const pageCount = parseInt(data[0].getAttribute('data-page'))+1; + for(let page = 1; page <= pageCount; page++) { + let mangas = await this._getMangasFromPage(page); + mangaList.push(...mangas); + } + return mangaList; + } + + async _getMangasFromPage(page) { + let uri = new URL(this.path+'?page=' + page, this.url); + let request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, this.queryMangas); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, request.url), + title: element.text.trim() + }; + }); + } + async _getChapters(manga) { + let uri = new URL(manga.id, this.url); + let request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, 'div#rating'); + const mangaid = data[0].getAttribute('data-novel-id'); + + let apiuri = new URL('/ajax/chapter-archive?novelId='+mangaid, this.url); + request = new Request(apiuri, this.requestOptions); + request.headers.set('x-referer', uri); + request.headers.set('X-Requested-With', 'XMLHttpRequest'); + data = await this.fetchDOM(request, 'a'); + + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.text.trim() + }; + }).reverse(); + } + + async _getPages(chapter) { + const uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + return this._getPagesNovel(request); + + } + async _getPagesNovel(request) { + let darkmode = Engine.Settings.NovelColorProfile(); + let script = ` + new Promise(resolve => { + document.body.style.width = '${this.novelWidth}'; + let novel1 = document.querySelector('${this.novelContentQuery}'); + novel1.style.padding = '${this.novelPadding}'; + [...novel1.querySelectorAll(":not(:empty)")].forEach(ele => { + ele.style.backgroundColor = '${darkmode.background}' + ele.style.color = '${darkmode.text}' + }) + novel1.style.backgroundColor = '${darkmode.background}' + novel1.style.color = '${darkmode.text}' + let script = document.createElement('script'); + script.onerror = error => reject(error); + script.onload = async function() { + try{ + let canvas = await html2canvas(novel1); + resolve(canvas.toDataURL('${this.novelFormat}')); + }catch (error){ + reject(error) + } + } + script.src = 'https://html2canvas.hertzen.com/dist/html2canvas.min.js'; + document.body.appendChild(script); + }); + `; + return [ await Engine.Request.fetchUI(request, script, 30000, true) ]; + } + +} \ No newline at end of file diff --git a/src/web/mjs/connectors/TopMangasFR.mjs b/src/web/mjs/connectors/TopMangasFR.mjs new file mode 100644 index 00000000000..e1e53cb08f2 --- /dev/null +++ b/src/web/mjs/connectors/TopMangasFR.mjs @@ -0,0 +1,55 @@ +import Connector from '../engine/Connector.mjs'; + +export default class TopMangasFR extends Connector { + + constructor() { + super(); + super.id = 'topmangasfr'; + super.label = 'TopMangasFR'; + this.tags = [ 'manga', 'french' ]; + this.url = 'https://topmangas.fr'; + + } + + async _getMangas() { + const request = new Request(this.url, this.requestOptions); + const data = await this.fetchDOM(request, 'div.u-repeater div.u-list-item h4 a'); + return data.map(element => { + return { + id: element.href, + title : `${element.text.trim()} [${this._getTopLevelDomain(new URL(element.href))}]` + }; + }); + } + + _getTopLevelDomain(uri) { + return uri.hostname.split('.').slice(-2).join('.'); + } + + async _getChapters(manga) { + const request = new Request(manga.id, this.requestOptions); + const data = await this.fetchDOM(request, 'div#All_chapters ul li ul li a,div#Chapters_List ul ul li a'); + return data.map(element => { + return { + id: element.href, + title : element.text.replace(/scan/i, '').replace(/manga/i, '').replace(/chapitre/i, '').trim() + }; + }); + } + + async _getPages(chapter) { + const request = new Request(new URL(chapter.id, this.url), this.requestOptions); + const data = await this.fetchDOM(request, 'source.wp-manga-chapter-img, source[decoding], source.img-responsive'); + return data.map(element => { + let url = element.dataset['lazySrc'] ? element.dataset['lazySrc'] : element; + url = this.getAbsolutePath(url, request.url); + //HACK some pages are from wp cache BUT source website is dead (sushiscan), we add a WP pattern to bypass late filter and ensure pics are loaded from WP + let realwp = url.match(/\/i\d+\.wp\.com/); + if (realwp != null) { + //replace /ix.wp.com with /ix.wp.com/i0.wp.com + url = url.replace(realwp[0], realwp[0]+'/i0.wp.com'); + } + return url; + }); + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/TopToonGlobal.mjs b/src/web/mjs/connectors/TopToonGlobal.mjs new file mode 100644 index 00000000000..e7b130aefb6 --- /dev/null +++ b/src/web/mjs/connectors/TopToonGlobal.mjs @@ -0,0 +1,122 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class TopToonGlobal extends Connector { + constructor() { + super(); + super.id = 'toptoonglobal'; + super.label = 'TOPTOON Global (English)'; + this.tags = [ 'webtoon', 'english' ]; + this.url = 'https://global.toptoon.com'; + this.api ='https://api.toptoonplus.com' + } + /* + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.bnr_episode_info p.tit_toon'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); + } + */ + async _getMangas() { + const request = new Request(new URL('/api/v1/page/genres', this.api), this.requestOptions); + request.headers.set('language', 'en'); + request.headers.set('ua', 'web'); + request.headers.set('x-api-key', 'SUPERCOOLAPIKEY2021#@#('); + request.headers.set('x-origin', 'global.toptoon.com'); + request.headers.set('user-id', ''); + request.headers.set('partnerCode', ''); + + + const data = await this.fetchJSON(request); + return data.data.genres.map(item => { + let link = '/content/'+item.information.title; + link = link.replace(' ', '-'); + link = link.replace(/[\?]/g, ''); + link = link + '/'+ item.comicId; + return{ + id: link, + title: item.information.title + }; + }); + } + async _getChapters(manga) { + const mangaid = manga.id.match(/\/([0-9]+$)/)[1]; + const request = new Request(new URL('/api/v1/page/episode?comicId='+mangaid, this.api), this.requestOptions); + request.headers.set('language', 'en'); + request.headers.set('ua', 'web'); + request.headers.set('x-api-key', 'SUPERCOOLAPIKEY2021#@#('); + request.headers.set('x-origin', 'global.toptoon.com'); + request.headers.set('user-id', ''); + request.headers.set('partnerCode', ''); + + const data = await this.fetchJSON(request); + return data.data.episode.map(item => { + let link = '/content/content/'+mangaid+'/'+item.episodeId; + return{ + id: link, + title: item.information.title + }; + }) + .reverse(); + } + async _getPages(chapter) { + const mangaid = chapter.id.match(/content\/([0-9]+)/)[1]; + const chapterid = chapter.id.match(/\/([0-9]+$)/)[1]; + + let request = new Request(new URL(this.url), this.requestOptions); + let secretKey = await Engine.Request.fetchUI(request, 'localStorage.getItem("udid") || ""'); + + + const uri = new URL('/api/v2/viewer/'+mangaid+'/'+chapterid, this.api); + let body = { + action : 'view_contents', + cToken : '', + isCached : false, + location : 'viewer', + }; + + let pdata = this.generatePayloadData(secretKey); + + request = new Request(uri, { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'Accept': '*/*', + 'Content-Type': 'application/json', + 'x-referrer': this.url, + 'language': 'en', + 'ua':'web', + 'x-api-key': pdata.k, + 'Origin': 'global.toptoon.com', + 'x-origin': 'global.toptoon.com', + 'user-id': '', + 'deviceId': secretKey, + 'partnerCode':'', + 'timestamp' : pdata.t, + 'version' : '1.18.189a', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:107.0) Gecko/20100101 Firefox/107.0', + } + }); + let data = await fetch(request); + data = await data.json(); + } + + generatePayloadData(uuid){ + //timestampUTC+MS + + //var hash = r.createHash('sha256').update(uuid.toString().replace(/-/g, ''.concat(timestamp))).digest('hex'); + //return r.pbkdf2Sync(''.concat(uuid, '|').concat(timestamp), hash, iterations (for now 257), hash.length, 'sha512').toString('base64') + //this has been tested with fixed values and it works + + let timestamp = Date.now().toString()+ (Math.trunc(Math.random() * (999 - 100) + 100)).toString(); + let message = uuid.toString().replace(/-/g, ''.concat(timestamp)); + let salt = CryptoJS.SHA256(message).toString(); + let cfg = { + keySize: 64, + hasher: CryptoJS.algo.SHA512, + iterations: 257 + }; + message = uuid + '|'+timestamp; + let apikey = CryptoJS.PBKDF2(message, salt, cfg).toString(CryptoJS.enc.Base64); + return {k : apikey, t : timestamp } + } +} diff --git a/src/web/mjs/connectors/kakaopage.mjs b/src/web/mjs/connectors/kakaopage.mjs index 0774a99b071..83c4f4e91b4 100644 --- a/src/web/mjs/connectors/kakaopage.mjs +++ b/src/web/mjs/connectors/kakaopage.mjs @@ -1,18 +1,16 @@ import Connector from '../engine/Connector.mjs'; import Manga from '../engine/Manga.mjs'; - export default class kakaopage extends Connector { - constructor() { super(); super.id = 'kakaopage'; super.label = 'Page Kakao (카카오페이지) '; this.tags = [ 'webtoon', 'manga', 'korean' ]; this.url = 'https://page.kakao.com'; + this.api = 'https://page.kakao.com/graphql'; this.requestOptions.headers.set('x-origin', this.url); this.requestOptions.headers.set('x-referer', this.url); } - async _getMangaFromURI(uri) { uri = new URL(uri); const request = new Request(uri, this.requestOptions); @@ -21,12 +19,10 @@ export default class kakaopage extends Connector { let title = data[0].content.trim(); return new Manga(this, id, title); } - async _getMangas() { let msg = 'This website does not provide a manga list, please copy and paste the URL containing the images directly from your browser into HakuNeko.'; throw new Error(msg); } - async _getChapters(manga) { let nextCursor = ''; const chapterList = []; diff --git a/src/web/mjs/connectors/templates/Lezhin.mjs b/src/web/mjs/connectors/templates/Lezhin.mjs index acd7521be18..88e54029ff2 100644 --- a/src/web/mjs/connectors/templates/Lezhin.mjs +++ b/src/web/mjs/connectors/templates/Lezhin.mjs @@ -1,8 +1,6 @@ import Connector from '../../engine/Connector.mjs'; import Manga from '../../engine/Manga.mjs'; - export default class Lezhin extends Connector { - constructor() { super(); super.id = undefined; @@ -88,7 +86,6 @@ export default class Lezhin extends Connector { return fetch(`${this.url}/locale/${this.locale}`, this.requestOptions); } - async _getMangaFromURI(uri) { const request = new Request(uri, this.requestOptions); const data = await this.fetchDOM(request, 'div.comicInfo__detail h2.comicInfo__title'); @@ -164,7 +161,6 @@ export default class Lezhin extends Connector { const request = new Request(`${this.url}/comic/${manga.id}`, this.requestOptions); return await Engine.Request.fetchUI(request, script); } - async _getPages(chapter) { await this._initializeAccount(); diff --git a/src/web/mjs/videostreams/Kaas.mjs b/src/web/mjs/videostreams/Kaas.mjs new file mode 100644 index 00000000000..681d4dfc9de --- /dev/null +++ b/src/web/mjs/videostreams/Kaas.mjs @@ -0,0 +1,43 @@ +export default class Kaas { + + constructor(url) { + this._uri = new URL(url); + } + + async getStream() { + + + if (this._uri.href.match(/\/axplayer/)){ + const realurl = decodeURI(this._uri.searchParams.get('data')); + + throw new Error('Kaas : this use an external player, you need to implement it :/'); + + } + + if (this._uri.href.match(/\/dust\/player/)){ + //get window.sources + let script = ` + new Promise(resolve => { + resolve(sources); + }); + `; + let request = new Request(this._uri); + const sources = await Engine.Request.fetchUI(request, script, 3000); + + const mav = sources.find(element => element.name.match(/MAVERICK/i)).src; + const referer = new URL(mav); + + const videoid = referer.href.match(/\/embed\/(\S+)$/)[1]; + const apiurl = new URL('/api/source/'+videoid, referer.origin); + + request = new Request(apiurl); + request.headers.set('x-referer', referer); + request.headers.set('accept', 'application/json, text/plain, */*'); + const response = await fetch(request); + const data = await response.json(); + return new URL(data.hls, referer.origin).href; + } + + + } +} \ No newline at end of file diff --git a/src/web/mjs/videostreams/Okru.mjs b/src/web/mjs/videostreams/Okru.mjs new file mode 100644 index 00000000000..282e8d48727 --- /dev/null +++ b/src/web/mjs/videostreams/Okru.mjs @@ -0,0 +1,34 @@ +export default class Okru { + constructor(url) { + this._uri = new URL(url); + } + async getStream(resolution) { + /* + * mobile + * low + * sd + * hd + */ + const script = ` + new Promise((resolve, reject) => { + setTimeout(() => { + try { + const el = document.querySelector('div[data-module="OKVideo"]'); + el.click(); + resolve(el.getAttribute('data-options')); + } + catch(error) { + reject(error); + } + }, + 3000); + }); + `; + const request = new Request(this._uri); + const response = await Engine.Request.fetchUI(request, script, 10000); + const data = JSON.parse(JSON.parse(response).flashvars.metadata); + let video = (data.videos.find(video => video.name == resolution)); + (video) ? video = video.url : video = data.videos[0].url; + return video; + } +}