From 2bbd9c7537879d2620d7b7bd0dead0b892295caf Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 27 Nov 2022 15:09:19 +0100 Subject: [PATCH 01/47] Add Manhwa18.cc Fixes https://github.com/manga-download/hakuneko/issues/4016 --- src/web/mjs/connectors/Manhwa18cc.mjs | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/web/mjs/connectors/Manhwa18cc.mjs diff --git a/src/web/mjs/connectors/Manhwa18cc.mjs b/src/web/mjs/connectors/Manhwa18cc.mjs new file mode 100644 index 00000000000..2bff461a934 --- /dev/null +++ b/src/web/mjs/connectors/Manhwa18cc.mjs @@ -0,0 +1,44 @@ +import WordPressMadara from './templates/WordPressMadara.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class Manhwa18cc extends WordPressMadara { + constructor() { + super(); + super.id = 'manhwa18cc'; + super.label = 'Manhwa 18 (.cc)'; + this.tags = [ 'webtoon', 'hentai', 'multi-lingual' ]; + this.url = 'https://manhwa18.cc'; + this.path ='/webtoons'; + this.queryMangas = 'div.manga-item div.thumb a'; + this.mangaNumberPerPage = 24; + this.queryChapters ='div#chapterlist li a'; + this.queryPages = 'div.read-manga div.read-content source'; + } + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.post-title > h1'); + const id = uri.pathname; + const title = data[0].textContent.replace('18+', '').trim(); + return new Manga(this, id, title); + } + async _getMangas() { + let mangaList = []; + for (let page = 1, run = true; run; page++) { + let mangas = await this._getMangasFromPage(page); + mangaList.push(...mangas); + if (mangas.length != this.mangaNumberPerPage){ + run = false; + } + } + return mangaList; + } + async _getMangasFromPage(page) { + let request = new Request(new URL(this.path+'/'+page, this.url), this.requestOptions); + let data = await this.fetchDOM(request,this.queryMangas); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, request.url), + title: element.title.trim() + }; + }); + } +} \ No newline at end of file From d5fc7bc63e97eda2f2dfbf045b9426ad74744bcb Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 27 Nov 2022 15:10:38 +0100 Subject: [PATCH 02/47] manhwa18cc icon --- src/web/img/manhwa18cc | Bin 0 -> 1098 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/manhwa18cc diff --git a/src/web/img/manhwa18cc b/src/web/img/manhwa18cc new file mode 100644 index 0000000000000000000000000000000000000000..72bb76c50880a876e4d2da448aa3586170ea826a GIT binary patch literal 1098 zcmV-Q1hxB#P)6WV~5P&ZND&mTYtcCCYlo!Qx?8Nj;r{|oRbB7;U zpz5R{tF{}dmQ@s?UCI8e4M^LMs^2qAKnZ&tP1;UWY}=;c zsIt$1x*Bp1Ewjb2>C;x>Hu${rHNs{$vEVapqJG}0?fU@$SSX}MqCK8BLz)0&44(FV zIg@_VkDpLXF@iZjK0E;wT6{^fIanm)8gXl!365~AfpZ8hZ^A00n?_<+y~#WPrp$&h z6A(`$>d|5K1yaFgC@yw6gbCZ#Qsh19R$xdy0eJL1YPoHp>c3X0QE-Ep)IAuJ^~;no2N3Y(zM3mZcEz3g9^wQ+~9)+Q$6Z z;!WqwfFQSaSWV^h3NYZRxi`*StqUovrQG@U)KF|k)X1Pp1l*~*<)9OJ^R0>{!bZadOZ36?U+=*zx9lm5;1B5H-uVi`J&$79wcq2UT( z68s}wAU;^EuAoMd`_YZNP%8Y7jb&ddPRZOyTUUiEr|-d&r05l3*_YaL`ZHYkb;jpb zXZQ0YuPw;$!fw$t0;41EI|%Bv1?KGFJuKTWNB6BkkmUm)QZ!uwe38!h=!k}udUwOL zt|O6$VLC2=9l4FG=5&qs9;iD{G#BaMm>N~Db{jaywz z&bp?zJh_g>vK7`acK!_{ziHx^%hXe(fv!;oD>DUfDl$@%-`C_g@~r0r*TTrbcTN Q&;S4c07*qoM6N<$g1%!J6#xJL literal 0 HcmV?d00001 From 2857f28772fd378334ce14e24b2d2267382ca8d1 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 27 Nov 2022 19:35:54 +0100 Subject: [PATCH 03/47] Add MadTheme template Cover TrueManga, MangaBuddy, possibly MangaReaderTo --- src/web/mjs/connectors/templates/MadTheme.mjs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/web/mjs/connectors/templates/MadTheme.mjs diff --git a/src/web/mjs/connectors/templates/MadTheme.mjs b/src/web/mjs/connectors/templates/MadTheme.mjs new file mode 100644 index 00000000000..2ea3ad84f16 --- /dev/null +++ b/src/web/mjs/connectors/templates/MadTheme.mjs @@ -0,0 +1,60 @@ +import Connector from '../../engine/Connector.mjs'; +import Manga from '../../engine/Manga.mjs'; +export default class MadTheme extends Connector { + constructor() { + super(); + super.id = undefined; + super.label = undefined; + this.tags = []; + this.url = undefined; + this.path = '/az-list?page='; + this.queryMangaTitleFromURI = 'div.name.box h1'; + this.queryMangas = 'div.thumb a'; + this.queryPages = 'div.chapter-image'; + this.queryChapterTitle = 'strong.chapter-title'; + } + async _getMangaFromURI(uri) { + const request = new Request(new URL(uri), this.requestOptions); + const data = await this.fetchDOM(request, this.queryMangaTitleFromURI); + const id = uri.pathname; + const title = data[0].textContent.trim(); + return new Manga(this, id, title); + } + async _getMangas() { + const mangaList = []; + for(let page = 1, run = true; run; page++) { + const mangas = await this._getMangasFromPage(page); + mangas.length ? mangaList.push(...mangas) : run = false; + } + return mangaList; + } + async _getMangasFromPage(page) { + const uri = new URL(this.path + page, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, this.queryMangas); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.title.trim() + }; + }); + } + async _getChapters(manga) { + let uri = new URL('/api/manga'+manga.id+'/chapters?source=detail', this.url); + let request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, 'a'); + return data.map(element => { + const link = element.pathname; + const title = element.querySelector(this.queryChapterTitle).textContent.trim(); + return { + id: link, + title: title, + }; + }); + } + async _getPages(chapter) { + const request = new Request(new URL(chapter.id, this.url), this.requestOptions); + const data = await this.fetchDOM(request, this.queryPages); + return data.map(ele => ele.children[0].dataset.src); + } +} \ No newline at end of file From df80aa4377c9cbee8a6e803ddcaf518aeaac2f75 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 27 Nov 2022 19:38:45 +0100 Subject: [PATCH 04/47] MadTheme Websites * Changed TrueManga to use the new template * Add Mangabuddy connector using the new template --- src/web/mjs/connectors/MangaBuddy.mjs | 29 +++++++++++++++++++++++++++ src/web/mjs/connectors/TrueManga.mjs | 11 ++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/web/mjs/connectors/MangaBuddy.mjs create mode 100644 src/web/mjs/connectors/TrueManga.mjs diff --git a/src/web/mjs/connectors/MangaBuddy.mjs b/src/web/mjs/connectors/MangaBuddy.mjs new file mode 100644 index 00000000000..e565674a53d --- /dev/null +++ b/src/web/mjs/connectors/MangaBuddy.mjs @@ -0,0 +1,29 @@ +import MadTheme from './templates/MadTheme.mjs'; +export default class MangaBuddy extends MadTheme { + constructor() { + super(); + super.id = 'mangabuddy'; + super.label = 'MangaBuddy'; + this.tags = ['manga', 'webtoon', 'english']; + this.url = 'https://mangabuddy.com'; + } + async _getPages(chapter) { + let scriptPages = ` + new Promise(resolve => { + resolve(final_images); + }); + `; + let request = new Request(this.url + chapter.id, this.requestOptions); + let data = await Engine.Request.fetchUI(request, scriptPages); + return data.map(element => this.createConnectorURI(this.getAbsolutePath(element, request.url))); + } + async _handleConnectorURI(payload) { + let request = new Request(payload, this.requestOptions); + request.headers.set('x-referer', this.url); + let response = await fetch(request); + let data = await response.blob(); + data = await this._blobToBuffer(data); + this._applyRealMime(data); + return data; + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/TrueManga.mjs b/src/web/mjs/connectors/TrueManga.mjs new file mode 100644 index 00000000000..6895ff37ca5 --- /dev/null +++ b/src/web/mjs/connectors/TrueManga.mjs @@ -0,0 +1,11 @@ +import MadTheme from './templates/MadTheme.mjs'; + +export default class TrueManga extends MadTheme { + constructor() { + super(); + super.id = 'truemanga'; + super.label = 'TrueManga'; + this.tags = ['manga', 'webtoon', 'english']; + this.url = 'https://truemanga.com'; + } +} From d21bc149c50d2b758976f3e9f8d8b5cd6d5a9d32 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 27 Nov 2022 19:39:34 +0100 Subject: [PATCH 05/47] Add Mangabuddy Icon --- src/web/img/connectors/mangabuddy | Bin 0 -> 1098 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/mangabuddy diff --git a/src/web/img/connectors/mangabuddy b/src/web/img/connectors/mangabuddy new file mode 100644 index 0000000000000000000000000000000000000000..72bb76c50880a876e4d2da448aa3586170ea826a GIT binary patch literal 1098 zcmV-Q1hxB#P)6WV~5P&ZND&mTYtcCCYlo!Qx?8Nj;r{|oRbB7;U zpz5R{tF{}dmQ@s?UCI8e4M^LMs^2qAKnZ&tP1;UWY}=;c zsIt$1x*Bp1Ewjb2>C;x>Hu${rHNs{$vEVapqJG}0?fU@$SSX}MqCK8BLz)0&44(FV zIg@_VkDpLXF@iZjK0E;wT6{^fIanm)8gXl!365~AfpZ8hZ^A00n?_<+y~#WPrp$&h z6A(`$>d|5K1yaFgC@yw6gbCZ#Qsh19R$xdy0eJL1YPoHp>c3X0QE-Ep)IAuJ^~;no2N3Y(zM3mZcEz3g9^wQ+~9)+Q$6Z z;!WqwfFQSaSWV^h3NYZRxi`*StqUovrQG@U)KF|k)X1Pp1l*~*<)9OJ^R0>{!bZadOZ36?U+=*zx9lm5;1B5H-uVi`J&$79wcq2UT( z68s}wAU;^EuAoMd`_YZNP%8Y7jb&ddPRZOyTUUiEr|-d&r05l3*_YaL`ZHYkb;jpb zXZQ0YuPw;$!fw$t0;41EI|%Bv1?KGFJuKTWNB6BkkmUm)QZ!uwe38!h=!k}udUwOL zt|O6$VLC2=9l4FG=5&qs9;iD{G#BaMm>N~Db{jaywz z&bp?zJh_g>vK7`acK!_{ziHx^%hXe(fv!;oD>DUfDl$@%-`C_g@~r0r*TTrbcTN Q&;S4c07*qoM6N<$g1%!J6#xJL literal 0 HcmV?d00001 From 98530add37c49205cb1e23ad4cd6f8a9302f05cf Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 28 Nov 2022 19:10:47 +0100 Subject: [PATCH 06/47] Streaming handlers enhancement * Fix Streamtape * Add Mav, VoeSX, and SendVid as streaming handlers --- src/web/mjs/videostreams/Mav.mjs | 32 +++++++++++++++++++++++++ src/web/mjs/videostreams/SendVid.mjs | 16 +++++++++++++ src/web/mjs/videostreams/Streamtape.mjs | 2 +- src/web/mjs/videostreams/VoeSX.mjs | 16 +++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/web/mjs/videostreams/Mav.mjs create mode 100644 src/web/mjs/videostreams/SendVid.mjs create mode 100644 src/web/mjs/videostreams/VoeSX.mjs diff --git a/src/web/mjs/videostreams/Mav.mjs b/src/web/mjs/videostreams/Mav.mjs new file mode 100644 index 00000000000..11f4bc7a9b2 --- /dev/null +++ b/src/web/mjs/videostreams/Mav.mjs @@ -0,0 +1,32 @@ +export default class Mav { + constructor(originwebsite, url) { + this._uri = new URL(url); + this.hostname = this._uri.hostname; + this.videoid = url.match(/\/v\/([\S]+)/)[1]; + this.website = originwebsite; + } + async getStream() { + let uri = new URL('/api/source/'+this.videoid ,this._uri.origin); + let body = { + 'r': this.website, + 'd': this.hostname, + }; + const request = new Request(uri, { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'x-origin': this._uri.origin, + 'x-referer': this._uri, + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'X-Requested-With': 'XMLHttpRequest', + 'Alt-Used': this.hostname, + } + }); + const response = await fetch(request); + let data = await response.json(); + return { + file : data.data[0].file, + type : data.data[0].type + }; + } +} \ No newline at end of file diff --git a/src/web/mjs/videostreams/SendVid.mjs b/src/web/mjs/videostreams/SendVid.mjs new file mode 100644 index 00000000000..a75d1213569 --- /dev/null +++ b/src/web/mjs/videostreams/SendVid.mjs @@ -0,0 +1,16 @@ +export default class SendVid { + + constructor(url) { + this._uri = new URL(url); + } + + async getStream() { + const script = ` + new Promise(resolve => { + resolve(video_source); + }); + `; + const request = new Request(this._uri); + return Engine.Request.fetchUI(request, script); + } +} \ No newline at end of file diff --git a/src/web/mjs/videostreams/Streamtape.mjs b/src/web/mjs/videostreams/Streamtape.mjs index bdb2c084ee8..6960e09803c 100644 --- a/src/web/mjs/videostreams/Streamtape.mjs +++ b/src/web/mjs/videostreams/Streamtape.mjs @@ -8,7 +8,7 @@ export default class Streamtape { const script = ` new Promise(resolve => { for(let count = 0; count < 3; count++) { - document.querySelector('.plyr-overlay').click(); + document.querySelector('.play-overlay').click(); } resolve(new URL(document.getElementById('mainvideo').src, window.location.origin).href); }); diff --git a/src/web/mjs/videostreams/VoeSX.mjs b/src/web/mjs/videostreams/VoeSX.mjs new file mode 100644 index 00000000000..5aa7f8565c4 --- /dev/null +++ b/src/web/mjs/videostreams/VoeSX.mjs @@ -0,0 +1,16 @@ +export default class VoeSX { + + constructor(url) { + this._uri = new URL(url); + } + + async getStream() { + const script = ` + new Promise(resolve => { + resolve(sources); + }); + `; + const request = new Request(this._uri); + return Engine.Request.fetchUI(request, script); + } +} \ No newline at end of file From 6da11c1b12fddf1b66de9ad011ac0cee76c85d89 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 28 Nov 2022 20:19:51 +0100 Subject: [PATCH 07/47] Add Mavanimes.co --- src/web/mjs/connectors/MavAnimes.mjs | 129 +++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/web/mjs/connectors/MavAnimes.mjs diff --git a/src/web/mjs/connectors/MavAnimes.mjs b/src/web/mjs/connectors/MavAnimes.mjs new file mode 100644 index 00000000000..3e681bf3fe2 --- /dev/null +++ b/src/web/mjs/connectors/MavAnimes.mjs @@ -0,0 +1,129 @@ +import Connector from '../engine/Connector.mjs'; +import Streamtape from '../videostreams/Streamtape.mjs'; +import Mav from '../videostreams/Mav.mjs'; +import SendVid from '../videostreams/SendVid.mjs'; +import VoeSX from '../videostreams/VoeSX.mjs'; + +export default class MavAnime extends Connector { + constructor() { + super(); + super.id = 'mavanimes'; + super.label = 'MavAnimes'; + this.tags = [ 'anime', 'french', 'multi-lingual' ]; + this.url = 'https://mavanimes.co'; + this.genres = ['/tous-les-animes-en-vf','/tous-les-animes-en-vostfr-fullhd-2']; + } + + async _getMangas() { + let mangaslist = []; + for (let i = 0;i < this.genres.length;i++) + { + let request = new Request( new URL(this.genres[i], this.url), this.requestOptions ); + let data = await this.fetchDOM( request, 'div#az-slider li a' ); + let mangas = data.map(element => { + return { + id : element.pathname, + title : element.text.trim() + } + }); + mangaslist.push(...mangas); + } + return(mangaslist); + } + + async _getChapters(manga) { + let chapterslist = []; + let request = new Request( new URL(manga.id, this.url), this.requestOptions ); + let data = await this.fetchDOM( request, 'body' ); + //first attempt to get the episodes list and various hosts + let chaptersNodes = data[0].querySelectorAll('header.entry-header h2 a'); + for (let i = 0;i < chaptersNodes.length;i++) { + chapterslist.push(...await this.getChapterPlayers(chaptersNodes[i])); + } + //second attempts, with another CSS Selector + if (chapterslist.length == 0){ + chaptersNodes = data[0].querySelectorAll('div.entry-content a'); + for (let i = 0;i < chaptersNodes.length;i++) { + chapterslist.push(...await this.getChapterPlayers(chaptersNodes[i])); + } + } + //if nothing worked, check if the page itself got iframes + // Somes film does not have a chapter page and just one page with video frames + if (chapterslist.length == 0){ + try{ + let testnode= document.createElement("a"); + testnode.href = new URL(manga.id, this.url); + testnode.text = manga.title; + chapterslist.push(...await this.getChapterPlayers(testnode)); + } + catch(e) + { + } + } + if (chapterslist.length == 0){ + throw('No episode / supported video hoster found :/ !'); + } + return(chapterslist); + } + + async getChapterPlayers(chapterNode){ + let request = new Request(chapterNode.href, this.requestOptions); + let scriptPages = ` + new Promise(resolve => { + resolve([...document.querySelectorAll('iframe')].map(el => el.src)); + }); + `; + let data = await Engine.Request.fetchUI(request, scriptPages); + return data.map(element => { + let sourcesite = this.getWebsiteTag(element); + return { + id: element, + title : sourcesite + ' '+ chapterNode.text.replace(':•', '').trim() + } + }).filter(el => (!el.title.match(/\[UNK\]/))); + } + + async _getPages(chapter) { + + let sourcesite = this.getWebsiteTag(chapter.id); + switch(sourcesite){ + case '[MAV]': + { + let vid = await new Mav(this.url,chapter.id).getStream(); + return (vid.type == 'mp4') ? {video: vid, subtitles: [] }: {hash: 'id,language,resolution', mirrors: [ vid ], subtitles: [], referer : chapter.id }; + break; + } + case '[Streamtape]': + { + let vid = await new Streamtape(chapter.id).getStream(); + return{video: vid, subtitles: [] }; + break; + } + case '[SendVid]': + { + let vid = await new SendVid(chapter.id).getStream(); + return {hash: 'id,language,resolution', mirrors: [ vid ], subtitles: [], referer : chapter.id }; + break; + } + case '[VOESX]': + { + let vid = await new VoeSX(chapter.id).getStream(); + return (vid.mp4) ? {video: vid.mp4, subtitles: [] }: {hash: 'id,language,resolution', mirrors: [ vid.hls ], subtitles: [], referer : chapter.id }; + break; + } + default: + break; + } + + + } + + getWebsiteTag(link){ + let sourcesite = '[UNK]'; + sourcesite = (link.match(/mavplay|mavlecteur|mavavid/)) ? '[MAV]' : sourcesite; + sourcesite = (link.match(/streamtape/)) ? '[Streamtape]' : sourcesite; + sourcesite = (link.match(/sendvid.com/)) ? '[SendVid]' : sourcesite + sourcesite = (link.match(/voe.sx/)) ? '[VOESX]' : sourcesite; + return sourcesite; + }; +} \ No newline at end of file From 75944d733e0f11f75fee1c1438559cbd7ccff28a Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 28 Nov 2022 20:22:26 +0100 Subject: [PATCH 08/47] add mavanimes icon --- src/web/img/connectors/mavanimes | Bin 0 -> 5913 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/mavanimes diff --git a/src/web/img/connectors/mavanimes b/src/web/img/connectors/mavanimes new file mode 100644 index 0000000000000000000000000000000000000000..36cbac33837e49d5d4fdc51a6020d295f4e2ee9a GIT binary patch literal 5913 zcmbW4cTf{fo5w?MVgLm}fhYt}>JOv}Bq~xu5kcuffKUuodItgNO~HtO5R@(@(yR0$ zC?Z`#C-fdl2!wL+y?5?r?ytN1?(WRa?DOnApV{5#nZ5XPF$ZALRD-DjC@3fZmgEMw zm*!czA;?nZU>e@PfZ~x$s za6~*l`O8HCp!!d&|04SzT&!d+O7a;{)BWY5p!6XRDpqQmTavVF5A^7)+^+CRh0=I>r-drqc1Z)>Ya zzBei}|L{W&vB(R+~RWA;55ciiicXb7|!@P z+?3b_z>-J(!Ej5pj3XDg&e~4PK@5!DC9;UAlA7BbgV+|1g_x|ABn_Nf7TR`BZi9Me!c9I$}0?xF< zUrD-Q0n5-6)k7tps2rJ3?ua_ZzrNvCOKtN8A~FIIam1eCrpz5`EBfVyZP-x9&;~lI zt;s`W=oZ65*Qn<-ZRby$Cgc941@xSs9vFoF&;yMcjvhP!)^z1%XSfnlln`GpmDYk{ z`1dj4{gu~8MX(Z&f@P10K6@6 z$K6!XonG-+;igw>?*U!xnG`V`kLd^&B6_%%BK_Z8sTxwvpD^te;!}H4!JK`XFF)Bo zpe^-rZhQ{f77~wlJZHz#X1MTIr|49j{|>h&N%1wD>|wHk?s;_WFbBf$@Gf+8GuLk= zxi32T>ktW_;f3UYHU66P`N4OexFcLqPZKrXQnaoeOhhQLt6owGK1zMMcc~GWG2Cg` z-ZLHU4=+IXgK}nT>`$-uXb@rfI`h!W#Mm+Usv?`%1)@5Ew6V;b+z!)F<&vi{=)Jk$ zI?$cPcL50d(zd{qCg!4KP--syK{8W~KKFIQp42C(_`So!G8IAz*~I45k|>soCFXP) zwjZf9G0KQcs`}*(!T_}ONs-U2!V;PI4(Xd60mkSKT~;Kh;IvHgt{ zF0ely4jU*oTZMpXF94e9FU|*OH`$*644KDyT2G6Y3KxW;i*1ge_q(kSkI{G4j=IoS z&5&Qajr?=BS7m!rBD(#$>BLR~#usW&+0nSWzLhFt1)+w}tf{19fmvdyG`dy;u}pDKW2Wp+dT&mQsB>L4pt3mQxQtiBlhI1NjXLG zsCURcpy0Q9Z=7#4x%NKxR`>?oOEbM~Ug!p?AN0YXAR3dVS0pj7rgPGWJVvtw-j6wC ziZfd_|GdyW!PU5(a7pL_z#E;^UJb2G4~e+$o#S9)Q-!KnJNo=a(GqQEk+oq)PC%-M z<88_k#!;Hh&XB7CCKy~a=c9NM;cf^MWe(T*zFqMtYxRhdS^ketCNo{GTxk{yTrfRR z-GpDpRyC1;Eo$Be{ed!O-FZv(C&9dW#hSkZkS;a1eaAQh_35Ie>o~ah_qr?XitE;n z0EPRn))xCrG?YK0R%uO2XSB)Bq)DP!BVRSlEK~BxQmvaWMU8$_b|A^g=#LS!La5`0 z>qHc6#1qQln~Ymf^7!HC=1jO%(2+M;z7%Q$MZOMGe?R0x^WkN-OyC6Gbv|LB0~$W` zr~2^Qh~x&o_u6)!M&4Fmr46_9Ed=%D*Ss3t(pA}IXq7~%_~h6)m?F3jPIv0aGuBuJ zb7lOKixoIl%HlAKEDcSDKYVPPm-KuO0b_2`+Vn6)fAz z_XcNsB-40=&@4MChc4Ghe0??*?=t|?8*TjX_bX=46 zf_WxwslUyyl}!<~dT--yfHLCj%gk=(v}+1DXAJpFv>*{)5Iqo|bS~#z0O*6J32BbS zxie1bk!eat*9Nb`rVK3jcfs1AmNo2;hY_AD!9rAy*z=R zC9J8U&NN7ip6%{%mLMsu+BDlg8vC3S)r5KZ1BF)Qe$Uc{I1yzCs9MPllWx%Zk_+&h zS8_YZe?0Y+8DE0daT=;tp*^YOE0(e*GKgH?+z@ufD6KMwgk#Y0$K%*DA(GC&Vovg~hcs52w*zxeN8d(mE!ZAgdk}6D0 zLqSwE`tp?EAHgx4pWsYq@k8@u=w04C&UlX<3pujC2NCLpg9Cg+x(sW8t_5p}XzZ2V zqw&BWzz@?k+b948bwV3}`eNdlL)Q;(!PF6XuPofbRRWPF5Lmr?b#+Qji`&XM8*yX# zynQT~DRG^2&Wd3(ztcFhSZ7yVQ~!!(Nce2j+W1YtB7Sdtz*M5+n^8MNxVCY~g`cF_ zSYCx1*j`aN`t%|3TKrrV1hqC>VH72EZI`w*cf((M_=UIW&-CX$qH-$CbK2h;L-*D1 z7>IvlCfQ&gcfbl)pAxk?eK^>_X%R8;@$OA5Aoqn5*k)|3l6_NX&7$?eRsy#)#6G>!8lKJ}$5+SO++dYp6N zsk3hUmI#b+y|?a*l0SBjoz^Y4R3DyAstcKmY5D3C4RruUr$U5>JsXQlh1$0Gsx}G7NA662r%Pif}17!EMK-E=%*AXg*v^MT-- zXDO#Lb7+P&|9IY;yWYk5y+x+avDo9AQ$JWxuVWwFNSt18>lLQ=r0(u<@XCp-)_lKL z-~SZ&EA1!fmC^N(h#QX^A{i1EP^i9yHA;mQ`K=fglzt;-4b-kgL(KR{FZ(OTtuWxthY$x3z zI^(5uT<0UuF`hnIE3jb~{;HNYfHZ`K%7p_(c4bL`_H)Ni5{pn zg`UU233Mf)#SP(Kb7GNQ``+lc>OTqKUK1YnH@v=HQ)zUZisNDVEjoSXoW0Kqnm5lq zaf~0*o|J}ms?VWt;&|+=c$Vg5R`~7;L1d))relguLHB?wW936Q7nW1vIimcuF^oAt zHiE>BC3eiO#GLZu)J#0ltVp)i;fL!L3OsId1%#scxH=xr0Iaf{as{9H>G5<||#73joSbp_Xmb zOXzV;&!0sl$lBbhQTF!cH`j5My-I@k*{f>Ti3`AjdW`2R8**@C<{B{+K^N6zg_Lql zs1|u%nwa}xiEUfZTf9a3siwliS4lwf-Fg+=o<80I;x<#6&y>@{b7V-D5K_80NYOZ3 z#m}h#mhT;0^Tw9DFe-vBA&%HFr#k^{ckeGVS0()T4oqS39Ya!vshK48DQsj(X>g~X zk~2&gid+Kpt1)ruON6*N!gaOupNO}mV6nQ(5ad5hB#PH`M0nAz)g5biby$VDZcTfi z8@e*1so?_9frgw~Ht(*qnbYnmBRU(Dvc#X(@X!u>;nYIDDZLF|3qXrXDqUJM`gwkn z7**(7VBt8L{9?%}^ezi`l4{R1Z>uL&dI83aOCo3EcM&Y(_Clm@$(QM&=E zy8{Ul7l6v0CMZ{R7(s*AIIzh|j^+~npQlyjH2*+|+@O`>_F(bz_Bc<6yaA}>nt5v2 zx#)3vWEm zVS-EHF2+A?wRk!hY1N;I;1%bu^<*Rw)tX?;Gnj=fsPWLjpoeP_(l4#FK)jJ3Rm)%~ z*}Wj8!t&2Q9t2=d?$`yu{sO>6q{MY)-5Y8aa!6U7tS&S_#B&vL|K;4v2Cw`WMcy+9rgm> zNX>!ipPj%x8G-PgYTkQo`ME29eX#b&o4kXKJow1DFQzWUy8l|+1%Rh&mZC#0&I?Gp z;r~AOH@v12nE82tJ3fAg>%lUun&>a%&_3@=>XjXbUp$}!-hr;5rGs)#nEoXC5lJhj z}BZH~l?3wi$N9nD;^j%f;_vwO0pQv{L862*^ z9_PUKQ0ppwM+-lSmGV_4uI<26TPhN&XWM}Z>7p%Ijs|qpDb&YfY5mP(Z$A1(RJ0Q% z%dzU$);I0}Zb>9$F!p-t7O;@~rpO4P!%UZ~r#np}vZAZZ> z>rkRrcDNs6dnxPQg*{ei4>%=pygRm`B)&y*3*8!`NdRFf!HL_%oz4)4_cFE?+&L() z4}Uz>Dox<{0%mHKb@{YpHhQ_D)bv^MGlCf?$7;3vquA)qvP$8)h>xNi^y#yO2-_5N zdr|wRMN2rc%Y3NB)Z_v%7Mv*(h1IP3w&+|T=Lu(q6$&Dp+oCiN2PS3-FGe83fv{oE zy3^#z&TcN?ju2Ba`xQ`=0@Y0f6?j)fZVn!rGb({dMji?2l=y*tFe5uea8m2)DJ$tQ z5o%cXdt0VRG^c)KSsN`lMuAW;N)di4MyfHSTe7NJky#6|t5?D1m9H57yfc&>pIH{D zK!TqCZ02u1D?a5fm_^F{CTf}B!;o)aFJFu2fIZ~ZxQ;VTeQWPBXYaVx#mAmh63)-k z0^wh@97`Q!PZU+W?j+<4e`3D%NHIRP@!eAF+m9dL_)*2K1@jSyjhfC{nk4ZV#>sog z(nFH-L#-F>A4;87s7ca+TMXQy-^01*vU10N&+h7j!dG-1gUq(toEE0*`0TV^P4tKz zGuPqRr_HeQ0*qeL@u*);(nUt`d7AyRd%sV+&a$9(MV^OWQ}l-p>4Zs?9o$ad_ueUR zuG|{HaFrP?7;R4CBwpVu{dk=Q9a|NUDUQ{eRDMLKe%sdRE7Q?v_WcQMS*pMSA5dQK zWg;B}o!j~Bd+80A<11j?EY~I^(_y9a0uYMLDwx9&d^Z;z-Rg7GrEC~pQPAZNe(&Lw zmlvXTwWw6Sk z1-B_R32Y$}^4ddXah3brTPqaS(A^~jvHkan_5BTr)dPRG|xgDgw?tj{! zKs}wF0 zV#*{8K1|UfY0V(jGdeWO1(aQm_B>7Q;+tPid!XlE@@FYeVI?LyUat95T8{KTQ)t@n zoQ$JmJ08PnCPulPrML77v)q&E!LzZ0L$FI)9%5(%J(FOJO1=7)Zeft; z-aF$mgb6htwb-S?vKsriG+UN#m1H>oK>#6&m}3u#;Te%*98!xNMEz-oYqJ~Bfq7TP znzII*CD$$hRp?E{c_;_mLdC04%T9EK?VbzCpep(PQffJZMPXGgz=Jr817XW Date: Mon, 28 Nov 2022 20:28:42 +0100 Subject: [PATCH 09/47] Delete manhwa18cc --- src/web/img/manhwa18cc | Bin 1098 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/web/img/manhwa18cc diff --git a/src/web/img/manhwa18cc b/src/web/img/manhwa18cc deleted file mode 100644 index 72bb76c50880a876e4d2da448aa3586170ea826a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1098 zcmV-Q1hxB#P)6WV~5P&ZND&mTYtcCCYlo!Qx?8Nj;r{|oRbB7;U zpz5R{tF{}dmQ@s?UCI8e4M^LMs^2qAKnZ&tP1;UWY}=;c zsIt$1x*Bp1Ewjb2>C;x>Hu${rHNs{$vEVapqJG}0?fU@$SSX}MqCK8BLz)0&44(FV zIg@_VkDpLXF@iZjK0E;wT6{^fIanm)8gXl!365~AfpZ8hZ^A00n?_<+y~#WPrp$&h z6A(`$>d|5K1yaFgC@yw6gbCZ#Qsh19R$xdy0eJL1YPoHp>c3X0QE-Ep)IAuJ^~;no2N3Y(zM3mZcEz3g9^wQ+~9)+Q$6Z z;!WqwfFQSaSWV^h3NYZRxi`*StqUovrQG@U)KF|k)X1Pp1l*~*<)9OJ^R0>{!bZadOZ36?U+=*zx9lm5;1B5H-uVi`J&$79wcq2UT( z68s}wAU;^EuAoMd`_YZNP%8Y7jb&ddPRZOyTUUiEr|-d&r05l3*_YaL`ZHYkb;jpb zXZQ0YuPw;$!fw$t0;41EI|%Bv1?KGFJuKTWNB6BkkmUm)QZ!uwe38!h=!k}udUwOL zt|O6$VLC2=9l4FG=5&qs9;iD{G#BaMm>N~Db{jaywz z&bp?zJh_g>vK7`acK!_{ziHx^%hXe(fv!;oD>DUfDl$@%-`C_g@~r0r*TTrbcTN Q&;S4c07*qoM6N<$g1%!J6#xJL From 3694c1baef274b0fa2f4d04e55f93ff1e057912d Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 28 Nov 2022 20:29:26 +0100 Subject: [PATCH 10/47] Upload manhwa18cc icon again --- src/web/img/connectors/manhwa18cc | Bin 0 -> 1098 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/manhwa18cc diff --git a/src/web/img/connectors/manhwa18cc b/src/web/img/connectors/manhwa18cc new file mode 100644 index 0000000000000000000000000000000000000000..72bb76c50880a876e4d2da448aa3586170ea826a GIT binary patch literal 1098 zcmV-Q1hxB#P)6WV~5P&ZND&mTYtcCCYlo!Qx?8Nj;r{|oRbB7;U zpz5R{tF{}dmQ@s?UCI8e4M^LMs^2qAKnZ&tP1;UWY}=;c zsIt$1x*Bp1Ewjb2>C;x>Hu${rHNs{$vEVapqJG}0?fU@$SSX}MqCK8BLz)0&44(FV zIg@_VkDpLXF@iZjK0E;wT6{^fIanm)8gXl!365~AfpZ8hZ^A00n?_<+y~#WPrp$&h z6A(`$>d|5K1yaFgC@yw6gbCZ#Qsh19R$xdy0eJL1YPoHp>c3X0QE-Ep)IAuJ^~;no2N3Y(zM3mZcEz3g9^wQ+~9)+Q$6Z z;!WqwfFQSaSWV^h3NYZRxi`*StqUovrQG@U)KF|k)X1Pp1l*~*<)9OJ^R0>{!bZadOZ36?U+=*zx9lm5;1B5H-uVi`J&$79wcq2UT( z68s}wAU;^EuAoMd`_YZNP%8Y7jb&ddPRZOyTUUiEr|-d&r05l3*_YaL`ZHYkb;jpb zXZQ0YuPw;$!fw$t0;41EI|%Bv1?KGFJuKTWNB6BkkmUm)QZ!uwe38!h=!k}udUwOL zt|O6$VLC2=9l4FG=5&qs9;iD{G#BaMm>N~Db{jaywz z&bp?zJh_g>vK7`acK!_{ziHx^%hXe(fv!;oD>DUfDl$@%-`C_g@~r0r*TTrbcTN Q&;S4c07*qoM6N<$g1%!J6#xJL literal 0 HcmV?d00001 From 2518f0887ddc39c50dc3d416fb03fb4c098a8a00 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 28 Nov 2022 21:38:36 +0100 Subject: [PATCH 11/47] Add MangaPlanet Fixes https://github.com/manga-download/hakuneko/issues/5185 --- src/web/mjs/connectors/MangaPlanet.mjs | 79 ++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/web/mjs/connectors/MangaPlanet.mjs diff --git a/src/web/mjs/connectors/MangaPlanet.mjs b/src/web/mjs/connectors/MangaPlanet.mjs new file mode 100644 index 00000000000..4747838bf0c --- /dev/null +++ b/src/web/mjs/connectors/MangaPlanet.mjs @@ -0,0 +1,79 @@ +import SpeedBinb from './templates/SpeedBinb.mjs'; +import Manga from '../engine/Manga.mjs'; + +export default class Futekiya extends SpeedBinb { + + constructor() { + super(); + super.id = 'mangaplanet'; + super.label = 'Manga Planet'; + this.tags = ['manga', 'english']; + this.url = 'https://read.mangaplanet.com'; + } + + async _getMangaFromURI(uri) { + let request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, '.card-body.book-detail h3'); + let id = uri.pathname; + let title = data[0].innerText.trim(); + return new Manga(this, id, title); + } + + async _getMangas() { + let maxPageCount = 1; + let mangas = []; + let request = new Request(new URL('/browse?page=1', this.url), this.requestOptions); + const newpagecount = await this.fetchDOM(request, ".pagination :nth-last-child(2)"); + maxPageCount = parseInt(newpagecount[0].innerText.trim()); + for (let i = 1; i <= maxPageCount; i++) { + request = new Request(new URL('/browse?page=' + i, this.url), this.requestOptions); + const data = await this.fetchDOM(request, ".contents.comics .linkbox"); + for (let element of data) { + mangas.push({ + id: this.getRootRelativeOrAbsoluteLink(element.querySelector('a').pathname, this.url), + title: element.querySelector('h3').innerText.trim() + }); + } + } + return mangas; + } + async _getChapters(manga) { + const uri = new URL(manga.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, '#accordion div[id*="vol_"]'); + let chapters = []; + for (let volume of data) { + const title = volume.querySelector('h3').textContent.trim() + " - "; + + for (let chapter of [...volume.querySelectorAll(".list-group")].filter(e => e.querySelector('a') != null)) { + const origurl = /'([a-z0-9:/.?=]*)'/g.exec(chapter.querySelector('a').getAttribute("@click"))[1]; + //the chapter site gives different urls. sometimes you first get redirected to a login or 18+ restricted page other times not. + let chapid = origurl.split("/").slice(-1)[0]; + if (chapid.includes("?")) { + chapid = chapid.split("=").slice(-1)[0];//gets the id of the cid parameter + } + let url = ""; + if (origurl.includes("/reader")) { + url = "/reader?cid=" + chapid + "&sk=1"; + } else if (origurl.includes("/viewer")) { + url = "https://image.mangaplanet.com/viewer/" + chapid; + } + chapters.push({ + id: url, + title: title + chapter.querySelector('span').innerText.trim() + }); + } + } + + return chapters; + } + _getPageList(manga, chapter, callback) { + this.requestOptions.headers.set('x-referer', this.url + '/'); + //add 18 plus cookie otherwise you can get redirected to a different page + this.requestOptions.headers.set('x-cookie', 'faconf=' + 18); + let data = super._getPageList(manga, chapter, callback); + const url = new URL(chapter.id, this.baseURL); + this.requestOptions.headers.set('x-referer', url); + return data; + } +} \ No newline at end of file From 35a78d6da14c51dc1ccec39e26ebd8e670bbb555 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 28 Nov 2022 21:39:20 +0100 Subject: [PATCH 12/47] Add MangaPlanet Icon --- src/web/img/connectors/mangaplanet | Bin 0 -> 7303 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/mangaplanet diff --git a/src/web/img/connectors/mangaplanet b/src/web/img/connectors/mangaplanet new file mode 100644 index 0000000000000000000000000000000000000000..88b1d9d74001251d72b430eb2427c513f2d4dcce GIT binary patch literal 7303 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 NNlsn1O4=;^{{S0FF}wf( literal 0 HcmV?d00001 From 663e6777d548aeb566f82394b8f902bcd97e4bc5 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 29 Nov 2022 17:26:58 +0100 Subject: [PATCH 13/47] Add Anizero Needs BloggerVideo videoStream mjs --- src/web/mjs/connectors/Anizero.mjs | 151 +++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/web/mjs/connectors/Anizero.mjs diff --git a/src/web/mjs/connectors/Anizero.mjs b/src/web/mjs/connectors/Anizero.mjs new file mode 100644 index 00000000000..bca9d453126 --- /dev/null +++ b/src/web/mjs/connectors/Anizero.mjs @@ -0,0 +1,151 @@ +import Connector from '../engine/Connector.mjs'; +import BloggerVideo from '../videostreams/BloggerVideo.mjs'; +export default class AniZero extends Connector { + constructor() { + super(); + super.id = 'anizero'; + super.label = 'AniZero'; + this.tags = [ 'anime', 'spanish' ]; + this.url = 'https://anizero.site'; + this.search_nonce = undefined; + } + async getnoonce(){ + if (this.search_nonce){ + return; + } + let url = new URL(this.url); + let request = new Request(url, this.requestOptions); + let scriptPages = ` + new Promise(resolve => { + resolve(js_global.search_nonce); + }); + `; + this.search_nonce = await Engine.Request.fetchUI(request, scriptPages); + } + async _getMangas() { + this.getnoonce(); + let mangaList = []; + for (let page = 1, run = true; run; page++) { + let mangas = await this._getMangasFromPage(page); + mangas.length > 0 ? mangaList.push(...mangas) : run = false; + } + return mangaList; + } + async _getMangasFromPage(page) { + let form = new URLSearchParams(); + form.append('search_nonce', this.search_nonce); + form.append('action', 'show_animes_ajax'); + form.append('letra', ''); + form.append('paged', page); + let body = form.toString(); + let url = new URL('/wp-admin/admin-ajax.php', this.url); + let request = new Request(url, { + method: 'POST', + body: body, + headers: { + 'x-origin': this.url, + 'x-referer': this.url, + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'X-Requested-With': 'XMLHttpRequest', + } + }); + let data = await this.fetchJSON(reques); + if (!data.animes){ + return []; + } + return data.animes.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element.anime_permalink, request.url), + title: element.anime_title.trim() + }; + }); + } + async _getChapters(manga) { + let chapterslist = []; + //get mangaid from page + let request = new Request( new URL(manga.id, this.url), this.requestOptions ); + let data = await this.fetchDOM( request, 'link[rel="shortlink"]' ); + const mangaid = data[0].getAttribute('href').match(/\?p=(\d+)/)[1]; + let form = new URLSearchParams(); + form.append('action', 'show_videos'); + form.append('anime_id', mangaid); + let body = form.toString(); + let url = new URL('/api', this.url); + request = new Request(url, { + method: 'POST', + body: body, + headers: { + 'x-origin': this.url, + 'x-referer': this.url, + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'X-Requested-With': 'XMLHttpRequest', + } + }); + //EPISODES + let chapters = []; + data = await this.fetchJSON(request); + for (let i in data.episodios) { + let element = data.episodios[i]; + chapters.push({ + id: this.getRootRelativeOrAbsoluteLink(element.epi_url, this.url), + title : element.epi_num + ': '+element.epi_title.trim() + }); + } + chapterslist.push(...chapters); + //OVAS + chapters = []; + for (let i in data.ovas) { + let element = data.ovas[i]; + chapters.push({ + id: this.getRootRelativeOrAbsoluteLink(element.epi_url, this.url), + title : '[OVA] '+ element.epi_num + ': '+element.epi_title.trim() + }); + } + chapterslist.push(...chapters); + //FILMES + chapters = []; + for (let i in data.filmes) { + let element = data.filmes[i]; + chapters.push({ + id: this.getRootRelativeOrAbsoluteLink(element.epi_url, this.url), + title : '[FILM] '+ element.epi_num + ': '+element.epi_title.trim() + }); + } + chapterslist.push(...chapters); + return(chapterslist.reverse()); + } + async _getPages(chapter) { + let request = new Request(new URL(chapter.id, this.url), this.requestOptions); + let scriptPages = ` + new Promise(resolve => { + resolve(players_links); + }); + `; + let data = await Engine.Request.fetchUI(request, scriptPages); + let videolink = Object.values(data)[0]; + //remove redirector + if(videolink.match(/assistirFHD/)){ + let tmp = new URL(videolink); + videolink = decodeURI(tmp.searchParams.get("video")); + } + //if link is not already blogger + if (!videolink.match(/blogger/)) + { + //fetch the dom returned by the link -its html= + request = new Request(videolink, this.requestOptions); + data = await this.fetchDOM( request, 'body'); + let ele = data[0].querySelectorAll('script'); + ele.forEach(node =>{ + if (node.text.match(/blogger/)){ + videolink = node.text.split("'").filter(el => el.match(/blogger/))[0]; + } + }); + } + if (!videolink.match(/blogger/)) + { + throw Error('Unsupported Video host : '+ videolink); + } + let vid = await new BloggerVideo(videolink).getStream(); + return {video: vid, subtitles: [] }; + } +} \ No newline at end of file From 872ee4fc2b9d48e3fe74c0f6d3b3fe30c3b695b5 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 29 Nov 2022 17:27:41 +0100 Subject: [PATCH 14/47] Add anizero icon --- src/web/img/connectors/anizero | Bin 0 -> 8330 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/anizero diff --git a/src/web/img/connectors/anizero b/src/web/img/connectors/anizero new file mode 100644 index 0000000000000000000000000000000000000000..b7cf43626d16d3368bda2b77347ab9471fa86e8b GIT binary patch literal 8330 zcma)iS6ow1)NM#1gx-4zO_YuxhME8Z(m_BJks{Kh1wp!mP(2$32=ngnwBf8Tw)5BFitemQ6F-|RDc&0cHfnWecQ6FomY003Yzx_<4(xyAoC z=xEQMH)egH=LUwpemf8VVE+8ypvjV9761U?0HbRXnU(y2BtM!bG`7_c!_nYK1=9VqV4}UEV}^qF$$V0MlDu=QG(P2SSz*%!vw!+b zWme&|ytXrMQ>OolNtfc{Ngh`{EpI1(*m2=gO&jH2@!Ry9xp}N|5vD&rrGM3piNC_Y z=GE>b(foP=?QzNFywLGYC8rW6oS~%o9lAt{j@v-4Y_K?1I?e!ih4+#Qdo_P`vIOsy z2w7HA+BkD(MDPy@%YBJ!-t%3&tb&ZLcys606MjSBA0~KoDN_T?U8oK0U@!&ripBPD zQFnYd1viOKY{|oLdPjoDfeMh+622&@ z>Gsr<`97K|8A61`0@&x&EPva3;P4akJDRjuP5M;{G+SF7nvEcNO?4J%^^Ct|y@gfY ztNZdzewYmoSbw);fH(Ie-sNzgok-WM!+0{HJN$(hR^Aa$9yVb3OgnwA0Hg+44T!__ zkP&G9=O%dWq=B;_+t+Q z^IZEUW+IbsQ>80VtrgCGU87-{w-(?Bh@#I@JF)z!yy;mkTzQ2kN_32a>U=LdjbA+7 zk>eGDRq$aL#i3oZiDYTU$>lEs7B|2jRXz&m%_E}z?vZVh7BY=k08X^qAhp=x1FpEQ z{aIq0i#e!5^DQpC3~C#oZ07UKHH09MX6;7jEMFqmy@st5ZRn*7O@Cqslp9p5ZgoeJ z`H59n?_ruZw2QYusURxDk#xQ4;C%Vw3zYrF4#*EOPw$!Hp9wwEj;Oa?b$Qs$?ZJDr z2qeSFmxwgiY=Gx<8CY)~h1VuB1oc zWkG!)@o$zCCr)+LKGa5eM)Oat9m{J{y%yC9O@pmFiUG$N%=DCGX+_{-{rbGoa?CqeS zbpC)dE%Fea&%LVkmKHaq*xPN=4(Qb@0we}Q7yW6m`qa^X9o-;y@bBhHF#jGBxTX*5!7J_I7mV z@^|#seudE2*3cP-sr~CQ;iy`K^6G^usmD_omB%dFh0Bbu7bWBRaQ0z2S-~%p3lfRHq;2=x**G|Ycx&e5mIEr>ks^l{1GF+rSkC$ zN*bq(Z~LbIuGWlhJurqB_6M`JF@!RZW1p$P514`c>f2GxPLH5fLfh5sF^Q8jZDQk@}a-zizFpM|*c(2RBPtO6_4- z+dzfiXG`WonWvSwiQvYug|``kZ&JYz!Ws;<0iEj#L9fL`45XTW+3iK(-dt3n1RNJ99Kebo!fwG2%IP0gN5U$sUmk*} zT%qDAn|$t?JXv}hp2g0MK58qh+dCourp!$>1ke9=dk?Y0(tqglL390?YhKh_IWybi z9(E~toSg6e(P1g|a4Way+e@fljubX1=gD3C43?YF%3=|Vrm&K4#BmIoK2G}KYMBAv zHF*4y2YaaL7)&x}Y6bb1f1#8=CGbYU+Op%r6l8N*7+MFOqZk62`&I1soL+umVC$<@ z$5^UPh57`ne`mQ0l>00gq-TgauS*S*7TXLQFbYDhUKMMD5UD~t89&He4#x8MAx!ag^UHHx!v744Z{{#xdO6W2j2vGHodZpr>VGr6ko9Qs~ry5u*pxlLk`xwRsg@sOztW#iAB zTH8KwJU{(ZUy@lPplGm^!UV}ve9E%hf?cx`mycx)LBXJz8Uu@s>RN(sYsF-s(1&Z5B+ zqZhl_9hJ^7Xs-2na6G$Cui!0DH3D{j5WK$Ue?az1r(Je-Oz?}l$(#5bRiienzvZth z+tI2Y2K(O{t&^d;5cly#WwibDyTC7q{vjH>L!o;~^l6zbiz4n(TdaiSI%k_%h9<=| z=Rp_6*0IgGpKuLkATsIFs!waAq^0*f%>`D+H2E06VYp0hbUs#KmSXDytN1qggV*s( z@K$lpF^81z+_AuCybh6>QDY8k%VL{}a+eC<{rB7)omC}fhsEhIBJgI;wF)r^dZUjh zyRyBnp-U9ql|SqsB}k$KU@bq1b7-M|r_H z_}_2*#70H)-V4-8wB3iRd?Eyms>;QoV(LZ3wTF<##b)}aIjfW zBya_$xLiH3EySC8FEb|KmB|{Pssn{ty?He}S&hNxxHfAAGK#GO;mwlgTb{7ch(NYI zypEf=f!ay(|EMbUH{;6o8iL_KsdV_`9?`b-NS% z>rX3o0rCky`t^|JmX`Z1t%JwzRmH$hG@1|x8jAO4Gq+_%)zg-frk-aJR6-99^!VWL zvhpdlVf%8SFWmIelsWMi@9(=?Zjm^#Hwu2bOH(%mZ4;e7#rAV|2`O>s%*fUhK#v*w zHLw=0ZLD9%hKgNump->sMOP-xxExnkomeWb!&z&=wXoS_7F%8)={OuyZ%fsUKJFIQ zn{4w86tVim0|EZ~1YsibdF;yfRmkPuE;JJw9>fYZ3C%#|UMaJF0_#2f`(lqN=ZjKN>1@OUb$XXiDkx7g51yVyaW8X z*^?!g?jO?$oddt=xI*W>ojHrCvmvD*WhqYx_(wrS0hic4k?o1d$D&`$%#D9t6%L`u z9cO3H(D7gu$^<}EZuF;xNzTN7yB0tNrkwdd>5f1z=#iqjbzN+QaMn2{^(zq1I=-Am z_xw^7%co;0?Zcn3lNFctybe2Bqi%@uN_{o4b~;m1p@tuTt5tS?Z$?fJ{-h=8As3iXPIeChu82J^9#G$;4wFt=zMDw#4P955WHTleerDkGi#@E8mGc+dWu0d4gc78Cw*+oTqWxs)b zv(7YDQRCm$pC&T!0>cy99l&p7qNaMN_T2UnP%>SW8i9R}e!pf|fVe%H-S5-{rk{3P zK&>Eh56i|bZF@A`0YRS|wKJfeJHnM|SEHj`a;Nsh%?o6`Ojbl*fa&iAd`LmV78!yx z^*v51_#Js06V6=iZ&o}F5S?Cio)zAq{60LKzUOL*dU zP)NCWzmUaZA!7ok_*!?}u-|LMWK?cuv2;NNXYokHbB81Cn zkp&;+pJ2$Cm1O*P2*1bQ1M2&7WA=+rD}Sk?VMmXft{j;?nY3O3Ag?7(AX0d)^Nf7n z>gjvm7z+EkH2Iy!#s~=U-GckW3$CrmH(hBH$*;P2Q7%JM zRLQ*UbzUJfn}?BQohmL5-Ov=*AoZ?i5|%eSnYbs$(w6W3=IbYMRAo?>j7l^g{JAG- znI?B9)Ui}z=o~vZF4)obDnxm`ybgrySG&RvXMAHAlp9J~KuWZCIcCSwjJ>OVBPCjS zR(pqkM7F=S@h!%%;H2a@v@(_2D^oZ^6XtJ}Ls0cpMBO$1N!3s2d?6`c9OA4VK@nc+ zeg7v!+W+VW(XUuhT#bLSPk%9PE716Jl=dZwbRX=8 z@%9DxUK{-=j%?mfd5NoSO!H65B)znB1a;xur$xhqLM)?313?enMkJ&a$XTmqF9AhfH23B_Z zOLrxQyJvaP0M~iI;Ih}LOVpW=PiaDG=!`h`HpHF~7aFtlcWU{6F$tq_m?LwZrs$t3 ziv~eOLjzf+BLpArxt{~%E$>bUO7ZODxvQpa)i!M1&Kxk1(lC$}tLz4BEFxDNE&@IVqcvQF<{Y+hEOz_HGx$vS+AVt{TUg~j@B<{;x>{bS zr;F#|9x%+_$@{c5_5;>R8zaYOUg~Gbn+v|-06gPA;sa;tv^YizYRJ|gH4~9OZ)%F6 zCIzyTx$6p8n_WR*6!VIlb1fPwko1wdNW_6DA0s9nzWHQltibAAk&>H9>MSuw;#cMV zs3*T%x;ABH<|lY5D;qfwM2c5~o@-lMq?KlYyIaV%lR7P20nofHV>WFnD_yV3h>U|0 zBDT^LV*fc=usu^LjLCyz$-5q@lWCJX)-QTx@_3ZQr6U4rrGByKvGg4vxh>AwUrNW@rg_Fr$P~}Fos&G> zZQQ}ROi@=JA*k~;>K3;GD~W$FbNgF`hHA6tzBMoI&w~n2h4k>yV4Bv_q)~r~XS1)j z_AV7rm56O8kY}sZqpgzh_eRCR)is|c8%e4W_U!TIzexTQ-9Es@a}Kt2L|Cd|>fPb# z`@W<7wkWlJh@iX%NWdPJoCM4#)+=AYj2t>SMHOPvf^8(febP|JvrQlN>&w0X3~eXP zRL)eUUNX5J5zxu%vgm?|mh(kW)0_tpT|GMMi$5W&S=9+^w4KUa8GjNI)&i;fE0aA6 z81uut7wqRMz&CL`Urf6y!+SxaY^!HvPcP=#SrK8{t*u?>7$JQ;T zm{9hMEE*}B#`!Jz9#Ui{lpi3FerwZwxjD{i6h3pf8c2P#GI_h-N&kMPtT?&$Bj{WI z1@d;58g;E??pb5F>FiA+5=c!Q>1K9DqB*sY$6>$bzgxO89BGmz&trLPR`9;fVO7Zv|g z%><5$Sa;He%Ji!)S^f1ijm;zK5KVHoIGm-Wv8YLgBu%Vw=|Cw+AZn_+oK`>Mv)9qa zx4QWrDpP^Gmm1H{<<`9{RFUuC1#$=e*|5@mY8s;e=kFX<^MWV1dCL0nhlJ#@BEqJa z1gmUKw4dD{cNwN<}b zD}9d!mKK29G{UIl5VHO@yQd+M+lR@DxAQeeaKwJDOYpy9tOu4aFU3JXf^aE44ZfSJ zv9_RBcm1m~k2tnvu57?Ju8s!N-7iu-0;f@pR)`6kn)}1|@H{mg2-x`hAz-m?j=6MM zCymSEqGQaw`(&?c(iqt;i%LTbeEQBhdea5Y-rs1K*8K9NdcyZSic)8m)m@U5tUxmB zcOX^h2)P}J>A!uhklm9^ht7T2I2@s2N=jFC_mLU!lj&Rb>LKSzYOJH%@b{Kc z)OgIdB8b-NuGn8oxKe&LblRzBRg@S-`H!6IpYIhoz!_A`;(Z%14|;lhIiw>Lw!$Tm zrAvJ(f4`^;Cu;ND^Jt@+Do1<_UqBm1GGuB+MJTfOn^fH(u~@1#{R}kGFIhZaT5iNl zIu`CLL@sxhZiewio16(X;^fE~>S}i<)9KQ+Fi{_`lPhCg2qw&Pih9_M6~CYf57GG| zMGX@WNkKMXli5Dd75|M_o7TbCjVH0DkpE1Y+=@Mk<~EV3d?(8H4!nC#DOI1GU@b~c zVEbkKW`P7JS*kcGH7rdDbv|9_GGrb7wM9$gR938Nwq8U()Z9~fdMK6O=J%wjl-Ue< zjZnw6a2GGq*wkRubsjhZ?O5X6bE*6#B!pJYgjupVJkn|Ur&T! zafU-ZT5*%NrrSwf=kV1~;d`{VJUK}9N>8Ji|B?#IIbRE!uyMJB=|S^4Q9-CSJk61UN3tL1h2Pw_vj9FC5G8E> z4H?lpQXLM65lIg47Xni^J4eVTBeZ965K4AzTo#QR*3^I2j77zZzYZCgjmnQ7$zRNxO6o!VFb^Z zf{;^bK&e~ye}Vz8lgD9=jcvS{Q7+n^-_l$LRi+i%gr*8*FtHT~(7R~a#-_;j&3vQ8 zb(4~knyMS;qOO6It&k3bPImV!TupWO(@zCZvElucAFh=qc@@ErB`k*>bD>HcynfJm zm;;L`AlM>4p~5lMk;RbUk4TgEK9%Se|1>sTs=Iq3^)3>|dCl98@ln<%VXc}o)|O8? zv6}VICjQ}=W@G!qxO0+DF$@N$=RPMMK^An5Wa13x7XJE2udPzv8Svd9ITp%z8Xjsp z&&@$PgZMIgYU{uaa~|H`yU>6(zBZ0FW(#iYUE!m;u=+5o?v*&X@8~~HfKR|(4QFB9 z`*St>GUBccQ~Nn6)uz=xMM2=6GJbcG1aLJ-(=W->Hp4Y(WVICrNjA`?G!b!{jWN5tR+{B*$&XgZ~^F8$PEQYEr}X!{=SNIk zgK6waRQt4d zKVFEHpA32zYu%OlAP;dsR?Yi4Im~s-V|KDN+Fz{TbN#lTQCeL~T{5LQX6|~4#I0Nb zJ(h#1c;j+Cd=|;kzzz_>c5rWMxtGt7!7y)s(ASy4g@fQ0l(Gdme9WZvlyb+@7c#Lo%4Mzh+VKDY literal 0 HcmV?d00001 From a6cad44fb6043d8d160db91d81030b70f2c864c4 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 29 Nov 2022 17:28:42 +0100 Subject: [PATCH 15/47] Add videostream "BloggerVideo" --- src/web/mjs/videostreams/BloggerVideo.mjs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/web/mjs/videostreams/BloggerVideo.mjs diff --git a/src/web/mjs/videostreams/BloggerVideo.mjs b/src/web/mjs/videostreams/BloggerVideo.mjs new file mode 100644 index 00000000000..67209b771fb --- /dev/null +++ b/src/web/mjs/videostreams/BloggerVideo.mjs @@ -0,0 +1,17 @@ +export default class BloggerVideo { + + constructor(url) { + this._uri = new URL(url); + } + + async getStream() { + const script = ` + new Promise(resolve => { + resolve(VIDEO_CONFIG); + }); + `; + const request = new Request(this._uri); + let data = await Engine.Request.fetchUI(request, script); + return data.streams[0].play_url; + } +} \ No newline at end of file From c422eb71d268ef891f9859b184fd575bed5e2a3b Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 29 Nov 2022 18:35:48 +0100 Subject: [PATCH 16/47] Add Yurineko icon --- src/web/img/connectors/yurineko | Bin 0 -> 10023 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/yurineko diff --git a/src/web/img/connectors/yurineko b/src/web/img/connectors/yurineko new file mode 100644 index 0000000000000000000000000000000000000000..2d292969253a1cd94e3391e06c70297b449a368b GIT binary patch literal 10023 zcmV+?C)n7DP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 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 x;P{;;T~F* Date: Tue, 29 Nov 2022 18:37:01 +0100 Subject: [PATCH 17/47] Add Yurineko https://github.com/manga-download/hakuneko/issues/5189 --- src/web/mjs/connectors/Yurineko.mjs | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/web/mjs/connectors/Yurineko.mjs diff --git a/src/web/mjs/connectors/Yurineko.mjs b/src/web/mjs/connectors/Yurineko.mjs new file mode 100644 index 00000000000..bb669fad30d --- /dev/null +++ b/src/web/mjs/connectors/Yurineko.mjs @@ -0,0 +1,55 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class Yurineko extends Connector { + constructor() { + super(); + super.id = 'yurineko'; + super.label = 'Yurineko'; + this.tags = ['manga', 'hentai', 'vietnamese']; + this.url = 'https://yurineko.net'; + this.api = 'https://api.yurineko.net'; + } + async _getMangas() { + let mangaList = []; + let uri = new URL('/directory/general', this.api); + const request = new Request(uri, this.requestOptions); + let data = await this.fetchJSON(request); + return data.map (element => { + return { + id: '/manga/'+element.id, + title : element.originalName.trim() + } + }); + } + async _getChapters(manga) { + let uri = new URL(manga.id, this.api); + const request = new Request(uri, this.requestOptions); + let data = await this.fetchJSON(request); + return data.chapters.map (element => { + return { + id: '/read/'+element.mangaID+'/'+element.id, + title : element.name.trim() + } + }); + } + async _getPages(chapter) { + let uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, '#__NEXT_DATA__'); + let j = JSON.parse(data[0].text); + return j.props.pageProps.chapterData.url.map( el => { + return this.createConnectorURI({ + url : el + }) + }); + } + async _handleConnectorURI(payload) { + let request = new Request(payload.url, this.requestOptions); + request.headers.set('x-referer', this.url); + let response = await fetch(request); + let data = await response.blob(); + data = await this._blobToBuffer(data); + this._applyRealMime(data); + return data; + } +} \ No newline at end of file From 467ddbe2e59e74adf4f4c3e89cf88d9f04a4c3ef Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 29 Nov 2022 19:08:50 +0000 Subject: [PATCH 18/47] Pair Of Two fix * Change URL * Template is now madara Fixes https://github.com/manga-download/hakuneko/issues/5191 --- src/web/mjs/connectors/PairOfTwo.mjs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/web/mjs/connectors/PairOfTwo.mjs b/src/web/mjs/connectors/PairOfTwo.mjs index 751460e08eb..be15c5998ed 100644 --- a/src/web/mjs/connectors/PairOfTwo.mjs +++ b/src/web/mjs/connectors/PairOfTwo.mjs @@ -1,12 +1,11 @@ -import WordPressMangastream from './templates/WordPressMangastream.mjs'; -export default class PairOfTwo extends WordPressMangastream { +import WordPressMadara from './templates/WordPressMadara.mjs'; +export default class PairOfTwo extends WordPressMadara { constructor() { super(); super.id = 'pairoftwo'; super.label = 'Pair of 2'; this.tags = [ 'manga', 'english' ]; - this.url = 'https://pairof2.com'; - this.path = '/manga/list-mode/'; + this.url = 'https://po2scans.com'; } -} \ No newline at end of file +} From d57372d39e520c7633d2d84af56b9820de58dfd1 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Wed, 30 Nov 2022 14:02:13 +0100 Subject: [PATCH 19/47] Add MangaTales Fixes https://github.com/manga-download/hakuneko/issues/1428 --- src/web/mjs/connectors/Mangatales.mjs | 223 ++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 src/web/mjs/connectors/Mangatales.mjs diff --git a/src/web/mjs/connectors/Mangatales.mjs b/src/web/mjs/connectors/Mangatales.mjs new file mode 100644 index 00000000000..817999383b5 --- /dev/null +++ b/src/web/mjs/connectors/Mangatales.mjs @@ -0,0 +1,223 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class MangaTales extends Connector { + constructor() { + super(); + super.id = 'mnagatales'; + super.label = 'MangaTales'; + this.tags = ['manga', 'webtoon', 'arabic']; + this.url = 'https://mangatales.com'; + this.mangaSearch = { + title: '', + manga_types: { + include: ['1', '2', '3', '4', '5', '6', '7', '8'], + exclude: [] + }, + oneshot: { + value: null }, + story_status: { + include: [], exclude: [] }, + translation_status: { + include: [], exclude: ['3'] }, + categories: { + include: [], exclude: [] }, + chapters: { + min: '', max: '' }, + dates: { + start: null, end: null }, + page: 0 + }; + } + async _getMangaFromURI(uri) { + let request = new Request(uri, this.requestOptions); + let data = await this.fetchDOM(request, 'script[data-component-name="HomeApp"]'); + data = JSON.parse(data[0].textContent); + let id = data.mangaDataAction.mangaData.id; + let title = data.mangaDataAction.mangaData.title; + // data.mangaDataAction.mangaData.arabic_title + return new Manga(this, id, title); + } + async _getMangas() { + let mangaList = []; + for (let page = 1, run = true; run; page++) { + let mangas = await this._getMangasFromPage(page); + mangas.length > 0 ? mangaList.push(...mangas) : run = false; + } + return mangaList; + } + async _getMangasFromPage(page) { + this._setMangaRequestOptions(page); + let request = new Request(new URL('/api/mangas/search', this.url), this.requestOptions); + this._clearRequestOptions(); + let data = await this.fetchJSON(request); + data = data['iv'] ? this._haqiqa(data.data) : data; + data = data.mangas || []; + return data.map(manga => { + return { + id: manga.id, + title: manga.title + }; + }); + } + async _getChapters(manga) { + function getMangaSlug(/*mangaTitle*/) { + // NOTE: As of today, the manga slug (e.g. 'how-to-fight') can be any arbitrary string + return 'manga-slug'; + // mangaTitle.replace(/\s+/g, '-').replace(/[^-\w]+/gi, '').toLowerCase(); + } + let request = new Request(new URL(`/api/mangas/${ + manga.id} + `, this.url), this.requestOptions); + let data = await this.fetchJSON(request); + data = data['iv'] ? this._haqiqa(data.data) : data; + data = data['isCompact'] ? this._unpack(data) : data; + return data.mangaReleases.map(chapter => { + const team = chapter.teams.find(t => t.id === chapter.team_id); + //const chapterization = data.chapterizations.find(c => c.id === chapter.chapterization_id); + let title = 'Vol.' + chapter.volume + ' Ch.' + chapter.chapter; + title += chapter.title ? ' - ' + chapter.title : ''; + title += team.name ? ' [' + team.name + ']' : ''; + return { + id: [manga.id, getMangaSlug(manga.title), chapter.chapter, team.name].join('/'), + title: title, + language: '' + }; + }); + } + async _getPages(chapter) { + let request = new Request(new URL('/mangas/' + chapter.id, this.url), this.requestOptions); + let data = await this.fetchDOM(request, 'script[data-component-name="HomeApp"]'); + data = JSON.parse(data[0].textContent); + let url = (data.globals.wla.configs.http_media_server || data.globals.wla.configs.media_server) + '/uploads/releases/'; + data = data.readerDataAction.readerData.release; + let images = []; + if (data.hq_pages && data.hq_pages.length > 0) { + images = data.hq_pages.split('\r\n'); + } + else if (data.mq_pages && data.mq_pages.length > 0) { + images = data.mq_pages.split('\r\n'); + } + else if (data.lq_pages && data.lq_pages.length > 0) { + images = data.lq_pages.split('\r\n'); + } + return images.map(image => { + let uri = new URL(url, request.url); + uri.pathname += image; + return this.createConnectorURI(uri.href); + }); + } + async _handleConnectorURI(payload) { + /* + * TODO: only perform requests when from download manager + * or when from browser for preview and selected chapter matches + */ + let uri = new URL(payload, this.url); + // See: https://gmanga.me/assets/javascripts/main_v38.js + let lease = (parseInt(Date.now() / 1000) + 120).toString(36); + uri.searchParams.set('ak3', lease); + let data = await super._handleConnectorURI(uri.href); + return data; + } + _setMangaRequestOptions(page) { + this.mangaSearch.page = page; + this.requestOptions.method = 'POST'; + this.requestOptions.headers.set('content-type', 'application/json'); + this.requestOptions.body = JSON.stringify(this.mangaSearch); + } + _clearRequestOptions() { + delete this.requestOptions.body; + this.requestOptions.headers.delete('content-type'); + this.requestOptions.method = 'GET'; + this.mangaSearch.page = 0; + } + /** + ******************* + * ** BEGIN GMANGA *** + ****************** + */ + _r(t) { + return (this._r = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { + return typeof t; + } + : function (t) { + return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t; + } + )(t); + } + _a(t) { + return (this._a = "function" == typeof Symbol && "symbol" === this._r(Symbol.iterator) ? function (t) { + return this._r(t); + } + .bind(this) + : function (t) { + return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : this._r(t); + } + .bind(this) + )(t); + } + _isObject(t) { + var e = this._a(t); + return "function" === e || "object" === e && !!t; + } + _dataExists(t) { + var e = null !== t; + return "object" === this._a(t) ? e && 0 !== Object.keys(t).length : "" !== t && void 0 !== t && e; + } + _haqiqa(t) { + let c = { + default: CryptoJS }; + if (!this._dataExists(t) || "string" != typeof t) + return !1; + var e = t.split("|") + , n = e[0] + , r = e[2] + , o = e[3] + , i = c.default.SHA256(o).toString() + , a = c.default.AES.decrypt({ + ciphertext: c.default.enc.Base64.parse(n) + }, + c.default.enc.Hex.parse(i), { + iv: c.default.enc.Base64.parse(r) + }); + return JSON.parse(c.default.enc.Utf8.stringify(a)); + } + _unpack(t) { + var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 1; + if (!t || e > t.maxLevel) + return t; + if (!this._isObject(t) || !t.isCompact) + return t; + var n = t.cols + , r = t.rows; + if (t.isObject) { + var o = {} + , i = 0; + return n.forEach(function (t) { + o[t] = this._unpack(r[i], e + 1), + i += 1; + } + .bind(this)), + o; + } + if (t.isArray) { + o = []; + return r.forEach(function (t) { + var e = {} + , r = 0; + n.forEach(function (n) { + e[n] = t[r], + r += 1; + }) + , + o.push(e); + }) + , + o; + } + } + /** + ***************** + * ** END GMANGA *** + **************** + */ +} \ No newline at end of file From f8f8b3b5a3b597102d6061c22ed8881b98212571 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Wed, 30 Nov 2022 14:04:55 +0100 Subject: [PATCH 20/47] Add Mangatales icon --- src/web/img/connectors/mangatales | Bin 0 -> 4815 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/mangatales diff --git a/src/web/img/connectors/mangatales b/src/web/img/connectors/mangatales new file mode 100644 index 0000000000000000000000000000000000000000..e686208109aceb268a12c0484eba458d8f5fca45 GIT binary patch literal 4815 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 Date: Wed, 30 Nov 2022 14:19:56 +0100 Subject: [PATCH 21/47] fix typo --- src/web/mjs/connectors/Mangatales.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/mjs/connectors/Mangatales.mjs b/src/web/mjs/connectors/Mangatales.mjs index 817999383b5..a56f641a255 100644 --- a/src/web/mjs/connectors/Mangatales.mjs +++ b/src/web/mjs/connectors/Mangatales.mjs @@ -3,7 +3,7 @@ import Manga from '../engine/Manga.mjs'; export default class MangaTales extends Connector { constructor() { super(); - super.id = 'mnagatales'; + super.id = 'mangatales'; super.label = 'MangaTales'; this.tags = ['manga', 'webtoon', 'arabic']; this.url = 'https://mangatales.com'; @@ -220,4 +220,4 @@ export default class MangaTales extends Connector { * ** END GMANGA *** **************** */ -} \ No newline at end of file +} From a015a6a82c8024a0ce7cd7acb49fc520db346595 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Fri, 9 Dec 2022 18:04:11 +0000 Subject: [PATCH 22/47] mangaplanet fix classname --- src/web/mjs/connectors/MangaPlanet.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/mjs/connectors/MangaPlanet.mjs b/src/web/mjs/connectors/MangaPlanet.mjs index 4747838bf0c..02ca1c087bc 100644 --- a/src/web/mjs/connectors/MangaPlanet.mjs +++ b/src/web/mjs/connectors/MangaPlanet.mjs @@ -1,7 +1,7 @@ import SpeedBinb from './templates/SpeedBinb.mjs'; import Manga from '../engine/Manga.mjs'; -export default class Futekiya extends SpeedBinb { +export default class MangaPlanet extends SpeedBinb { constructor() { super(); @@ -76,4 +76,4 @@ export default class Futekiya extends SpeedBinb { this.requestOptions.headers.set('x-referer', url); return data; } -} \ No newline at end of file +} From 0a2c519af479156add9abb9c48bf19eebaa9f788 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 17 Dec 2022 13:47:48 +0000 Subject: [PATCH 23/47] Madtheme template : Adjust connectors --- src/web/mjs/connectors/MangaBuddy.mjs | 19 ------------------- src/web/mjs/connectors/MangaForest.mjs | 10 ++++++++++ src/web/mjs/connectors/TrueManga.mjs | 8 ++++++-- 3 files changed, 16 insertions(+), 21 deletions(-) create mode 100644 src/web/mjs/connectors/MangaForest.mjs diff --git a/src/web/mjs/connectors/MangaBuddy.mjs b/src/web/mjs/connectors/MangaBuddy.mjs index e565674a53d..acffa971dde 100644 --- a/src/web/mjs/connectors/MangaBuddy.mjs +++ b/src/web/mjs/connectors/MangaBuddy.mjs @@ -7,23 +7,4 @@ export default class MangaBuddy extends MadTheme { this.tags = ['manga', 'webtoon', 'english']; this.url = 'https://mangabuddy.com'; } - async _getPages(chapter) { - let scriptPages = ` - new Promise(resolve => { - resolve(final_images); - }); - `; - let request = new Request(this.url + chapter.id, this.requestOptions); - let data = await Engine.Request.fetchUI(request, scriptPages); - return data.map(element => this.createConnectorURI(this.getAbsolutePath(element, request.url))); - } - async _handleConnectorURI(payload) { - let request = new Request(payload, this.requestOptions); - request.headers.set('x-referer', this.url); - let response = await fetch(request); - let data = await response.blob(); - data = await this._blobToBuffer(data); - this._applyRealMime(data); - return data; - } } \ No newline at end of file diff --git a/src/web/mjs/connectors/MangaForest.mjs b/src/web/mjs/connectors/MangaForest.mjs new file mode 100644 index 00000000000..99ef9377c69 --- /dev/null +++ b/src/web/mjs/connectors/MangaForest.mjs @@ -0,0 +1,10 @@ +import MadTheme from './templates/MadTheme.mjs'; +export default class MangaForest extends MadTheme { + constructor() { + super(); + super.id = 'MangaForest'; + super.label = 'MangaForest'; + this.tags = ['manga', 'webtoon', 'english']; + this.url = 'https://mangaforest.me'; + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/TrueManga.mjs b/src/web/mjs/connectors/TrueManga.mjs index 6895ff37ca5..657ac3d1dd1 100644 --- a/src/web/mjs/connectors/TrueManga.mjs +++ b/src/web/mjs/connectors/TrueManga.mjs @@ -1,5 +1,4 @@ import MadTheme from './templates/MadTheme.mjs'; - export default class TrueManga extends MadTheme { constructor() { super(); @@ -8,4 +7,9 @@ export default class TrueManga extends MadTheme { this.tags = ['manga', 'webtoon', 'english']; this.url = 'https://truemanga.com'; } -} + async _getPages(chapter) { + const request = new Request(this.url + chapter.id, this.requestOptions); + const data = await this.fetchDOM(request, 'div.chapter-image source'); + return data.map(element => this.createConnectorURI(this.getAbsolutePath(element.dataset['src'], request.url))); + } +} \ No newline at end of file From 6e9aa153f2171c1448995ec73abb628b0bf1c03d Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 17 Dec 2022 13:48:49 +0000 Subject: [PATCH 24/47] Better MadTheme template --- src/web/mjs/connectors/templates/MadTheme.mjs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/web/mjs/connectors/templates/MadTheme.mjs b/src/web/mjs/connectors/templates/MadTheme.mjs index 2ea3ad84f16..a082856008a 100644 --- a/src/web/mjs/connectors/templates/MadTheme.mjs +++ b/src/web/mjs/connectors/templates/MadTheme.mjs @@ -34,7 +34,7 @@ export default class MadTheme extends Connector { const data = await this.fetchDOM(request, this.queryMangas); return data.map(element => { return { - id: this.getRootRelativeOrAbsoluteLink(element, this.url), + id: element.pathname, title: element.title.trim() }; }); @@ -53,8 +53,22 @@ export default class MadTheme extends Connector { }); } async _getPages(chapter) { - const request = new Request(new URL(chapter.id, this.url), this.requestOptions); - const data = await this.fetchDOM(request, this.queryPages); - return data.map(ele => ele.children[0].dataset.src); + let scriptPages = ` + new Promise(resolve => { + resolve(final_images); + }); + `; + let request = new Request(this.url + chapter.id, this.requestOptions); + let data = await Engine.Request.fetchUI(request, scriptPages); + return data.map(element => this.createConnectorURI(this.getAbsolutePath(element, request.url))); + } + async _handleConnectorURI(payload) { + let request = new Request(payload, this.requestOptions); + request.headers.set('x-referer', this.url); + let response = await fetch(request); + let data = await response.blob(); + data = await this._blobToBuffer(data); + this._applyRealMime(data); + return data; } } \ No newline at end of file From 0ab3ec7e3903ca436ca3d4968b6e170ae0bf4d92 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 17 Dec 2022 13:51:40 +0000 Subject: [PATCH 25/47] add mangaforest icon --- src/web/img/connectors/mangaforest | Bin 0 -> 5671 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/mangaforest diff --git a/src/web/img/connectors/mangaforest b/src/web/img/connectors/mangaforest new file mode 100644 index 0000000000000000000000000000000000000000..7ca90dd0e32a34e1e59012bc9d1f1c98fa35253b GIT binary patch literal 5671 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 N002ovPDHLkV1jYUw^INB literal 0 HcmV?d00001 From 7f3ed2677921fefe41a328088dc82a9a033ddc7a Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 17 Dec 2022 17:21:11 +0000 Subject: [PATCH 26/47] Madtheme : tweaking getChapters --- src/web/mjs/connectors/templates/MadTheme.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/web/mjs/connectors/templates/MadTheme.mjs b/src/web/mjs/connectors/templates/MadTheme.mjs index a082856008a..07415bbaa63 100644 --- a/src/web/mjs/connectors/templates/MadTheme.mjs +++ b/src/web/mjs/connectors/templates/MadTheme.mjs @@ -40,7 +40,9 @@ export default class MadTheme extends Connector { }); } async _getChapters(manga) { - let uri = new URL('/api/manga'+manga.id+'/chapters?source=detail', this.url); + let mangaid = manga.id.split('/'); + mangaid = '/'+mangaid[mangaid.length - 1];//make sure to take last part of url for the api + let uri = new URL('/api/manga'+mangaid+'/chapters?source=detail', this.url); let request = new Request(uri, this.requestOptions); let data = await this.fetchDOM(request, 'a'); return data.map(element => { @@ -71,4 +73,4 @@ export default class MadTheme extends Connector { this._applyRealMime(data); return data; } -} \ No newline at end of file +} From 205a9239b5020b1585ff3d0bd69d03fa12d6bf24 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 24 Dec 2022 15:44:06 +0000 Subject: [PATCH 27/47] Update MadTheme.mjs --- src/web/mjs/connectors/templates/MadTheme.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/mjs/connectors/templates/MadTheme.mjs b/src/web/mjs/connectors/templates/MadTheme.mjs index 07415bbaa63..aa6fec06043 100644 --- a/src/web/mjs/connectors/templates/MadTheme.mjs +++ b/src/web/mjs/connectors/templates/MadTheme.mjs @@ -7,7 +7,7 @@ export default class MadTheme extends Connector { super.label = undefined; this.tags = []; this.url = undefined; - this.path = '/az-list?page='; + this.path = '/az-list'; this.queryMangaTitleFromURI = 'div.name.box h1'; this.queryMangas = 'div.thumb a'; this.queryPages = 'div.chapter-image'; @@ -29,7 +29,7 @@ export default class MadTheme extends Connector { return mangaList; } async _getMangasFromPage(page) { - const uri = new URL(this.path + page, this.url); + const uri = new URL(this.path + '?page=' + page, this.url); const request = new Request(uri, this.requestOptions); const data = await this.fetchDOM(request, this.queryMangas); return data.map(element => { From 9fcc1687e33099424973d6bf6c96628461b544fb Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 24 Dec 2022 22:42:39 +0000 Subject: [PATCH 28/47] Manga1001 : Use MadThme and unscramble pictures Fixes https://github.com/manga-download/hakuneko/issues/5155 --- src/web/mjs/connectors/Manga1001.mjs | 126 +++++++++++++-------------- 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/src/web/mjs/connectors/Manga1001.mjs b/src/web/mjs/connectors/Manga1001.mjs index 57a9c6a9c8f..541b5ab2754 100644 --- a/src/web/mjs/connectors/Manga1001.mjs +++ b/src/web/mjs/connectors/Manga1001.mjs @@ -1,85 +1,83 @@ -import Connector from '../engine/Connector.mjs'; -import Manga from '../engine/Manga.mjs'; +import MadTheme from './templates/MadTheme.mjs'; -export default class Manga1001 extends Connector { +export default class Manga1001 extends MadTheme { constructor() { super(); super.id = 'manga1001'; super.label = 'Manga 1001'; this.tags = [ 'manga', 'japanese' ]; - this.url = 'https://manga1001.com'; + this.url = 'https://manga1001.club'; + this.path = '/popular'; } - - canHandleURI(uri) { - return /^manga100[01]\.com$/.test(uri.hostname); - } - - async _getMangaFromURI(uri) { - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, 'header.entry-header h1.entry-title'); - let id = decodeURI(uri.pathname); - let title = data[0].textContent.trim(); - return new Manga(this, id, title); - } - - async _getMangas() { - let mangaList = []; - let uri = new URL(this.url); - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, 'nav.pagination div.nav-links a.page-numbers:nth-last-of-type(2)'); - let pageCount = parseInt(data[0].text.trim()); - 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(`/page/${page}/`, this.url); - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, 'main#main header.entry-header h3.entry-title a'); - return data.map(element => { - return { - id: decodeURI(element.pathname), - 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.chaplist table.table tbody tr td:first-of-type a'); + const uri = new URL(manga.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'ul#chapter-list li a'); return data.map(element => { return { - id: decodeURI(element.pathname), - title: element.text.trim(), - language: '' + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.querySelector('strong.chapter-title').textContent.trim() }; }); } - async _getPages(chapter) { - let uri = new URL(chapter.id, this.url); - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, 'div.entry-content figure.wp-block-image source'); + //first : fetch page to read slices (with script execution) + let scriptPages = ` + new Promise(resolve => { + setTimeout( () => { + resolve(slices); + }, + 1000); + }); + `; + let request = new Request(this.url + chapter.id, this.requestOptions); + const slices = await Engine.Request.fetchUI(request, scriptPages, 5000); + //now fetch raw page to get picture elements (which may be full of holes if we let scripts running) + request = new Request(this.url + chapter.id, this.requestOptions); + const data = await this.fetchDOM(request, 'div#chapter-images source' ); return data.map(element => { - return this.createConnectorURI({ - url: this.getAbsolutePath(element.dataset.src || element, request.url), - referer: request.url - }); + if (element.dataset['src'].includes('scramble')) { + return this.createConnectorURI({ + url : element.dataset['src'], + slices : slices}); + } else return element.dataset['src']; }); } - async _handleConnectorURI(payload) { - let request = new Request(payload.url, this.requestOptions); - request.headers.set('x-referer', payload.referer); - let response = await fetch(request); + const request = new Request(payload.url, this.requestOptions); + const response = await fetch(request); let data = await response.blob(); - data = await this._blobToBuffer(data); - this._applyRealMime(data); - return data; + data = await this.deobfuscate(data, payload.slices); + return this._blobToBuffer(data); + } + async deobfuscate(imgdata, slicedata) { + let bitmap = await createImageBitmap(imgdata); + return new Promise(resolve => { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext('2d'); + canvas.width = bitmap.width; + canvas.height = bitmap.height; + const sl1 = slicedata.shift(); + const sl2 = slicedata.shift(); + const colnum = canvas.width / sl1; + const rownum = canvas.height/ sl2; + for (var i = 0; i< slicedata.length; i++) { + var f = slicedata[i]; + var b = parseInt(f / sl1); + var c = f - b * sl1; + var d = c * colnum; + var e = b * rownum; + var a = parseInt(i / sl1); + var g = i - a * sl1; + var k = g * colnum; + var h = a * rownum; + ctx.drawImage(bitmap, k, h, colnum, rownum, d, e, colnum, rownum); + } + canvas.toBlob(data => { + resolve(data); + }, + Engine.Settings.recompressionFormat.value, parseFloat(Engine.Settings.recompressionQuality.value)/100); + }); } -} \ No newline at end of file +} From a4744943c0c2eff5355034a353f4759189aad741 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 24 Dec 2022 23:26:16 +0000 Subject: [PATCH 29/47] Update MadTheme.mjs --- src/web/mjs/connectors/templates/MadTheme.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/mjs/connectors/templates/MadTheme.mjs b/src/web/mjs/connectors/templates/MadTheme.mjs index aa6fec06043..c93260c1f90 100644 --- a/src/web/mjs/connectors/templates/MadTheme.mjs +++ b/src/web/mjs/connectors/templates/MadTheme.mjs @@ -9,7 +9,7 @@ export default class MadTheme extends Connector { this.url = undefined; this.path = '/az-list'; this.queryMangaTitleFromURI = 'div.name.box h1'; - this.queryMangas = 'div.thumb a'; + this.queryMangas = 'div.book-detailed-item div.thumb a'; this.queryPages = 'div.chapter-image'; this.queryChapterTitle = 'strong.chapter-title'; } From 3548f6b74e28315524e0e4f08541affae7fd4772 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 24 Dec 2022 23:38:02 +0000 Subject: [PATCH 30/47] Update MangaForest.mjs --- src/web/mjs/connectors/MangaForest.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/mjs/connectors/MangaForest.mjs b/src/web/mjs/connectors/MangaForest.mjs index 99ef9377c69..ceaaa82918f 100644 --- a/src/web/mjs/connectors/MangaForest.mjs +++ b/src/web/mjs/connectors/MangaForest.mjs @@ -2,9 +2,9 @@ import MadTheme from './templates/MadTheme.mjs'; export default class MangaForest extends MadTheme { constructor() { super(); - super.id = 'MangaForest'; + super.id = 'mangaforest'; super.label = 'MangaForest'; this.tags = ['manga', 'webtoon', 'english']; this.url = 'https://mangaforest.me'; } -} \ No newline at end of file +} From a235a325e8e83182fc869414a13d973bd3f10e7c Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 24 Dec 2022 23:59:14 +0000 Subject: [PATCH 31/47] Add Manga1001Top --- src/web/mjs/connectors/Manga1001Top.mjs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/web/mjs/connectors/Manga1001Top.mjs diff --git a/src/web/mjs/connectors/Manga1001Top.mjs b/src/web/mjs/connectors/Manga1001Top.mjs new file mode 100644 index 00000000000..3e33876ec8b --- /dev/null +++ b/src/web/mjs/connectors/Manga1001Top.mjs @@ -0,0 +1,12 @@ +import MadTheme from './templates/MadTheme.mjs'; + +export default class Manga1001Top extends MadTheme { + + constructor() { + super(); + super.id = 'manga1001top'; + super.label = 'Manga 1001.top'; + this.tags = [ 'manga', 'webtoon', 'english' ]; + this.url = 'https://manga1001.top'; + } +} \ No newline at end of file From 58c2c511850f7e85b7e17adcfb87da1f4d9cf02b Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 24 Dec 2022 23:59:40 +0000 Subject: [PATCH 32/47] Add manga1001top icon --- src/web/img/connectors/manga1001top | Bin 0 -> 5547 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/web/img/connectors/manga1001top diff --git a/src/web/img/connectors/manga1001top b/src/web/img/connectors/manga1001top new file mode 100644 index 0000000000000000000000000000000000000000..84eb7428b73a6dec6007f3712fe15c640fba500a GIT binary patch literal 5547 zcmaJ_XIK+kw@v`5ktQG@B?P3Gga84gCsd^)O{pPB3xp7=gpL%YN$*_*6;OH;q)HL6 z0gCjZh%`AAsb4(zob%lu_uP4A&&=M>yWX|-+V8Aq&%_xSXkVn|qy+!~7j<<|CZxOl z??z2Yx-Mq<%ad+2I2|h=0DzA9cLTng2{{1($RVy~mIO=v8;WQztfYgJmm@|p0E;7` z0RSb{0GtEb9YX*+V(z$lDnqu~Iw4?JCuN9*tUgp9hs3zJ>IC62ra=Z~=pc8rf)hkl z1*{aHNFu;u2oB%?tcRzMVt_K_PhLgR^Y39P2>4G3!Ce`m{@WqgQr`%S^ulAnvXXEK zG!zB{%gIT?WMt*!VB%nDC`?)kN;>5vU^0rba*EOj@LvanFZ>-Tn1X`BZw_f`2@*oWC(x7N5Fp{{!~Zt}3gd&uyW$A0UY_9J zj1G=oz650mDboL$0v7iVt*6glX(A;|D!>6J1(SsSp3>hyef|F*ipBne_92*H{u}TA zN$g`5h{H&kV0^rM@n{SQ&i~sLP7#U6I1s$>W?o(%e`nFi#f#wO`B(>v7vffkDDgXeOrF2p1 zW&wE{`PXq=1AzmfGrON{$XJ>KQYh5Iz(75gKKTrc)&48pbgAb=9ntb37!@~udoVN2 zu-eR^WrdlSFajIoQ|Mi(N*m6gH_UXGz6Y-;8+jY56LW(KKIt^8;t={ z+;E(SSLV0En@Stwm$ExAJ93_~ZfACNlAm7`N4tnYN5~rCYW5lN40+^d9ET##6Vq>z z1HE%s9)5N^^qGfjOKxAc=Rq!k-BO`l#e&mwz!1uIhGpoY;0@o92OUhqL2*n=TiWO6 zwEPdjC4u5~j%0sl|R_h}BwVwD83z*D^gNrqg4Z0{y5tYD)1AqYMWGC1v z{>&w-P)17+g!vggJo}u<@&Z>JUkGA*vdb_`5;U2}y1Ag{=wj7BzAwaX?N6?LW1iP> zImGaf^PLgZ+d3fqSL26w*mL;9)#s1d+dlTcu0${LstDX`l)E=EM@5O9*X}BxXiKLS zQRj-Vd&JUeJ<~P=1dG!x^NmQAkL!g`+-<@>xZaiIF)vZ1>We-vy+p=+?=e*Awc5j% zQ5ZgtX~u6wr?TI8%O7Xx;izxS%1xZ|&ouJ}4EGDi{1Ll<@TQcbMvT?0#lyJ=2rfef z75p-(VRne6B_limO>jo3Dbh>l$mrk5AoPeDC2|-qB9dU_uhVgqRiUF`J~q0armRMVb*?W_<=8L4&&Iveql;KI$OIp@Q#sslUipEF$->H4sd zjwuauC->gXv{4uq%rE1eGk6}lJn`_D&%ZX1&7e~B_12t*#S`a!IpDLj?BH}-JKIi! z<`&&wn>JBP;M+~CB4F!DEe&uK6!Q8Y)m=hew!cDl>*JEPaMngHFqIc)0BA6#z4hR{2tH_k0ELtraTlG+D% zw^H)6)PB9>n+3isi?vW|h`%PU;}@)C1&k3{q>tE#1DG9kCRcjPuQwx+S`5OzwN`=@ zE(x?`?PnpalDE}bJqhbsKpIC!>H$8uOq9=00Y&N@IMWsX2Z?_?L_pOxPkuSwB+x2G z0M(?Y>-hPbJ379(HC&Chof9&Fw6%FtQPx7TL#NCq)W!YFl^`qs9DuF2gSaWZoCs>^;^qVud? zT)L-6{yEi+ua;`#>L8s^W3hBIH-K?z3AOvWJOU8)CY(w10j$xYrGRy1Aq_W-SbsI;E

s6Z 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 tsVlQ+wac>y5y_MitJ|`4j~}Y Date: Tue, 27 Dec 2022 20:48:28 +0000 Subject: [PATCH 33/47] Add files via upload --- src/web/mjs/connectors/MangaHereToday.mjs | 5 +-- src/web/mjs/connectors/Pururin.mjs | 55 +++++++++-------------- 2 files changed, 22 insertions(+), 38 deletions(-) diff --git a/src/web/mjs/connectors/MangaHereToday.mjs b/src/web/mjs/connectors/MangaHereToday.mjs index dc216431632..00b8ead5037 100644 --- a/src/web/mjs/connectors/MangaHereToday.mjs +++ b/src/web/mjs/connectors/MangaHereToday.mjs @@ -20,8 +20,7 @@ export default class MangaHereToday extends AnyACG { let data = await this.fetchDOM(request, this.queryMangaTitle); let id = uri.pathname + uri.search; //HACK We need to remove trailing "Manga" from the title - let r = new RegExp(' Manga$'); - let title = data[0].querySelector(this.queryMangaTitleText).textContent.replace(r, '').trim(); + let title = data[0].querySelector(this.queryMangaTitleText).textContent.replace(/\s+Manga$/i, '').trim(); return new Manga(this, id, title); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/Pururin.mjs b/src/web/mjs/connectors/Pururin.mjs index 95edab27c10..a7efcb887bf 100644 --- a/src/web/mjs/connectors/Pururin.mjs +++ b/src/web/mjs/connectors/Pururin.mjs @@ -7,7 +7,7 @@ export default class Pururin extends Connector { super.label = 'Pururin'; this.tags = ['manga', 'hentai', 'english']; this.url = 'https://pururin.to'; - this.CDN = "https://cdn.pururin.to/assets/images/data/{1}/{2}.{3}"; + this.CDN = "https://cdn.pururin.to/assets/images/data/"; this.path = "/browse/title"; this.api = "/api/contribute/gallery/info"; this.queryMangasPageCount = 'ul.pagination.flex-wrap li:nth-last-of-type(2) a'; @@ -40,46 +40,35 @@ export default class Pururin extends Connector { }); } async _getChapters(manga) { - let mid = this.getRootRelativeOrAbsoluteLink(manga.id, this.url); - const chapters = [{ id: mid, title: 'Chapter' }]; - return chapters; + return [{ id: manga.id, title: 'Chapter' }]; } async _getPages(chapter) { - let pictures = []; - const myRegexp = new RegExp(/gallery\/([0-9]+)/g); - let match = myRegexp.exec(chapter.id); - let mangaID = match[1]; - let params = '{"id":'+mangaID+',"type":2}'; + let mangaID = chapter.id.match(/\/gallery\/([0-9]+)/)[1]; const uri = new URL(this.api, this.url); - const request = new Request(uri, { - method: 'POST', - body: params, - headers: { - 'x-origin': this.url, - 'x-referer': this.url, - 'Content-Type': 'application/json;charset=UTF-8', - 'X-Requested-With': 'XMLHttpRequest', - } - }); + const request = this.getApiRequest(uri, mangaID); const data = await this.fetchJSON(request); let pagesMax = data.gallery.total_pages; let extension = data.gallery.image_extension; //https://cdn.pururin.to/assets/images/data//.image_extension - for (var i = 1; i <= pagesMax; i++) { - let imageuri = this.CDN.replace('{1}', mangaID).replace('{2}', i).replace('{3}', extension); - pictures.push(imageuri); - } - return pictures; + return new Array(pagesMax).fill().map((_, index) => new URL(`${this.CDN}/${mangaID}/${index + 1}.${extension}`).href); } async _getMangaFromURI(uri) { - const myRegexp = new RegExp(/gallery\/([0-9]+)/g); - let match = myRegexp.exec(uri.href); - let mangaID = match[1]; - let params = '{"id":'+mangaID+',"type":2}'; + let mangaID = uri.href.match(/\/gallery\/([0-9]+)/)[1]; const req = new URL(this.api, this.url); - const request = new Request(req, { + const request = this.getApiRequest(req, mangaID); + const data = await this.fetchJSON(request); + const title = data.gallery.title; + const id = this.getRootRelativeOrAbsoluteLink(uri, this.url); + return new Manga(this, id, title); + } + getApiRequest(url, id) { + let params = { + id: id, + type:2 + }; + return new Request(url, { method: 'POST', - body: params, + body: JSON.stringify(params), headers: { 'x-origin': this.url, 'x-referer': this.url, @@ -87,9 +76,5 @@ export default class Pururin extends Connector { 'X-Requested-With': 'XMLHttpRequest', } }); - const data = await this.fetchJSON(request); - const title = data.gallery.title; - const id = this.getRootRelativeOrAbsoluteLink(uri, this.url); - return new Manga(this, id, title); } -} \ No newline at end of file +} From 7a172ef9149bb3f1a9177835448ff18b69261e95 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 2 Jan 2023 20:17:13 +0000 Subject: [PATCH 34/47] Update Manga1001.mjs --- src/web/mjs/connectors/Manga1001.mjs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/web/mjs/connectors/Manga1001.mjs b/src/web/mjs/connectors/Manga1001.mjs index 541b5ab2754..2842bf0fad0 100644 --- a/src/web/mjs/connectors/Manga1001.mjs +++ b/src/web/mjs/connectors/Manga1001.mjs @@ -58,21 +58,21 @@ export default class Manga1001 extends MadTheme { const ctx = canvas.getContext('2d'); canvas.width = bitmap.width; canvas.height = bitmap.height; - const sl1 = slicedata.shift(); - const sl2 = slicedata.shift(); - const colnum = canvas.width / sl1; - const rownum = canvas.height/ sl2; + const colNum = slicedata.shift(); + const rowNum = slicedata.shift(); + const pieceWidth = canvas.width / colNum; + const pieceHeight = canvas.height/rowNum; for (var i = 0; i< slicedata.length; i++) { var f = slicedata[i]; - var b = parseInt(f / sl1); - var c = f - b * sl1; - var d = c * colnum; - var e = b * rownum; - var a = parseInt(i / sl1); - var g = i - a * sl1; - var k = g * colnum; - var h = a * rownum; - ctx.drawImage(bitmap, k, h, colnum, rownum, d, e, colnum, rownum); + var b = parseInt(f / colNum); + var c = f - b * colNum; + var d = c * pieceWidth; + var e = b * pieceHeight; + var a = parseInt(i / colNum); + var g = i - a * colNum; + var k = g * pieceWidth; + var h = a * pieceHeight; + ctx.drawImage(bitmap, k, h, pieceWidth, pieceHeight, d, e, pieceWidth, pieceHeight); } canvas.toBlob(data => { resolve(data); From 6e152fbe44f343a261e4c35f18ceea848d1f4206 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 2 Jan 2023 21:36:41 +0000 Subject: [PATCH 35/47] Add files via upload --- src/web/mjs/connectors/Anizero.mjs | 65 ++++++++++++------------ src/web/mjs/connectors/Mangatales.mjs | 72 +++++++++++++------------- src/web/mjs/connectors/Manhwa18cc.mjs | 4 +- src/web/mjs/connectors/MavAnimes.mjs | 73 ++++++++++++--------------- src/web/mjs/connectors/Yurineko.mjs | 9 ++-- 5 files changed, 108 insertions(+), 115 deletions(-) diff --git a/src/web/mjs/connectors/Anizero.mjs b/src/web/mjs/connectors/Anizero.mjs index bca9d453126..fec938c5192 100644 --- a/src/web/mjs/connectors/Anizero.mjs +++ b/src/web/mjs/connectors/Anizero.mjs @@ -1,5 +1,6 @@ import Connector from '../engine/Connector.mjs'; import BloggerVideo from '../videostreams/BloggerVideo.mjs'; + export default class AniZero extends Connector { constructor() { super(); @@ -7,39 +8,35 @@ export default class AniZero extends Connector { super.label = 'AniZero'; this.tags = [ 'anime', 'spanish' ]; this.url = 'https://anizero.site'; - this.search_nonce = undefined; } - async getnoonce(){ - if (this.search_nonce){ - return; - } + async getnoonce() { let url = new URL(this.url); - let request = new Request(url, this.requestOptions); - let scriptPages = ` + const request = new Request(url, this.requestOptions); + const scriptPages = ` new Promise(resolve => { resolve(js_global.search_nonce); }); `; - this.search_nonce = await Engine.Request.fetchUI(request, scriptPages); + return await Engine.Request.fetchUI(request, scriptPages); } async _getMangas() { - this.getnoonce(); + const noonce = await this.getnoonce(); let mangaList = []; for (let page = 1, run = true; run; page++) { - let mangas = await this._getMangasFromPage(page); + let mangas = await this._getMangasFromPage(page, noonce); mangas.length > 0 ? mangaList.push(...mangas) : run = false; } return mangaList; } - async _getMangasFromPage(page) { + async _getMangasFromPage(page, noonce) { let form = new URLSearchParams(); - form.append('search_nonce', this.search_nonce); + form.append('search_nonce', noonce); form.append('action', 'show_animes_ajax'); form.append('letra', ''); form.append('paged', page); - let body = form.toString(); - let url = new URL('/wp-admin/admin-ajax.php', this.url); - let request = new Request(url, { + const body = form.toString(); + const url = new URL('/wp-admin/admin-ajax.php', this.url); + const request = new Request(url, { method: 'POST', body: body, headers: { @@ -49,8 +46,8 @@ export default class AniZero extends Connector { 'X-Requested-With': 'XMLHttpRequest', } }); - let data = await this.fetchJSON(reques); - if (!data.animes){ + const data = await this.fetchJSON(request); + if (!data.animes) { return []; } return data.animes.map(element => { @@ -69,8 +66,8 @@ export default class AniZero extends Connector { let form = new URLSearchParams(); form.append('action', 'show_videos'); form.append('anime_id', mangaid); - let body = form.toString(); - let url = new URL('/api', this.url); + const body = form.toString(); + const url = new URL('/api', this.url); request = new Request(url, { method: 'POST', body: body, @@ -112,40 +109,46 @@ export default class AniZero extends Connector { }); } chapterslist.push(...chapters); - return(chapterslist.reverse()); + return chapterslist.reverse(); } async _getPages(chapter) { let request = new Request(new URL(chapter.id, this.url), this.requestOptions); - let scriptPages = ` - new Promise(resolve => { - resolve(players_links); + const script = ` + new Promise((resolve, reject) => { + setTimeout(() => { + try { + resolve(players_links); + } + catch(error) { + reject(error); + } + }, + 3000); }); `; - let data = await Engine.Request.fetchUI(request, scriptPages); + let data = await Engine.Request.fetchUI(request, script); let videolink = Object.values(data)[0]; //remove redirector - if(videolink.match(/assistirFHD/)){ + if(videolink.match(/assistirFHD/)) { let tmp = new URL(videolink); videolink = decodeURI(tmp.searchParams.get("video")); } //if link is not already blogger - if (!videolink.match(/blogger/)) - { + if (!videolink.match(/blogger/)) { //fetch the dom returned by the link -its html= request = new Request(videolink, this.requestOptions); data = await this.fetchDOM( request, 'body'); let ele = data[0].querySelectorAll('script'); ele.forEach(node =>{ - if (node.text.match(/blogger/)){ + if (node.text.match(/blogger/)) { videolink = node.text.split("'").filter(el => el.match(/blogger/))[0]; } }); } - if (!videolink.match(/blogger/)) - { + if (!videolink.match(/blogger/)) { throw Error('Unsupported Video host : '+ videolink); } - let vid = await new BloggerVideo(videolink).getStream(); + const vid = await new BloggerVideo(videolink).getStream(); return {video: vid, subtitles: [] }; } } \ No newline at end of file diff --git a/src/web/mjs/connectors/Mangatales.mjs b/src/web/mjs/connectors/Mangatales.mjs index a56f641a255..50870551324 100644 --- a/src/web/mjs/connectors/Mangatales.mjs +++ b/src/web/mjs/connectors/Mangatales.mjs @@ -14,17 +14,17 @@ export default class MangaTales extends Connector { exclude: [] }, oneshot: { - value: null }, + value: null }, story_status: { - include: [], exclude: [] }, + include: [], exclude: [] }, translation_status: { - include: [], exclude: ['3'] }, + include: [], exclude: ['3'] }, categories: { - include: [], exclude: [] }, + include: [], exclude: [] }, chapters: { - min: '', max: '' }, + min: '', max: '' }, dates: { - start: null, end: null }, + start: null, end: null }, page: 0 }; } @@ -66,7 +66,7 @@ export default class MangaTales extends Connector { // mangaTitle.replace(/\s+/g, '-').replace(/[^-\w]+/gi, '').toLowerCase(); } let request = new Request(new URL(`/api/mangas/${ - manga.id} + manga.id} `, this.url), this.requestOptions); let data = await this.fetchJSON(request); data = data['iv'] ? this._haqiqa(data.data) : data; @@ -93,11 +93,9 @@ export default class MangaTales extends Connector { let images = []; if (data.hq_pages && data.hq_pages.length > 0) { images = data.hq_pages.split('\r\n'); - } - else if (data.mq_pages && data.mq_pages.length > 0) { + } else if (data.mq_pages && data.mq_pages.length > 0) { images = data.mq_pages.split('\r\n'); - } - else if (data.lq_pages && data.lq_pages.length > 0) { + } else if (data.lq_pages && data.lq_pages.length > 0) { images = data.lq_pages.split('\r\n'); } return images.map(image => { @@ -139,20 +137,20 @@ export default class MangaTales extends Connector { return (this._r = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t; } - : function (t) { - return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t; - } + : function (t) { + return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t; + } )(t); } _a(t) { return (this._a = "function" == typeof Symbol && "symbol" === this._r(Symbol.iterator) ? function (t) { return this._r(t); } - .bind(this) - : function (t) { - return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : this._r(t); - } - .bind(this) + .bind(this) + : function (t) { + return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : this._r(t); + } + .bind(this) )(t); } _isObject(t) { @@ -165,45 +163,45 @@ export default class MangaTales extends Connector { } _haqiqa(t) { let c = { - default: CryptoJS }; + default: CryptoJS }; if (!this._dataExists(t) || "string" != typeof t) - return !1; + return !1; var e = t.split("|") - , n = e[0] - , r = e[2] - , o = e[3] - , i = c.default.SHA256(o).toString() - , a = c.default.AES.decrypt({ - ciphertext: c.default.enc.Base64.parse(n) - }, - c.default.enc.Hex.parse(i), { - iv: c.default.enc.Base64.parse(r) - }); + , n = e[0] + , r = e[2] + , o = e[3] + , i = c.default.SHA256(o).toString() + , a = c.default.AES.decrypt({ + ciphertext: c.default.enc.Base64.parse(n) + }, + c.default.enc.Hex.parse(i), { + iv: c.default.enc.Base64.parse(r) + }); return JSON.parse(c.default.enc.Utf8.stringify(a)); } _unpack(t) { var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 1; if (!t || e > t.maxLevel) - return t; + return t; if (!this._isObject(t) || !t.isCompact) - return t; + return t; var n = t.cols - , r = t.rows; + , r = t.rows; if (t.isObject) { var o = {} - , i = 0; + , i = 0; return n.forEach(function (t) { o[t] = this._unpack(r[i], e + 1), i += 1; } - .bind(this)), + .bind(this)), o; } if (t.isArray) { o = []; return r.forEach(function (t) { var e = {} - , r = 0; + , r = 0; n.forEach(function (n) { e[n] = t[r], r += 1; diff --git a/src/web/mjs/connectors/Manhwa18cc.mjs b/src/web/mjs/connectors/Manhwa18cc.mjs index 2bff461a934..2ae4eafba89 100644 --- a/src/web/mjs/connectors/Manhwa18cc.mjs +++ b/src/web/mjs/connectors/Manhwa18cc.mjs @@ -25,7 +25,7 @@ export default class Manhwa18cc extends WordPressMadara { for (let page = 1, run = true; run; page++) { let mangas = await this._getMangasFromPage(page); mangaList.push(...mangas); - if (mangas.length != this.mangaNumberPerPage){ + if (mangas.length != this.mangaNumberPerPage) { run = false; } } @@ -33,7 +33,7 @@ export default class Manhwa18cc extends WordPressMadara { } async _getMangasFromPage(page) { let request = new Request(new URL(this.path+'/'+page, this.url), this.requestOptions); - let data = await this.fetchDOM(request,this.queryMangas); + let data = await this.fetchDOM(request, this.queryMangas); return data.map(element => { return { id: this.getRootRelativeOrAbsoluteLink(element, request.url), diff --git a/src/web/mjs/connectors/MavAnimes.mjs b/src/web/mjs/connectors/MavAnimes.mjs index 3e681bf3fe2..80f842a93ef 100644 --- a/src/web/mjs/connectors/MavAnimes.mjs +++ b/src/web/mjs/connectors/MavAnimes.mjs @@ -11,62 +11,60 @@ export default class MavAnime extends Connector { super.label = 'MavAnimes'; this.tags = [ 'anime', 'french', 'multi-lingual' ]; this.url = 'https://mavanimes.co'; - this.genres = ['/tous-les-animes-en-vf','/tous-les-animes-en-vostfr-fullhd-2']; + this.genres = ['/tous-les-animes-en-vf', '/tous-les-animes-en-vostfr-fullhd-2']; } - + async _getMangas() { let mangaslist = []; - for (let i = 0;i < this.genres.length;i++) - { + for (let i = 0; i < this.genres.length; i++) { let request = new Request( new URL(this.genres[i], this.url), this.requestOptions ); let data = await this.fetchDOM( request, 'div#az-slider li a' ); let mangas = data.map(element => { return { id : element.pathname, title : element.text.trim() - } + }; }); mangaslist.push(...mangas); } - return(mangaslist); + return mangaslist; } - + async _getChapters(manga) { let chapterslist = []; let request = new Request( new URL(manga.id, this.url), this.requestOptions ); let data = await this.fetchDOM( request, 'body' ); //first attempt to get the episodes list and various hosts let chaptersNodes = data[0].querySelectorAll('header.entry-header h2 a'); - for (let i = 0;i < chaptersNodes.length;i++) { + for (let i = 0; i < chaptersNodes.length; i++) { chapterslist.push(...await this.getChapterPlayers(chaptersNodes[i])); } //second attempts, with another CSS Selector - if (chapterslist.length == 0){ + if (chapterslist.length == 0) { chaptersNodes = data[0].querySelectorAll('div.entry-content a'); - for (let i = 0;i < chaptersNodes.length;i++) { + for (let i = 0; i < chaptersNodes.length; i++) { chapterslist.push(...await this.getChapterPlayers(chaptersNodes[i])); } } //if nothing worked, check if the page itself got iframes // Somes film does not have a chapter page and just one page with video frames - if (chapterslist.length == 0){ + if (chapterslist.length == 0) { try{ let testnode= document.createElement("a"); testnode.href = new URL(manga.id, this.url); testnode.text = manga.title; chapterslist.push(...await this.getChapterPlayers(testnode)); - } - catch(e) - { + } catch(e) { + // } } - if (chapterslist.length == 0){ - throw('No episode / supported video hoster found :/ !'); + if (chapterslist.length == 0) { + throw'No episode / supported video hoster found :/ !'; } - return(chapterslist); + return chapterslist; } - - async getChapterPlayers(chapterNode){ + + async getChapterPlayers(chapterNode) { let request = new Request(chapterNode.href, this.requestOptions); let scriptPages = ` new Promise(resolve => { @@ -79,51 +77,46 @@ export default class MavAnime extends Connector { return { id: element, title : sourcesite + ' '+ chapterNode.text.replace(':•', '').trim() - } - }).filter(el => (!el.title.match(/\[UNK\]/))); + }; + }).filter(el => !el.title.match(/\[UNK\]/)); } - + async _getPages(chapter) { - + let sourcesite = this.getWebsiteTag(chapter.id); - switch(sourcesite){ + switch(sourcesite) { case '[MAV]': { - let vid = await new Mav(this.url,chapter.id).getStream(); - return (vid.type == 'mp4') ? {video: vid, subtitles: [] }: {hash: 'id,language,resolution', mirrors: [ vid ], subtitles: [], referer : chapter.id }; - break; + let vid = await new Mav(this.url, chapter.id).getStream(); + return vid.type == 'mp4' ? {video: vid.file, subtitles: [] }: {hash: 'id,language,resolution', mirrors: [ vid.file ], subtitles: [], referer : chapter.id }; } case '[Streamtape]': { let vid = await new Streamtape(chapter.id).getStream(); return{video: vid, subtitles: [] }; - break; } case '[SendVid]': { let vid = await new SendVid(chapter.id).getStream(); return {hash: 'id,language,resolution', mirrors: [ vid ], subtitles: [], referer : chapter.id }; - break; } case '[VOESX]': { let vid = await new VoeSX(chapter.id).getStream(); - return (vid.mp4) ? {video: vid.mp4, subtitles: [] }: {hash: 'id,language,resolution', mirrors: [ vid.hls ], subtitles: [], referer : chapter.id }; - break; + return vid.mp4 ? {video: vid.mp4, subtitles: [] }: {hash: 'id,language,resolution', mirrors: [ vid.hls ], subtitles: [], referer : chapter.id }; } default: break; } - - + } - - getWebsiteTag(link){ + + getWebsiteTag(link) { let sourcesite = '[UNK]'; - sourcesite = (link.match(/mavplay|mavlecteur|mavavid/)) ? '[MAV]' : sourcesite; - sourcesite = (link.match(/streamtape/)) ? '[Streamtape]' : sourcesite; - sourcesite = (link.match(/sendvid.com/)) ? '[SendVid]' : sourcesite - sourcesite = (link.match(/voe.sx/)) ? '[VOESX]' : sourcesite; + sourcesite = link.match(/mavplay|mavlecteur|mavavid/) ? '[MAV]' : sourcesite; + sourcesite = link.match(/streamtape/) ? '[Streamtape]' : sourcesite; + sourcesite = link.match(/sendvid.com/) ? '[SendVid]' : sourcesite; + sourcesite = link.match(/voe.sx/) ? '[VOESX]' : sourcesite; return sourcesite; - }; + } } \ No newline at end of file diff --git a/src/web/mjs/connectors/Yurineko.mjs b/src/web/mjs/connectors/Yurineko.mjs index bb669fad30d..9c2d4094d4f 100644 --- a/src/web/mjs/connectors/Yurineko.mjs +++ b/src/web/mjs/connectors/Yurineko.mjs @@ -1,5 +1,5 @@ import Connector from '../engine/Connector.mjs'; -import Manga from '../engine/Manga.mjs'; + export default class Yurineko extends Connector { constructor() { super(); @@ -10,7 +10,6 @@ export default class Yurineko extends Connector { this.api = 'https://api.yurineko.net'; } async _getMangas() { - let mangaList = []; let uri = new URL('/directory/general', this.api); const request = new Request(uri, this.requestOptions); let data = await this.fetchJSON(request); @@ -18,7 +17,7 @@ export default class Yurineko extends Connector { return { id: '/manga/'+element.id, title : element.originalName.trim() - } + }; }); } async _getChapters(manga) { @@ -29,7 +28,7 @@ export default class Yurineko extends Connector { return { id: '/read/'+element.mangaID+'/'+element.id, title : element.name.trim() - } + }; }); } async _getPages(chapter) { @@ -40,7 +39,7 @@ export default class Yurineko extends Connector { return j.props.pageProps.chapterData.url.map( el => { return this.createConnectorURI({ url : el - }) + }); }); } async _handleConnectorURI(payload) { From ae2d72d987c7a7eb44a320ab43eb4742882697fa Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 2 Jan 2023 21:37:47 +0000 Subject: [PATCH 36/47] Update Mav.mjs --- src/web/mjs/videostreams/Mav.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/mjs/videostreams/Mav.mjs b/src/web/mjs/videostreams/Mav.mjs index 11f4bc7a9b2..4a6c64b5caa 100644 --- a/src/web/mjs/videostreams/Mav.mjs +++ b/src/web/mjs/videostreams/Mav.mjs @@ -6,7 +6,7 @@ export default class Mav { this.website = originwebsite; } async getStream() { - let uri = new URL('/api/source/'+this.videoid ,this._uri.origin); + let uri = new URL('/api/source/'+this.videoid, this._uri.origin); let body = { 'r': this.website, 'd': this.hostname, @@ -29,4 +29,4 @@ export default class Mav { type : data.data[0].type }; } -} \ No newline at end of file +} From 97de2fdecf3e79095f340d1d57c8efdb8115aace Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 7 Jan 2023 14:18:50 +0000 Subject: [PATCH 37/47] Add files via upload From 1842856d183c680145cc2da80c79a54afe9c9554 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 7 Jan 2023 14:19:28 +0000 Subject: [PATCH 38/47] Add files via upload --- src/web/img/connectors/mangabuddy | Bin 1098 -> 4303 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/web/img/connectors/mangabuddy b/src/web/img/connectors/mangabuddy index 72bb76c50880a876e4d2da448aa3586170ea826a..5de63b80968aee7b1792e25440c9efaf5021fd85 100644 GIT binary patch literal 4303 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 literal 1098 zcmV-Q1hxB#P)6WV~5P&ZND&mTYtcCCYlo!Qx?8Nj;r{|oRbB7;U zpz5R{tF{}dmQ@s?UCI8e4M^LMs^2qAKnZ&tP1;UWY}=;c zsIt$1x*Bp1Ewjb2>C;x>Hu${rHNs{$vEVapqJG}0?fU@$SSX}MqCK8BLz)0&44(FV zIg@_VkDpLXF@iZjK0E;wT6{^fIanm)8gXl!365~AfpZ8hZ^A00n?_<+y~#WPrp$&h z6A(`$>d|5K1yaFgC@yw6gbCZ#Qsh19R$xdy0eJL1YPoHp>c3X0QE-Ep)IAuJ^~;no2N3Y(zM3mZcEz3g9^wQ+~9)+Q$6Z z;!WqwfFQSaSWV^h3NYZRxi`*StqUovrQG@U)KF|k)X1Pp1l*~*<)9OJ^R0>{!bZadOZ36?U+=*zx9lm5;1B5H-uVi`J&$79wcq2UT( z68s}wAU;^EuAoMd`_YZNP%8Y7jb&ddPRZOyTUUiEr|-d&r05l3*_YaL`ZHYkb;jpb zXZQ0YuPw;$!fw$t0;41EI|%Bv1?KGFJuKTWNB6BkkmUm)QZ!uwe38!h=!k}udUwOL zt|O6$VLC2=9l4FG=5&qs9;iD{G#BaMm>N~Db{jaywz z&bp?zJh_g>vK7`acK!_{ziHx^%hXe(fv!;oD>DUfDl$@%-`C_g@~r0r*TTrbcTN Q&;S4c07*qoM6N<$g1%!J6#xJL From 737248ac249d6236a10017fe23fcb2b4bba5d691 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 7 Jan 2023 14:20:13 +0000 Subject: [PATCH 39/47] Add files via upload --- src/web/mjs/connectors/templates/MadTheme.mjs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/web/mjs/connectors/templates/MadTheme.mjs b/src/web/mjs/connectors/templates/MadTheme.mjs index c93260c1f90..1f77f108050 100644 --- a/src/web/mjs/connectors/templates/MadTheme.mjs +++ b/src/web/mjs/connectors/templates/MadTheme.mjs @@ -4,7 +4,7 @@ export default class MadTheme extends Connector { constructor() { super(); super.id = undefined; - super.label = undefined; + super.label = undefined; this.tags = []; this.url = undefined; this.path = '/az-list'; @@ -40,11 +40,11 @@ export default class MadTheme extends Connector { }); } async _getChapters(manga) { - let mangaid = manga.id.split('/'); - mangaid = '/'+mangaid[mangaid.length - 1];//make sure to take last part of url for the api - let uri = new URL('/api/manga'+mangaid+'/chapters?source=detail', this.url); - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, 'a'); + let mangaid = manga.id.split('/').pop(); + mangaid = '/'+mangaid;//make sure to take last part of url for the api + const uri = new URL('/api/manga'+mangaid+'/chapters?source=detail', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'a'); return data.map(element => { const link = element.pathname; const title = element.querySelector(this.queryChapterTitle).textContent.trim(); @@ -60,14 +60,14 @@ export default class MadTheme extends Connector { resolve(final_images); }); `; - let request = new Request(this.url + chapter.id, this.requestOptions); - let data = await Engine.Request.fetchUI(request, scriptPages); + const request = new Request(this.url + chapter.id, this.requestOptions); + const data = await Engine.Request.fetchUI(request, scriptPages); return data.map(element => this.createConnectorURI(this.getAbsolutePath(element, request.url))); } async _handleConnectorURI(payload) { let request = new Request(payload, this.requestOptions); request.headers.set('x-referer', this.url); - let response = await fetch(request); + const response = await fetch(request); let data = await response.blob(); data = await this._blobToBuffer(data); this._applyRealMime(data); From 6bf28edd5a9888d851ea3b61e6512921fd72ea07 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sat, 7 Jan 2023 14:38:43 +0000 Subject: [PATCH 40/47] Add files via upload --- src/web/mjs/connectors/Manga1001Top.mjs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/web/mjs/connectors/Manga1001Top.mjs b/src/web/mjs/connectors/Manga1001Top.mjs index 3e33876ec8b..738ad34cf36 100644 --- a/src/web/mjs/connectors/Manga1001Top.mjs +++ b/src/web/mjs/connectors/Manga1001Top.mjs @@ -9,4 +9,20 @@ export default class Manga1001Top extends MadTheme { this.tags = [ 'manga', 'webtoon', 'english' ]; this.url = 'https://manga1001.top'; } + + async _getChapters(manga) { + let mangaid = manga.id.split('/').pop(); + mangaid = '/'+mangaid;//make sure to take last part of url for the api + const uri = new URL('/api/manga'+mangaid+'/chapters?source=detail', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'li option'); + return data.map(element => { + const link = element.value; + const title = element.text.trim(); + return { + id: link, + title: title, + }; + }); + } } \ No newline at end of file From 13a1de3a17e33f52f077a2047ae550b62be9249d Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 10 Jan 2023 12:29:23 +0000 Subject: [PATCH 41/47] Add files via upload --- src/web/mjs/connectors/MangaPlanet.mjs | 27 +++++++++++--------------- src/web/mjs/connectors/Mangatales.mjs | 13 +++++-------- src/web/mjs/connectors/Yurineko.mjs | 26 ++++++++++++------------- 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/web/mjs/connectors/MangaPlanet.mjs b/src/web/mjs/connectors/MangaPlanet.mjs index 02ca1c087bc..5a08465cc31 100644 --- a/src/web/mjs/connectors/MangaPlanet.mjs +++ b/src/web/mjs/connectors/MangaPlanet.mjs @@ -1,6 +1,8 @@ import SpeedBinb from './templates/SpeedBinb.mjs'; import Manga from '../engine/Manga.mjs'; +//Copied from Futekiya + export default class MangaPlanet extends SpeedBinb { constructor() { @@ -9,14 +11,15 @@ export default class MangaPlanet extends SpeedBinb { super.label = 'Manga Planet'; this.tags = ['manga', 'english']; this.url = 'https://read.mangaplanet.com'; + this.requestOptions.headers.set('x-referer', this.url + '/'); + this.requestOptions.headers.set('x-cookie', 'faconf=' + 18); + } async _getMangaFromURI(uri) { - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, '.card-body.book-detail h3'); - let id = uri.pathname; - let title = data[0].innerText.trim(); - return new Manga(this, id, title); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, '.card-body.book-detail h3'); + return new Manga(this, uri.pathname, data[0].innerText.trim()); } async _getMangas() { @@ -27,7 +30,7 @@ export default class MangaPlanet extends SpeedBinb { maxPageCount = parseInt(newpagecount[0].innerText.trim()); for (let i = 1; i <= maxPageCount; i++) { request = new Request(new URL('/browse?page=' + i, this.url), this.requestOptions); - const data = await this.fetchDOM(request, ".contents.comics .linkbox"); + const data = await this.fetchDOM(request, '.contents.comics .linkbox'); for (let element of data) { mangas.push({ id: this.getRootRelativeOrAbsoluteLink(element.querySelector('a').pathname, this.url), @@ -37,6 +40,7 @@ export default class MangaPlanet extends SpeedBinb { } return mangas; } + async _getChapters(manga) { const uri = new URL(manga.id, this.url); const request = new Request(uri, this.requestOptions); @@ -64,16 +68,7 @@ export default class MangaPlanet extends SpeedBinb { }); } } - return chapters; } - _getPageList(manga, chapter, callback) { - this.requestOptions.headers.set('x-referer', this.url + '/'); - //add 18 plus cookie otherwise you can get redirected to a different page - this.requestOptions.headers.set('x-cookie', 'faconf=' + 18); - let data = super._getPageList(manga, chapter, callback); - const url = new URL(chapter.id, this.baseURL); - this.requestOptions.headers.set('x-referer', url); - return data; - } + } diff --git a/src/web/mjs/connectors/Mangatales.mjs b/src/web/mjs/connectors/Mangatales.mjs index 50870551324..2df1dabd3fc 100644 --- a/src/web/mjs/connectors/Mangatales.mjs +++ b/src/web/mjs/connectors/Mangatales.mjs @@ -1,6 +1,10 @@ import Connector from '../engine/Connector.mjs'; import Manga from '../engine/Manga.mjs'; + +//very similar to GMANGA + export default class MangaTales extends Connector { + constructor() { super(); super.id = 'mangatales'; @@ -32,10 +36,7 @@ export default class MangaTales extends Connector { let request = new Request(uri, this.requestOptions); let data = await this.fetchDOM(request, 'script[data-component-name="HomeApp"]'); data = JSON.parse(data[0].textContent); - let id = data.mangaDataAction.mangaData.id; - let title = data.mangaDataAction.mangaData.title; - // data.mangaDataAction.mangaData.arabic_title - return new Manga(this, id, title); + return new Manga(this, data.mangaDataAction.mangaData.id, data.mangaDataAction.mangaData.title.trim()); } async _getMangas() { let mangaList = []; @@ -105,10 +106,6 @@ export default class MangaTales extends Connector { }); } async _handleConnectorURI(payload) { - /* - * TODO: only perform requests when from download manager - * or when from browser for preview and selected chapter matches - */ let uri = new URL(payload, this.url); // See: https://gmanga.me/assets/javascripts/main_v38.js let lease = (parseInt(Date.now() / 1000) + 120).toString(36); diff --git a/src/web/mjs/connectors/Yurineko.mjs b/src/web/mjs/connectors/Yurineko.mjs index 9c2d4094d4f..13e92ff291c 100644 --- a/src/web/mjs/connectors/Yurineko.mjs +++ b/src/web/mjs/connectors/Yurineko.mjs @@ -9,10 +9,11 @@ export default class Yurineko extends Connector { this.url = 'https://yurineko.net'; this.api = 'https://api.yurineko.net'; } + async _getMangas() { - let uri = new URL('/directory/general', this.api); + const uri = new URL('/directory/general', this.api); const request = new Request(uri, this.requestOptions); - let data = await this.fetchJSON(request); + const data = await this.fetchJSON(request); return data.map (element => { return { id: '/manga/'+element.id, @@ -20,10 +21,11 @@ export default class Yurineko extends Connector { }; }); } + async _getChapters(manga) { - let uri = new URL(manga.id, this.api); + const uri = new URL(manga.id, this.api); const request = new Request(uri, this.requestOptions); - let data = await this.fetchJSON(request); + const data = await this.fetchJSON(request); return data.chapters.map (element => { return { id: '/read/'+element.mangaID+'/'+element.id, @@ -31,21 +33,19 @@ export default class Yurineko extends Connector { }; }); } + async _getPages(chapter) { - let uri = new URL(chapter.id, this.url); + const uri = new URL(chapter.id, this.url); const request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, '#__NEXT_DATA__'); - let j = JSON.parse(data[0].text); - return j.props.pageProps.chapterData.url.map( el => { - return this.createConnectorURI({ - url : el - }); - }); + const data = await this.fetchDOM(request, '#__NEXT_DATA__'); + const jsonData = JSON.parse(data[0].text); + return jsonData.props.pageProps.chapterData.url.map( element => this.createConnectorURI({url : element})); } + async _handleConnectorURI(payload) { let request = new Request(payload.url, this.requestOptions); request.headers.set('x-referer', this.url); - let response = await fetch(request); + const response = await fetch(request); let data = await response.blob(); data = await this._blobToBuffer(data); this._applyRealMime(data); From 143ca1b733e1158b7a6bb74c770b2847ed79eafd Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 15 Jan 2023 19:24:28 +0000 Subject: [PATCH 42/47] Add files via upload --- src/web/mjs/connectors/Yurineko.mjs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/web/mjs/connectors/Yurineko.mjs b/src/web/mjs/connectors/Yurineko.mjs index 13e92ff291c..9627612f72b 100644 --- a/src/web/mjs/connectors/Yurineko.mjs +++ b/src/web/mjs/connectors/Yurineko.mjs @@ -1,4 +1,5 @@ import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; export default class Yurineko extends Connector { constructor() { @@ -10,6 +11,13 @@ export default class Yurineko extends Connector { this.api = 'https://api.yurineko.net'; } + async _getMangaFromURI(uri) { + const mangaid = uri.href.match(/manga\/([\d]+)/)[1]; + const request = new Request(new URL('/manga/'+mangaid, this.api), this.requestOptions); + const data = await this.fetchJSON(request); + return new Manga(this, uri.pathname, data.originalName.trim()); + } + async _getMangas() { const uri = new URL('/directory/general', this.api); const request = new Request(uri, this.requestOptions); From 4c4362e4f4b4c60c701b2ef6cd004dfb1b15757b Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 15 Jan 2023 19:30:52 +0000 Subject: [PATCH 43/47] Add files via upload --- src/web/mjs/connectors/Ainzscans.mjs | 93 +++++++++++++++++++++++ src/web/mjs/connectors/Allanimesite.mjs | 97 ++++++++++++++++++++++++ src/web/mjs/connectors/Allanimesite2.mjs | 89 ++++++++++++++++++++++ src/web/mjs/connectors/Alphapolis.mjs | 2 +- src/web/mjs/connectors/AniGliScans.mjs | 26 +++++++ src/web/mjs/connectors/AnimeParadise.mjs | 90 ++++++++++++++++++++++ src/web/mjs/connectors/AnimeUnity.mjs | 85 +++++++++++++++++++++ src/web/mjs/connectors/Ayatoon.mjs | 7 +- src/web/mjs/connectors/Baozimh.mjs | 22 +++++- 9 files changed, 505 insertions(+), 6 deletions(-) create mode 100644 src/web/mjs/connectors/Ainzscans.mjs create mode 100644 src/web/mjs/connectors/Allanimesite.mjs create mode 100644 src/web/mjs/connectors/Allanimesite2.mjs create mode 100644 src/web/mjs/connectors/AniGliScans.mjs create mode 100644 src/web/mjs/connectors/AnimeParadise.mjs create mode 100644 src/web/mjs/connectors/AnimeUnity.mjs diff --git a/src/web/mjs/connectors/Ainzscans.mjs b/src/web/mjs/connectors/Ainzscans.mjs new file mode 100644 index 00000000000..63231ee09c2 --- /dev/null +++ b/src/web/mjs/connectors/Ainzscans.mjs @@ -0,0 +1,93 @@ +import Connector from '../engine/Connector.mjs'; + +export default class Ainzscans extends Connector { + + constructor() { + super(); + super.id = 'ainzscans'; + super.label = 'Ainz Scans'; + this.tags = [ 'webtoon', 'indonesian', 'scanlation' ]; + this.url = 'https://www.ainzscans.my.id'; + this.resultsPerPages = 500; + } + + async _getMangas() { + let mangalist = []; + let uri = new URL('/feeds/posts/default/-/Series?alt=json&max-results='+this.resultsPerPages, this.url); + let request = new Request(uri, this.requestOptions); + let data = await this.fetchJSON(request); + const totalentries = parseInt(data.feed.openSearch$totalResults['$t']); + let numpages = Math.floor(totalentries / this.resultsPerPages); + numpages == 0 ? numpages++ : numpages; + for (let page = 0; page < numpages; page++) { + uri = new URL('/feeds/posts/default/-/Series?alt=json&max-results=500&start-index='+ page * this.resultsPerPages+1, this.url); + request = new Request(uri, this.requestOptions); + let data = await this.fetchJSON(request); + + data.feed.entry.forEach(entry => { + mangalist.push( { + id: this.getRootRelativeOrAbsoluteLink( entry.link.find( a => a.rel === 'alternate' ).href, request.url ), + title: entry.title['$t'].trim() + }); + }); + + } + return mangalist; + } + + async _getChapters(manga) { + let chapterlist = []; + const uri = new URL(manga.id, this.url); + const script = ` + new Promise(resolve => { + resolve(label_chapter); + }); + `; + let request = new Request(uri); + const chapterSlang = await Engine.Request.fetchUI(request, script, 5000); + + let apiuri = new URL('/feeds/posts/default/-/'+chapterSlang+'?alt=json&max-results='+this.resultsPerPages, this.url); + request = new Request(apiuri, this.requestOptions); + let feeds = await this.fetchJSON(request); + const totalentries = parseInt(feeds.feed.openSearch$totalResults['$t']); + let numpages = Math.floor(totalentries / this.resultsPerPages); + numpages == 0 ? numpages++ : numpages; + + for (let page = 0; page < numpages; page++) { + apiuri = new URL('/feeds/posts/default/-/'+chapterSlang+'?alt=json&max-results='+this.resultsPerPages+'&start-index='+ page * this.resultsPerPages+1, this.url); + request = new Request(apiuri, this.requestOptions); + let data = await this.fetchJSON(request); + + data.feed.entry.forEach(entry => { + const entrylink = entry.link.find( a => a.rel === 'alternate' ).href; + if (entrylink != uri ) { + chapterlist.push( { + id: entry.id['$t'], + title: entry.title['$t'].trim() + }); + } + + }); + + } + return chapterlist; + } + + async _getPages(chapter) { + let uri = new URL(chapter.manga.id, this.url); + const script = ` + new Promise(resolve => { + resolve(label_chapter); + }); + `; + let request = new Request(uri); + const chapterSlang = await Engine.Request.fetchUI(request, script, 5000); + + uri = new URL('/feeds/posts/default/-/'+chapterSlang+'?alt=json', this.url); + request = new Request(uri, this.requestOptions); + const feeds = await this.fetchJSON(request); + const goodchap = feeds.feed.entry.find( a => a.id['$t'] == chapter.id); + const pictures = this.createDOM(goodchap.content['$t']).querySelectorAll('source'); + return Array.from(pictures).map(entry => this.getRootRelativeOrAbsoluteLink(entry.dataset['src'] || entry.src, this.url)); + } +} diff --git a/src/web/mjs/connectors/Allanimesite.mjs b/src/web/mjs/connectors/Allanimesite.mjs new file mode 100644 index 00000000000..f6e9637d811 --- /dev/null +++ b/src/web/mjs/connectors/Allanimesite.mjs @@ -0,0 +1,97 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class Allanimesite extends Connector { + constructor() { + super(); + super.id = 'allanimesite'; + super.label = 'AllAnime.site (Mangas)'; + this.tags = ['manga', 'webtoon', 'multi-lingual']; + this.url = 'https://allanime.site'; + this.path = '/allanimeapi'; + this.varQueryMangas = '?variables={"search":{"isManga":true,"allowAdult":true,"allowUnknown":true},"limit":100,"page":%PAGE%,"countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"0cf12b2c7e4c571ef8aaae655276b646f485e5022900dd9d721d3bf902d7ef76"}}'; + this.varQueryChapters ='?variables={"_id":"%MANGAID%"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"fbf62e4a2030ecf8bfb9540e0a8a14a300a531cafd82ebb4331e5a3a4a3a4e4e"}}'; + this.queryMangaTitleFromURI = 'ol.breadcrumb li.breadcrumb-item span'; + this.config = { + throttle: { + label: 'Manga list Throttle [ms]', + description: 'Enter the timespan in [ms] to delay consecutive requests to the website api for manga list fetching', + input: 'numeric', + min: 100, + max: 10000, + value: 1000 + } + }; + } + canHandleURI(uri) { + return /(www\.)?allanime\.site\/manga/.test(uri); + } + async _getMangaFromURI(uri) { + const request = new Request(new URL(uri), this.requestOptions); + const data = await this.fetchDOM(request, this.queryMangaTitleFromURI); + const id = uri.pathname.replace(/$\//, ''); + const title = data[0].textContent.trim(); + return new Manga(this, id, title); + } + async _getMangas() { + const mangaList = []; + for(let page = 1, run = true; run; page++) { + const mangas = await this._getMangasFromPage(page); + await this.wait(this.config.throttle.value); + mangas.length ? mangaList.push(...mangas) : run = false; + } + return mangaList; + } + async _getMangasFromPage(page) { + const uri = new URL(this.path + this.varQueryMangas.replace('%PAGE%', page), this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + if (!data.data) return []; + return data.data.mangas.edges.map(element => { + return { + id: '/manga/'+element._id, + title: element.englishName ? element.englishName : element.name, + }; + }); + } + async _getChapters(manga) { + let mangaid = manga.id.replace('/manga/', ''); + let uri = new URL(this.path+ this.varQueryChapters.replace('%MANGAID%', mangaid), this.url); + let request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + let chapterlist = []; + let subchapters = data.data.manga.availableChaptersDetail.sub; + subchapters.forEach(chapter => { + chapterlist.push({ + id : '/read/'+mangaid+'/chapter-'+chapter+'-sub', + title : 'Chapter '+ chapter+' [SUB]', + language : 'SUB', + }); + }); + let rawchapters = data.data.manga.availableChaptersDetail.raw; + rawchapters.forEach(chapter => { + chapterlist.push({ + id : '/read/'+mangaid+'/chapter-'+chapter+'-raw', + title : 'Chapter '+ chapter +' [RAW]', + language : 'RAW', + }); + }); + return chapterlist; + } + async _getPages(chapter) { + const request = new Request(new URL(chapter.id, this.url), this.requestOptions); + const script = ` + new Promise(resolve => { + resolve(__NUXT__); + }); + `; + const data = await Engine.Request.fetchUI(request, script); + const sourcename = data.fetch[0].selectedSourceName; + const sourcesArray = data.fetch[0].chapters; + const goodSource = sourcesArray.find(source => source.sourceName == sourcename); + + const pageslist = goodSource.pictureUrls.map( element => { + return new URL(element.url, goodSource.pictureUrlHead).href; + }); + return pageslist; + } +} diff --git a/src/web/mjs/connectors/Allanimesite2.mjs b/src/web/mjs/connectors/Allanimesite2.mjs new file mode 100644 index 00000000000..fc0f02a2be7 --- /dev/null +++ b/src/web/mjs/connectors/Allanimesite2.mjs @@ -0,0 +1,89 @@ +import Allanimesite from './Allanimesite.mjs'; +export default class Allanimesite2 extends Allanimesite { + constructor() { + super(); + super.id = 'allanimesite2'; + super.label = 'AllAnime.site (Animes)'; + this.tags = ['anime', 'multi-lingual']; + this.url = 'https://allanime.site'; + this.path = '/allanimeapi'; + this.varQueryMangas = '?variables={"search":{"allowAdult":true,"allowUnknown":true},"limit":100,"page":%PAGE%,"countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"9c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6"}}'; + } + get icon() { + return '/img/connectors/allanimesite'; + } + canHandleURI(uri) { + return /(www\.)?allanime\.site\/anime/.test(uri); + } + async _getMangasFromPage(page) { + const uri = new URL(this.path + this.varQueryMangas.replace('%PAGE%', page), this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + if (!data.data) return []; + return data.data.shows.edges.map(element => { + return { + id: '/anime/'+element._id, + title: element.englishName ? element.englishName : element.name, + }; + }); + } + async _getChapters(manga) { + const request = new Request(new URL(manga.id, this.url), this.requestOptions); + let script = ` + new Promise(resolve => { + resolve(__NUXT__); + }); + `; + let data = await Engine.Request.fetchUI(request, script); + let chapterlist = []; + const mangaid = data.fetch[0].show._id; + let subchapters = data.fetch[0].show.availableEpisodesDetail.sub; + subchapters.forEach(chapter => { + chapterlist.push({ + id : '/watch/'+mangaid+'/episode-'+chapter+'-sub', + title : 'Episode '+ chapter+' [SUB]', + language : 'SUB', + }); + }); + let rawchapters = data.fetch[0].show.availableEpisodesDetail.raw; + rawchapters.forEach(chapter => { + chapterlist.push({ + id : '/watch/'+mangaid+'/episode-'+chapter+'-raw', + title : 'Episode '+ chapter+' [RAW]', + language : 'RAW', + }); + }); + let dubchapters = data.fetch[0].show.availableEpisodesDetail.dub; + dubchapters.forEach(chapter => { + chapterlist.push({ + id : '/watch/'+mangaid+'/episode-'+chapter+'-dub', + title : 'Episode '+ chapter+' [DUB]', + language : 'DUB', + }); + }); + return chapterlist; + } + async _getPages(chapter) { + let request = new Request(new URL(chapter.id, this.url), this.requestOptions); + const script = ` + new Promise(resolve => { + resolve(__NUXT__); + }); + `; + let data = await Engine.Request.fetchUI(request, script); + const sourcesArray = data.fetch[0].episodeSelections; + const goodSource = sourcesArray.find(source => source.sourceName == 'Default'); + if (!goodSource) throw new Error('No Default source found !'); + let uri = new URL(goodSource.sourceUrl.replace('clock', 'clock.json'), 'https://blog.allanime.pro'); + request = new Request(uri, this.requestOptions); + data = await this.fetchJSON(request); + let stream = []; + let link = data.links[0]; + if (link.hls) { + stream = { mirrors: [ link.link ], subtitles: [], referer : 'https://blog.allanime.pro'}; + } else { + stream = {video: [ link.link ], subtitles: []}; + } + return stream; + } +} diff --git a/src/web/mjs/connectors/Alphapolis.mjs b/src/web/mjs/connectors/Alphapolis.mjs index 609cc4ed30e..286a10ad3a9 100644 --- a/src/web/mjs/connectors/Alphapolis.mjs +++ b/src/web/mjs/connectors/Alphapolis.mjs @@ -47,7 +47,7 @@ export default class Alphapolis extends Connector { const data = await this.fetchDOM(request, 'viewer-manga-horizontal'); try { const pages = JSON.parse(data[0].getAttribute('v-bind:pages')); - return pages.filter(element => typeof element != 'object' && !element.match('white_page')); + return pages.filter(element => typeof element != 'object' && !element.match('white_page')).map(element => element.replace(/\/[0-9]+x[0-9]+.([\w]+)/, '/1080x1536.$1')); } catch (error) { throw new Error(`The chapter '${chapter.title}' is neither public, nor purchased!`); } diff --git a/src/web/mjs/connectors/AniGliScans.mjs b/src/web/mjs/connectors/AniGliScans.mjs new file mode 100644 index 00000000000..a805b52ba3f --- /dev/null +++ b/src/web/mjs/connectors/AniGliScans.mjs @@ -0,0 +1,26 @@ +import WordPressMangastream from './templates/WordPressMangastream.mjs'; + +export default class AniGliScans extends WordPressMangastream { + + constructor() { + super(); + super.id = 'anigliscans'; + super.label = 'Animated Glitched Scans'; + this.tags = [ 'manga', 'english', 'scanlation' ]; + this.url = 'https://anigliscans.com'; + this.path = '/series/?list'; + } + async _getPages(chapter) { + const data = await super._getPages(chapter); + return data.map(element => this.createConnectorURI(element)); + } + async _handleConnectorURI(payload) { + const request = new Request(payload, this.requestOptions); + request.headers.set('x-referer', this.url); + let response = await fetch(request); + let data = await response.blob(); + data = await this._blobToBuffer(data); + this._applyRealMime(data); + return data; + } +} diff --git a/src/web/mjs/connectors/AnimeParadise.mjs b/src/web/mjs/connectors/AnimeParadise.mjs new file mode 100644 index 00000000000..e467406f4b9 --- /dev/null +++ b/src/web/mjs/connectors/AnimeParadise.mjs @@ -0,0 +1,90 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class AnimeParadise extends Connector { + constructor() { + super(); + super.id = 'animeparadise'; + super.label = 'Anime Paradise'; + this.tags = [ 'anime', 'subbed', 'multi-lingual' ]; + this.url = 'https://www.animeparadise.moe'; + this.api = 'https://api.animeparadise.moe'; + this.config = { + resolution: { + label: 'Preferred Resolution', + description: 'Try to download video in the selected resolution.\nIf the resolution is not supported, depending on the mirror the download may fail, or a fallback resolution may be used!', + input: 'select', + options: [ + { value: '', name: 'Mirror\'s Default' }, + { value: '360p', name: '360p' }, + { value: '480p', name: '480p' }, + { value: '720p', name: '720p' }, + { value: '1080p', name: '1080p' } + ], + value: '' + } + }; + } + async _getMangaFromURI(uri) { + let request = new Request(uri, this.requestOptions); + let data = await this._getNextData(request); + let title = data.props.pageProps.data.title.trim(); + let id = data.props.pageProps.data.link; + return new Manga(this, id, title); + } + async _getMangas() { + //api and search are limited to 35 results no matter what i do, and they are not the same. Better not using them :/ + let msg = 'This website does not provide a manga list, please copy and paste the URL containing the chapters directly from your browser into HakuNeko.'; + throw new Error(msg); + } + async _getChapters(manga) { + //first get anime id + let uri = new URL('/anime/'+manga.id, this.url); + let request = new Request(uri, this.requestOptions); + let data = await this._getNextData(request); + const mangaid = data.props.pageProps.data._id; + //api request for episodes + uri = new URL('/anime/'+mangaid+'/episode', this.api); + request = new Request(uri, this.requestOptions); + request.headers.set('x-origin', this.url); + request.headers.set('x-referer', this.url); + data = await this.fetchJSON(request); + // + return data.data.map(episode =>{ + return { + id : '/watch/'+episode.uid+'?origin='+episode.origin, + title : 'Episode '+ episode.number + }; + }).reverse(); + } + async _getPages(chapter) { + let uri = new URL(chapter.id, this.url); + let request = new Request(uri, this.requestOptions); + let data = await this._getNextData(request); + const subtitles = data.props.pageProps.subtitles.map(sub =>{ + return { + url : sub.src, + locale : sub.srclang + }; + }); + const epnumber = data.props.pageProps.episode.number; + const drive = data.props.pageProps.animeData.drive; + const animename = data.props.pageProps.animeData.title; + //buid api request to get links + uri = new URL('/storage/'+animename+'/'+epnumber+'?&folderId='+ drive, this.api); + request = new Request(uri, this.requestOptions); + request.headers.set('x-origin', this.url); + request.headers.set('x-referer', this.url); + data = await this.fetchJSON(request); + let streams = data.directUrl; + let resolution = parseInt(this.config.resolution.value || 0); + let stream = streams.find(stream => stream.resolution >= resolution) || streams.shift(); + return { + video: new URL(stream.src, this.api).href, + subtitles: subtitles + }; + } + async _getNextData(request) { + const [data] = await this.fetchDOM(request, '#__NEXT_DATA__'); + return JSON.parse(data.textContent); + } +} diff --git a/src/web/mjs/connectors/AnimeUnity.mjs b/src/web/mjs/connectors/AnimeUnity.mjs new file mode 100644 index 00000000000..d8bda5cda01 --- /dev/null +++ b/src/web/mjs/connectors/AnimeUnity.mjs @@ -0,0 +1,85 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class AnimeUnity extends Connector { + constructor() { + super(); + super.id = 'animeunity'; + super.label = 'AnimeUnity'; + this.tags = [ 'anime', 'italian' ]; + this.url = 'https://www.animeunity.tv'; + } + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + //strip episode part of url (website tends to add it) + uri = new URL(uri.href.split('/').slice(0, 5).join("/")); + const data = await this.fetchDOM(request, 'div.general h1.title'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); + } + async _getMangas() { + let mangaList = []; + const token = await this.getToken(); + for (let page = 0, run = true; run; page++) { + const mangas = await this._getMangasFromPage(page, token); + mangas.length > 0 ? mangaList.push(...mangas) : run = false; + } + return mangaList; + } + async _getMangasFromPage(page, token) { + const uri = new URL('/archivio/get-animes', this.url); + const body = { + 'title':false, + 'type':false, + 'year':false, + 'order':false, + 'status':false, + 'genres':false, + 'offset':page * 30, + 'dubbed':false, + 'season':false + }; + const request = new Request(uri, { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'accept': 'application/json, text/plain, */*', + 'content-type': 'application/json, text/plain, */*', + 'x-referer': this.url+ '/archivio', + 'x-origin': this.url, + 'X-Requested-With': 'XMLHttpRequest', + 'X-CSRF-TOKEN': token, + } + }); + const response = await fetch(request); + const data = await response.json(); + return data.records.map(element => { + return { + id: '/anime/'+element.id+'-'+element.slug, + title: element.title_eng.trim() + }; + }); + } + async _getChapters(manga) { + const uri = new URL(manga.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'video-player'); + const episodes = JSON.parse(data[0].getAttribute('episodes')); + return episodes.map(element => { + return { + id: manga.id +'/'+ element.id, + title: 'Episode '+ element.number, + }; + }).reverse(); + } + async _getPages(chapter) { + const uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'video-player'); + const episode = JSON.parse(data[0].getAttribute('episode')); + return { video : episode.link }; + } + async getToken() { + const request = new Request(this.url, this.requestOptions); + const data = await this.fetchDOM(request, 'meta[name=csrf-token]'); + return data[0].content; + } +} diff --git a/src/web/mjs/connectors/Ayatoon.mjs b/src/web/mjs/connectors/Ayatoon.mjs index 1aba800c940..c4131a158d0 100644 --- a/src/web/mjs/connectors/Ayatoon.mjs +++ b/src/web/mjs/connectors/Ayatoon.mjs @@ -1,6 +1,6 @@ -import WordPressMadara from './templates/WordPressMadara.mjs'; +import WordPressMangastream from './templates/WordPressMangastream.mjs'; -export default class Ayatoon extends WordPressMadara { +export default class Ayatoon extends WordPressMangastream { constructor() { super(); @@ -8,5 +8,6 @@ export default class Ayatoon extends WordPressMadara { super.label = 'AYATOON'; this.tags = [ 'webtoon', 'turkish' ]; this.url = 'https://ayatoon.com'; + this.path = '/manga/list-mode'; } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/Baozimh.mjs b/src/web/mjs/connectors/Baozimh.mjs index 0f1e8cf4f44..b12c9021d58 100644 --- a/src/web/mjs/connectors/Baozimh.mjs +++ b/src/web/mjs/connectors/Baozimh.mjs @@ -27,7 +27,10 @@ export default class Baozimh extends Connector { async _getChapters(manga) { let request = new Request(new URL(manga.id, this.url), this.requestOptions); //where can it find the chapters - const data = await this.fetchDOM(request, ".l-box #chapter-items > div > a, .l-box #chapters_other_list > div > a"); + let data = await this.fetchDOM(request, ".l-box #chapter-items > div > a, .l-box #chapters_other_list > div > a"); + if (data.length == 0) { + data = await this.fetchDOM(request, ".l-box .comics-chapters > a.comics-chapters__item"); + } let chapters = data.reverse(); //get first chapters on top return chapters.map((element) => { return { @@ -58,6 +61,21 @@ export default class Baozimh extends Connector { pagesList.push( ...data.map(element => element.getAttribute('src')).filter(page => page) ); run = uri != null; } - return pagesList; + pagesList = pagesList.filter((page, index) => { + return index === pagesList.findIndex(item => item === page); + }); + + return pagesList.map(page => this.createConnectorURI(page)); + + } + + async _handleConnectorURI(payload) { + const request = new Request(payload, this.requestOptions); + request.headers.set('x-referer', this.url); + const response = await fetch(request); + let data = await response.blob(); + data = await this._blobToBuffer(data); + this._applyRealMime(data); + return data; } } From f1345c78400669e27be7a58fd08174b8218ecee9 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 15 Jan 2023 19:39:03 +0000 Subject: [PATCH 44/47] Add files via upload --- src/web/mjs/connectors/CosmicScans.mjs | 13 +++ src/web/mjs/connectors/Delitoon.mjs | 118 +++++++++++++-------- src/web/mjs/connectors/DelitoonDE.mjs | 15 +-- src/web/mjs/connectors/Delitoonx.mjs | 19 ++++ src/web/mjs/connectors/Desu.mjs | 74 ++++++++++--- src/web/mjs/connectors/DigitalMargaret.mjs | 43 ++++++++ src/web/mjs/connectors/Dm5.mjs | 63 +++-------- src/web/mjs/connectors/DoujinDesu.mjs | 46 +++++++- src/web/mjs/connectors/DuniaKomik.mjs | 13 +++ src/web/mjs/connectors/EarlyManga.mjs | 66 ++++++------ src/web/mjs/connectors/FRScans.mjs | 16 ++- src/web/mjs/connectors/Fbsquads.mjs | 11 ++ src/web/mjs/connectors/FizManga.mjs | 10 ++ src/web/mjs/connectors/FuryoSquad.mjs | 27 ++--- 14 files changed, 358 insertions(+), 176 deletions(-) create mode 100644 src/web/mjs/connectors/CosmicScans.mjs create mode 100644 src/web/mjs/connectors/Delitoonx.mjs create mode 100644 src/web/mjs/connectors/DigitalMargaret.mjs create mode 100644 src/web/mjs/connectors/DuniaKomik.mjs create mode 100644 src/web/mjs/connectors/Fbsquads.mjs create mode 100644 src/web/mjs/connectors/FizManga.mjs diff --git a/src/web/mjs/connectors/CosmicScans.mjs b/src/web/mjs/connectors/CosmicScans.mjs new file mode 100644 index 00000000000..1be137ab104 --- /dev/null +++ b/src/web/mjs/connectors/CosmicScans.mjs @@ -0,0 +1,13 @@ +import WordPressMangastream from './templates/WordPressMangastream.mjs'; + +export default class CosmicScans extends WordPressMangastream { + + constructor() { + super(); + super.id = 'cosmicscans'; + super.label = 'Cosmic Scans'; + this.tags = [ 'manga', 'english', 'webtoon', 'scanlation' ]; + this.url = 'https://cosmicscans.com'; + this.path = '/manga/list-mode'; + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/Delitoon.mjs b/src/web/mjs/connectors/Delitoon.mjs index 20421f21a5d..b19ebb88663 100644 --- a/src/web/mjs/connectors/Delitoon.mjs +++ b/src/web/mjs/connectors/Delitoon.mjs @@ -2,7 +2,6 @@ import Connector from '../engine/Connector.mjs'; import Manga from '../engine/Manga.mjs'; export default class Delitoon extends Connector { - constructor() { super(); super.id = 'delitoon'; @@ -10,57 +9,86 @@ export default class Delitoon extends Connector { this.tags = [ 'webtoon', 'french' ]; this.url = 'https://www.delitoon.com'; this.links = { - login: 'https://www.delitoon.com/connexion' + login: 'https://www.delitoon.com/user/login' }; + this.requestOptions.headers.set('x-balcony-id', 'DELITOON_COM'); + this.requestOptions.headers.set('x-balcony-timeZone', 'Europe/Paris'); + this.requestOptions.headers.set('x-platform', 'WEB'); + this.requestOptions.headers.set('x-referer', this.url); } - async _getMangaFromURI(uri) { - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, 'div.serie-page div.container div.informations-part1.detail div div.bloc-left h2'); - return new Manga(this, uri.pathname, data[0].textContent.trim()); + await this.getToken(); + const mangaid = uri.href.match(/\/detail\/(\S+)/)[1]; + const req = new URL('/api/balcony-api-v2/contents/'+mangaid, this.url); + req.searchParams.set('isNotLoginAdult', 'true'); + const request = new Request(req, this.requestOptions); + const data = await this.fetchJSON(request); + return new Manga(this, mangaid, data.data.title.trim()); } - async _getMangas() { - let mangaList = []; - let genres = ['Romance', 'BL', 'Drama', 'Sentimental', 'Historique', 'Slice%20of%20Life', 'Fantastique', 'Comédie', 'Thriller', 'Action', 'Aventure', 'SF']; - for(let genre of genres) { - let mangas = await this._getMangasFromPage(genre); - mangaList.push(...mangas); - } - return mangaList; - } - - async _getMangasFromPage(page) { - let request = new Request(this.url + '/genres/' + page, this.requestOptions); - let data = await this.fetchDOM(request, 'div.genres-page div.content ul li'); - return data.filter(element => element.querySelector('h3')) - .map(element => { - let title = element.querySelector('div.container-infos h3').textContent.trim(); - let link = element.querySelector('li a'); - return { - id: this.getRootRelativeOrAbsoluteLink(link, request.url), - title: title - }; - }); + await this.getToken(); + const uri = new URL('/api/balcony-api-v2/contents/search', this.url); + uri.searchParams.set('searchText', ''); + uri.searchParams.set('isCheckDevice', 'true'); + uri.searchParams.set('isIncludeAdult', 'true'); + uri.searchParams.set('contentsThumbnailType', 'MAIN'); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + return data.data.map(element => { + return { + id : element.alias, + title : element.title.trim() + }; + }); } - async _getChapters(manga) { - let request = new Request(this.url + manga.id, this.requestOptions); - let data = await this.fetchDOM(request, 'div.container div.episodes div.title ul#tab1 li'); - return data.filter(element => element.querySelector('div.number')) - .map(element => { - let title = element.querySelector('div.container-infos div.bloc-infos div.number').textContent.trim(); - let link = element.querySelector('li a'); - return { - id: this.getRootRelativeOrAbsoluteLink(link, request.url), - title: 'Episode ' + title - }; - }).reverse(); + await this.getToken(); + const uri = new URL('/api/balcony-api-v2/contents/'+manga.id, this.url); + uri.searchParams.set('isNotLoginAdult', 'true'); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + return data.data.episodes.map(element => { + let title = ''; + try{ + let chapnum = parseInt(element.title); + !isNaN(chapnum) ? title = 'Chapter '+ chapnum : title = element.title.trim(); + } catch (error) { + title = element.title.trim(); + } + title += element.subTitle ? ' : ' + element.subTitle.trim() : ''; + return { + id : element.alias, + title : title, + }; + }).reverse(); } - async _getPages(chapter) { - let request = new Request(this.url + chapter.id, this.requestOptions); - let data = await this.fetchDOM(request, 'div.episode div#bck-color div.main div#image-container source'); - return data.map(element => this.getAbsolutePath(element, request.url)); + await this.getToken(); + const uri = new URL('/api/balcony-api-v2/contents/'+chapter.manga.id+'/'+chapter.id, this.url); + uri.searchParams.set('isNotLoginAdult', 'true'); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + if (data.result == 'ERROR') { + switch (data.error.code) { + case 'NOT_LOGIN_USER': + throw new Error('You must be logged to view this chapter !'); + case 'UNAUTHORIZED_CONTENTS': + throw new Error('You must unlock this chapter first !'); + default: + throw new Error('Unknown error : '+ data.error.code); + } + } + return data.data.images.map(element => this.createConnectorURI(element.imagePath)); + } + + async getToken() { + const uri = new URL('/api/auth/session', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchJSON(request); + if(data.user) { + this.requestOptions.headers.set('authorization', ' Bearer '+ data.user.accessToken.token); + } else { + this.requestOptions.headers.delete('authorization'); + } } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/DelitoonDE.mjs b/src/web/mjs/connectors/DelitoonDE.mjs index e1aff14cb64..f0623dc0ab8 100644 --- a/src/web/mjs/connectors/DelitoonDE.mjs +++ b/src/web/mjs/connectors/DelitoonDE.mjs @@ -9,18 +9,9 @@ export default class DelitoonDE extends Delitoon { this.tags = [ 'webtoon', 'german' ]; this.url = 'https://www.delitoon.de'; this.links = { - login: 'https://www.delitoon.de/connexion' + login: 'https://www.delitoon.de/user/login' }; + this.requestOptions.headers.set('x-balcony-id', 'DELITOON_DE'); + this.requestOptions.headers.set('x-referer', this.url); } - - async _getMangas() { - let mangaList = []; - let genres = ['Romance', 'Boys%20Love', 'Drama', 'Sentimental', 'Historisch', 'Slice%20of%20Life', 'Fantasy', 'Komödie', 'Thriller', 'Action', 'Abenteuer', 'Sci-Fi']; - for(let genre of genres) { - let mangas = await this._getMangasFromPage(genre); - mangaList.push(...mangas); - } - return mangaList; - } - } \ No newline at end of file diff --git a/src/web/mjs/connectors/Delitoonx.mjs b/src/web/mjs/connectors/Delitoonx.mjs new file mode 100644 index 00000000000..33cdce888be --- /dev/null +++ b/src/web/mjs/connectors/Delitoonx.mjs @@ -0,0 +1,19 @@ +import Delitoon from './Delitoon.mjs'; + +export default class Delitoonx extends Delitoon { + constructor() { + super(); + super.id = 'delitoonx'; + super.label = 'Delitoon X'; + this.tags = [ 'webtoon', 'french', 'hentai' ]; + this.url = 'https://www.delitoonx.com'; + this.links = { + login: 'https://www.delitoonx.com/user/login' + }; + this.requestOptions.headers.set('x-balcony-id', 'DELITOONX_COM'); + this.requestOptions.headers.set('x-referer', this.url); + } + get icon() { + return '/img/connectors/delitoon'; + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/Desu.mjs b/src/web/mjs/connectors/Desu.mjs index 9e03694ee11..48326dcb80a 100644 --- a/src/web/mjs/connectors/Desu.mjs +++ b/src/web/mjs/connectors/Desu.mjs @@ -1,28 +1,74 @@ import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; -/** - * - */ export default class Desu extends Connector { - /** - * - */ constructor() { super(); super.id = 'desu'; super.label = 'Desu'; - this.tags = []; + this.tags = ['manga', 'webtoon', 'russian']; this.url = 'https://desu.me'; } - _getMangaList( callback ) { - callback( new Error( 'Please report this broken website on HakuNeko\'s GitHub project page.' ), undefined ); + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.titleBar h1 span.name'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); } - _getChapterList( manga, callback ) { - callback( new Error( 'Please report this broken website on HakuNeko\'s GitHub project page.' ), undefined ); + + async _getMangas() { + let mangaList = []; + const uri = new URL('/manga/', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.PageNav'); + const pageCount = parseInt(data[0].dataset['last']); + for(let page = 1; page <= pageCount; page++) { + const mangas = await this._getMangasFromPage(page); + mangaList.push(...mangas); + } + return mangaList; + } + + async _getMangasFromPage(page) { + const uri = new URL('/manga/?page=' + page, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'h3 a.animeTitle.oTitle'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.title.trim() + }; + }); } - _getPageList( manga, chapter, callback ) { - callback( new Error( 'Please report this broken website on HakuNeko\'s GitHub project page.' ), undefined ); + + async _getChapters( manga ) { + const uri = new URL(manga.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div#animeView div.expandable ul.chlist li h4 a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.text.trim() + }; + }); + } + + async _getPages( chapter ) { + let script = ` + new Promise((resolve, reject) => { + setTimeout(() => { + try { + resolve(Reader.images); + } catch(error) { + reject(error); + } + }, 500); + }); + `; + const uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + const pages = await Engine.Request.fetchUI(request, script); + return pages.map(element => this.getAbsolutePath(element.url, this.url)); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/DigitalMargaret.mjs b/src/web/mjs/connectors/DigitalMargaret.mjs new file mode 100644 index 00000000000..12db09d1fc7 --- /dev/null +++ b/src/web/mjs/connectors/DigitalMargaret.mjs @@ -0,0 +1,43 @@ +import SpeedBinb from './templates/SpeedBinb.mjs'; +import Manga from '../engine/Manga.mjs'; + +export default class DigitalMargaret extends SpeedBinb { + + constructor() { + super(); + super.id = 'digitalmargaret'; + super.label = 'デジタルマーガレット (Digital Margaret)'; + this.tags = ['manga', 'japanese']; + this.url = 'https://digitalmargaret.jp'; + } + + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const [data] = await this.fetchDOM(request, 'section#product div.content h3'); + const title = data.textContent.trim(); + const id = uri.pathname; + return new Manga(this, id, title); + } + + async _getMangas() { + const request = new Request(new URL('/', this.url), this.requestOptions); + const data = await this.fetchDOM(request, 'section#serial ul.serial-list li a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.querySelector('source').getAttribute('alt') + }; + }); + } + + async _getChapters(manga) { + const request = new Request(new URL(manga.id, this.url), this.requestOptions); + const data = await this.fetchDOM(request, 'section#product div.list div.box div.number'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element.querySelector('a'), this.url) + '/', + title: element.querySelector('p').textContent.trim(), + }; + }); + } +} diff --git a/src/web/mjs/connectors/Dm5.mjs b/src/web/mjs/connectors/Dm5.mjs index 04e7045005f..f783c519a1c 100644 --- a/src/web/mjs/connectors/Dm5.mjs +++ b/src/web/mjs/connectors/Dm5.mjs @@ -57,54 +57,6 @@ export default class Dm5 extends Connector { return this._getPagesMobile(chapter); } - async _getPagesDesktop(chapter) { - const script = ` - new Promise((resolve, reject) => { - setTimeout(() => { - try { - let lastPage = 1; - lastPage = parseInt([...document.querySelectorAll('div#chapterpager.chapterpager a')].pop().text.trim()); - let pageList = []; - let ajaxResult = null; - - for(let dmpage = 1, run = true; dmpage <= lastPage && run; dmpage++) { - if (ajaxResult != null) { - ajaxResult.abort(); - ajaxResult = null; - } - - let mkey = ''; - let data; - if ($("#dm5_key").length > 0) { - mkey = $("#dm5_key").val(); - } - ajaxResult = $.ajax({ - url: 'chapterfun.ashx', - async: false, - data: { cid: DM5_CID, page: dmpage, key: mkey, language: 1, gtk: 6, _cid: DM5_CID, _mid: DM5_MID, _dt: DM5_VIEWSIGN_DT, _sign: DM5_VIEWSIGN }, - type: 'GET', - error: function (msg) { - reject(msg); - }, - success: function (msg) { - data = eval(msg); - } - }); - data.length ? pageList.push(...data) : run = false; - } - resolve(pageList); - } catch(error) { - reject(error); - } - }, 1000); - }); - `; - const uri = new URL(chapter.id, this.url); - const request = new Request(uri, this.requestOptions); - const data = await Engine.Request.fetchUI(request, script); - return data.filter((item, index) => data.indexOf(item) === index).map(element => this.getAbsolutePath(element, request.url)); - } - async _getPagesMobile(chapter) { const script = ` new Promise((resolve, reject) => { @@ -121,7 +73,17 @@ export default class Dm5 extends Connector { uri.hostname = uri.hostname.replace('www', 'm'); const request = new Request(uri, this.requestOptions); const data = await Engine.Request.fetchUI(request, script); - return data.filter((item, index) => data.indexOf(item) === index).map(element => this.getAbsolutePath(element, request.url)); + return data.filter((item, index) => data.indexOf(item) === index).map(element => this.createConnectorURI(this.getAbsolutePath(element, request.url))); + } + + async _handleConnectorURI(payload) { + let request = new Request(payload, this.requestOptions); + request.headers.set('x-referer', this.url); + let response = await fetch(request); + let data = await response.blob(); + data = await this._blobToBuffer(data); + this._applyRealMime(data); + return data; } async _getMangaFromURI(uri) { @@ -131,5 +93,4 @@ export default class Dm5 extends Connector { const title = data[0].textContent.trim(); return new Manga(this, id, title); } - -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/DoujinDesu.mjs b/src/web/mjs/connectors/DoujinDesu.mjs index b531844b214..1496fcffbcd 100644 --- a/src/web/mjs/connectors/DoujinDesu.mjs +++ b/src/web/mjs/connectors/DoujinDesu.mjs @@ -7,14 +7,14 @@ export default class DoujinDesu extends WordPressMangastream { super.id = 'doujindesu'; super.label = 'DoujinDesu'; this.tags = ['hentai', 'indonesian']; - this.url = 'https://doujindesu.xxx'; + this.url = 'https://212.32.226.234'; this.path = '/manga/page/%PAGE%/'; - this.querMangaTitleFromURI = 'main#archive div.wrapper h1.title'; - this.queryMangas = '#main .entries .entry > a'; + this.queryMangas = 'div.entries article.entry a'; this.queryChapters = 'div#chapter_list div.epsleft span.lchx a'; + this.queryPages = 'div.main div img[src]:not([src=""])'; this.queryChaptersTitle = undefined; - this.queryPages = 'main#reader div.main img[src]:not([src=""])'; + this.querMangaTitleFromURI = 'section.metadata h1.title'; } canHandleURI(uri) { @@ -42,4 +42,40 @@ export default class DoujinDesu extends WordPressMangastream { }; }); } -} \ No newline at end of file + + async _getPages(chapter) { + const script = ` + new Promise((resolve, reject) => { + if(window.ts_reader && ts_reader.params.sources) { + resolve(ts_reader.params.sources.shift().images); + } else { + setTimeout(() => { + try { + const images = [...document.querySelectorAll('${this.queryPages}')]; + resolve(images.map(image => image.dataset['lazySrc'] || image.dataset['src'] || image.getAttribute('original') || image.src)); + } catch(error) { + reject(error); + } + }, 2500); + } + }); + `; + const uri = new URL(chapter.id, this.url); + let request = new Request(uri, this.requestOptions); + let data = await Engine.Request.fetchUI(request, script); + // HACK: bypass 'i0.wp.com' image CDN to ensure original images are loaded directly from host + return data.map(link => { + return this.createConnectorURI(this.getAbsolutePath(link, request.url).replace(/\/i\d+\.wp\.com/, '')); + }).filter(link => !link.includes('histats.com')); + } + + async _handleConnectorURI(payload) { + let request = new Request(payload, this.requestOptions); + request.headers.set('x-referer', this.url); + let response = await fetch(request); + let data = await response.blob(); + data = await this._blobToBuffer(data); + this._applyRealMime(data); + return data; + } +} diff --git a/src/web/mjs/connectors/DuniaKomik.mjs b/src/web/mjs/connectors/DuniaKomik.mjs new file mode 100644 index 00000000000..67dcecbbd0c --- /dev/null +++ b/src/web/mjs/connectors/DuniaKomik.mjs @@ -0,0 +1,13 @@ +import WordPressMangastream from './templates/WordPressMangastream.mjs'; + +export default class DuniaKomik extends WordPressMangastream { + + constructor() { + super(); + super.id = 'duniakomik'; + super.label = 'Dunia Komik'; + this.tags = [ 'manga', 'indonesian' ]; + this.url = 'https://duniakomik.id'; + this.path = '/manga/list-mode/'; + } +} diff --git a/src/web/mjs/connectors/EarlyManga.mjs b/src/web/mjs/connectors/EarlyManga.mjs index 7b3606a0cbe..9c743e303dc 100644 --- a/src/web/mjs/connectors/EarlyManga.mjs +++ b/src/web/mjs/connectors/EarlyManga.mjs @@ -1,24 +1,13 @@ import Connector from '../engine/Connector.mjs'; import Manga from '../engine/Manga.mjs'; - export default class EarlyManga extends Connector { - constructor() { super(); super.id = 'earlymanga'; super.label = 'EarlyManga'; this.tags = [ 'webtoon', 'english' ]; - this.url = 'https://earlymanga.org'; + this.url = 'https://earlym.org'; } - - async _getMangaFromURI(uri) { - const request = new Request(new URL(uri), this.requestOptions); - const data = await this.fetchDOM(request, 'head title'); - const id = uri.pathname; - const title = data[0].text.trim(); - return new Manga(this, id, title); - } - async _getMangas() { let mangaList = []; for (let page = 0, run = true; run; page++) { @@ -27,38 +16,53 @@ export default class EarlyManga extends Connector { } return mangaList; } - async _getMangasFromPage(page) { - const uri = new URL(`/lazymore/manga-${ 40 * page }`, this.url); + const uri = new URL('/api/search/advanced?page='+page, this.url); const request = new Request(uri, this.requestOptions); let data = await this.fetchJSON(request); - if(typeof data === 'string') { - data = JSON.parse(data); - } - return !Array.isArray(data) ? [] : data.map(item => { + return data.data.map(item => { return { - id: '/manga/' + item.manga_slug.trim(), - title: item.manga_title.trim() + id: '/manga/' + item.slug.trim(), + title: item.title.trim() }; }); } - async _getChapters(manga) { - const uri = new URL(manga.id, this.url); + let chapterList = []; + const mangaid = manga.id.match(/\/manga\/(\S+)/)[1]; + for(let page = 1, run = true; run; page++) { + let chapters = await this._getChaptersFromPages(page, mangaid); + chapters.length ? chapterList.push(...chapters) : run = false; + } + return chapterList; + } + async _getChaptersFromPages(page, mangaid) { + const uri = new URL('/api/manga/'+mangaid+'/chapterlist?page='+page, this.url); const request = new Request(uri, this.requestOptions); - const data = await this.fetchDOM(request, 'div:not(.d-none) > div.chapter-row > div.order-lg-2 > a:not([style])'); - return data.map(element => { + let data = await this.fetchJSON(request); + return data.data.map(item => { return { - id: this.getRootRelativeOrAbsoluteLink(element, this.url), - title: element.querySelector('div:not([style])').textContent.trim() + id: '/manga/' + mangaid + '/chapter-'+item.slug, + title: 'Chapter '+item.chapter_number }; }); } - async _getPages(chapter) { - const uri = new URL(chapter.id, this.url); + const uri = new URL('/api'+chapter.id, this.url); const request = new Request(uri, this.requestOptions); - const data = await this.fetchDOM(request, 'div.chapter-images-container-up source:not(.d-none)'); - return data.map(image => this.getAbsolutePath(image, request.url)); + let data = await this.fetchJSON(request); + let manga_id = data.chapter.manga_id; + return data.chapter.images.map(item => { + return this.url+'/storage/uploads/manga/manga_'+manga_id+'/chapter_'+data.chapter.slug+'/'+item; + }); + } + async _getMangaFromURI(uri) { + const mangaid = uri.href.match(/\/manga\/(\S+)/)[1]; + const apicallurl = new URL('/api/manga/'+mangaid, this.url); + const request = new Request(apicallurl, this.requestOptions); + let data = await this.fetchJSON(request); + const id = uri.pathname; + const title = data.main_manga.title.trim(); + return new Manga(this, id, title); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/FRScans.mjs b/src/web/mjs/connectors/FRScans.mjs index 6379d4039e1..136fd7e173f 100644 --- a/src/web/mjs/connectors/FRScans.mjs +++ b/src/web/mjs/connectors/FRScans.mjs @@ -7,8 +7,22 @@ export default class FRScan extends MangaReaderCMS { super.id = 'frscan'; super.label = 'Frscan'; this.tags = [ 'manga', 'webtoon', 'french' ]; - this.url = 'https://www.frscan.cc'; + this.url = 'https://www.frscan.ws'; this.language = 'fr'; } + + async _getPages(chapter) { + return (await super._getPages(chapter)).map(element => this.createConnectorURI({url: element, referer : this.url})); + } + + async _handleConnectorURI(payload) { + let request = new Request(payload.url, this.requestOptions); + request.headers.set('x-referer', payload.referer); + let response = await fetch(request); + let data = await response.blob(); + data = await this._blobToBuffer(data); + this._applyRealMime(data); + return data; + } } diff --git a/src/web/mjs/connectors/Fbsquads.mjs b/src/web/mjs/connectors/Fbsquads.mjs new file mode 100644 index 00000000000..ac71a9042aa --- /dev/null +++ b/src/web/mjs/connectors/Fbsquads.mjs @@ -0,0 +1,11 @@ +import WordPressMadara from './templates/WordPressMadara.mjs'; + +export default class Fbsquads extends WordPressMadara { + constructor() { + super(); + super.id = 'fbsquads'; + super.label = 'Fleur Blanche Squads'; + this.tags = [ 'webtoon', 'spanish', 'scanlation', 'yaoi' ]; + this.url = 'https://fbsquads.com'; + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/FizManga.mjs b/src/web/mjs/connectors/FizManga.mjs new file mode 100644 index 00000000000..d18f5109f55 --- /dev/null +++ b/src/web/mjs/connectors/FizManga.mjs @@ -0,0 +1,10 @@ +import WordPressMadara from './templates/WordPressMadara.mjs'; +export default class FizManga extends WordPressMadara { + constructor() { + super(); + super.id = 'fizmanga'; + super.label = 'FizManga'; + this.tags = [ 'manga', 'webtoon', 'english' ]; + this.url = 'https://fizmanga.com'; + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/FuryoSquad.mjs b/src/web/mjs/connectors/FuryoSquad.mjs index 6c1ec576fa1..58b5af37d0e 100644 --- a/src/web/mjs/connectors/FuryoSquad.mjs +++ b/src/web/mjs/connectors/FuryoSquad.mjs @@ -7,25 +7,18 @@ export default class FuryoSquad extends Connector { super.id = 'furyosquad'; super.label = 'Furyo Squad'; this.tags = [ 'manga', 'french', 'high-quality' ]; - this.url = 'http://www.furyosquad.com'; + this.url = 'https://www.furyosquad.com'; } async _getMangas() { - const categories = ['/', '/en-cours', '/termines', '/stoppes']; - - let mangas = []; - for ( const category of categories) { - let request = new Request(new URL(category, this.url), this.requestOptions); - let data = await this.fetchDOM(request, 'div.fs-chap-container div.grid-item-container div.media-body a'); - mangas.push( ...data.map(manga => { - return { - id: this.getRootRelativeOrAbsoluteLink(manga, this.url), - title: manga.title.trim() - }; - })); - } - - return mangas; + const request = new Request(new URL('/mangas', this.url), this.requestOptions); + const data = await this.fetchDOM(request, 'div.fs-chap-container div.grid-item-container div.media-body a'); + return data.map(manga => { + return { + id: this.getRootRelativeOrAbsoluteLink(manga, this.url), + title: manga.title.trim() + }; + }); } async _getChapters(manga) { @@ -59,4 +52,4 @@ export default class FuryoSquad extends Connector { data = this.createDOM(await data.text()); return data.querySelectorAll(selector); } -} \ No newline at end of file +} From 38fbd9a8cdc3a876085d5e5f2dd7b7dcf61d34d8 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 15 Jan 2023 19:40:48 +0000 Subject: [PATCH 45/47] Add files via upload --- src/web/mjs/connectors/BilibiliComics.mjs | 3 +- src/web/mjs/connectors/BilibiliComicsES.mjs | 16 ++ src/web/mjs/connectors/BilibiliComicsFR.mjs | 16 ++ src/web/mjs/connectors/BilibiliComicsID.mjs | 16 ++ src/web/mjs/connectors/BilibiliManhua.mjs | 3 + src/web/mjs/connectors/Bontoon.mjs | 19 ++ src/web/mjs/connectors/Boosei.mjs | 4 +- src/web/mjs/connectors/BoysLove.mjs | 1 + src/web/mjs/connectors/CocoManHua.mjs | 37 +-- src/web/mjs/connectors/ComicBushi.mjs | 26 ++ src/web/mjs/connectors/ComicExtra.mjs | 4 +- src/web/mjs/connectors/ComicFuz.mjs | 117 +++++++++ src/web/mjs/connectors/ComicGrast.mjs | 269 ++++++++++++++++++++ src/web/mjs/connectors/ComicK.mjs | 6 +- src/web/mjs/connectors/Comico.mjs | 100 +++++--- src/web/mjs/connectors/GammaPlus.mjs | 2 +- 16 files changed, 555 insertions(+), 84 deletions(-) create mode 100644 src/web/mjs/connectors/BilibiliComicsES.mjs create mode 100644 src/web/mjs/connectors/BilibiliComicsFR.mjs create mode 100644 src/web/mjs/connectors/BilibiliComicsID.mjs create mode 100644 src/web/mjs/connectors/Bontoon.mjs create mode 100644 src/web/mjs/connectors/ComicBushi.mjs create mode 100644 src/web/mjs/connectors/ComicFuz.mjs create mode 100644 src/web/mjs/connectors/ComicGrast.mjs diff --git a/src/web/mjs/connectors/BilibiliComics.mjs b/src/web/mjs/connectors/BilibiliComics.mjs index 0fa0d5498ca..d739b189fb2 100644 --- a/src/web/mjs/connectors/BilibiliComics.mjs +++ b/src/web/mjs/connectors/BilibiliComics.mjs @@ -5,8 +5,9 @@ export default class BilibiliComics extends BilibiliManhua { constructor() { super(); super.id = 'bilibili-comics'; - super.label = 'Bilibili Comics'; + super.label = 'Bilibili Comics (English)'; this.tags = [ 'webtoon', 'english' ]; this.url = 'https://www.bilibilicomics.com'; + this.lang = 'en'; } } \ No newline at end of file diff --git a/src/web/mjs/connectors/BilibiliComicsES.mjs b/src/web/mjs/connectors/BilibiliComicsES.mjs new file mode 100644 index 00000000000..04691a4ad43 --- /dev/null +++ b/src/web/mjs/connectors/BilibiliComicsES.mjs @@ -0,0 +1,16 @@ +import BilibiliManhua from './BilibiliManhua.mjs'; + +export default class BilibiliComicsES extends BilibiliManhua { + + constructor() { + super(); + super.id = 'bilibili-comics-es'; + super.label = 'Bilibili Comics (Spanish)'; + this.tags = [ 'webtoon', 'spanish' ]; + this.url = 'https://www.bilibilicomics.com'; + this.lang = 'es'; + } + get icon() { + return '/img/connectors/bilibili-comics'; + } +} diff --git a/src/web/mjs/connectors/BilibiliComicsFR.mjs b/src/web/mjs/connectors/BilibiliComicsFR.mjs new file mode 100644 index 00000000000..394f48b9b24 --- /dev/null +++ b/src/web/mjs/connectors/BilibiliComicsFR.mjs @@ -0,0 +1,16 @@ +import BilibiliManhua from './BilibiliManhua.mjs'; + +export default class BilibiliComicsFR extends BilibiliManhua { + + constructor() { + super(); + super.id = 'bilibili-comics-fr'; + super.label = 'Bilibili Comics (French)'; + this.tags = [ 'webtoon', 'french' ]; + this.url = 'https://www.bilibilicomics.com'; + this.lang = 'fr'; + } + get icon() { + return '/img/connectors/bilibili-comics'; + } +} diff --git a/src/web/mjs/connectors/BilibiliComicsID.mjs b/src/web/mjs/connectors/BilibiliComicsID.mjs new file mode 100644 index 00000000000..f008f8de8ea --- /dev/null +++ b/src/web/mjs/connectors/BilibiliComicsID.mjs @@ -0,0 +1,16 @@ +import BilibiliManhua from './BilibiliManhua.mjs'; + +export default class BilibiliComicsID extends BilibiliManhua { + + constructor() { + super(); + super.id = 'bilibili-comics-id'; + super.label = 'Bilibili Comics (Indonesian)'; + this.tags = [ 'webtoon', 'indonesian' ]; + this.url = 'https://www.bilibilicomics.com'; + this.lang = 'id'; + } + get icon() { + return '/img/connectors/bilibili-comics'; + } +} diff --git a/src/web/mjs/connectors/BilibiliManhua.mjs b/src/web/mjs/connectors/BilibiliManhua.mjs index 685e8f78995..d2cc6f77634 100644 --- a/src/web/mjs/connectors/BilibiliManhua.mjs +++ b/src/web/mjs/connectors/BilibiliManhua.mjs @@ -9,6 +9,7 @@ export default class BilibiliManhua extends Connector { super.label = '哔哩哔哩 漫画 (Bilibili Manhua)'; this.tags = [ 'manga', 'webtoon', 'chinese' ]; this.url = 'https://manga.bilibili.com'; + this.lang = 'cn'; this.config = { quality: { @@ -29,6 +30,8 @@ export default class BilibiliManhua extends Connector { const uri = new URL('/twirp/comic.v1.Comic' + path, this.url); uri.searchParams.set('device', 'pc'); uri.searchParams.set('platform', 'web'); + uri.searchParams.set('lang', this.lang); + uri.searchParams.set('sys_lang', this.lang); const request = new Request(uri, { method: 'POST', body: JSON.stringify(body), diff --git a/src/web/mjs/connectors/Bontoon.mjs b/src/web/mjs/connectors/Bontoon.mjs new file mode 100644 index 00000000000..b8d69d1cbde --- /dev/null +++ b/src/web/mjs/connectors/Bontoon.mjs @@ -0,0 +1,19 @@ +import Delitoon from './Delitoon.mjs'; + +export default class Bontoon extends Delitoon { + constructor() { + super(); + super.id = 'bontoon'; + super.label = 'Bontoon'; + this.tags = [ 'webtoon', 'french', 'hentai', 'yaoi' ]; + this.url = 'https://www.bontoon.com'; + this.links = { + login: 'https://www.bontoon.com/user/login' + }; + this.requestOptions.headers.set('x-balcony-id', 'BONTOON_COM'); + this.requestOptions.headers.set('x-referer', this.url); + } + get icon() { + return '/img/connectors/delitoon'; + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/Boosei.mjs b/src/web/mjs/connectors/Boosei.mjs index c9c31add24e..b926be5d628 100644 --- a/src/web/mjs/connectors/Boosei.mjs +++ b/src/web/mjs/connectors/Boosei.mjs @@ -7,7 +7,7 @@ export default class Boosei extends WordPressMangastream { super.id = 'boosei'; super.label = 'Boosei'; this.tags = [ 'webtoon', 'indonesian' ]; - this.url = 'https://boosei.com'; + this.url = 'https://boosei.net'; this.path = '/manga/list-mode/'; } @@ -22,4 +22,4 @@ export default class Boosei extends WordPressMangastream { let data = await Engine.Request.fetchUI(request, script); return data.map(link => this.getAbsolutePath(link, request.url)); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/BoysLove.mjs b/src/web/mjs/connectors/BoysLove.mjs index 8f2b75d3356..d674125d98a 100644 --- a/src/web/mjs/connectors/BoysLove.mjs +++ b/src/web/mjs/connectors/BoysLove.mjs @@ -8,5 +8,6 @@ export default class BoysLove extends WordPressMadara { super.label = 'Boys Love'; this.tags = [ 'manga', 'webtoon', 'hentai', 'english' ]; this.url = 'https://boyslove.me'; + this.requestOptions.headers.set('x-referer', this.url); } } \ No newline at end of file diff --git a/src/web/mjs/connectors/CocoManHua.mjs b/src/web/mjs/connectors/CocoManHua.mjs index 2d3179932ec..dfb7ff8a6c3 100644 --- a/src/web/mjs/connectors/CocoManHua.mjs +++ b/src/web/mjs/connectors/CocoManHua.mjs @@ -1,29 +1,19 @@ -import ZYMK from './templates/ZYMK.mjs'; +import Mangadig from './MangaDig.mjs'; -export default class CocoManHua extends ZYMK { +export default class CocoManHua extends Mangadig { constructor() { super(); super.id = 'cocomanhua'; super.label = 'Coco漫画'; this.tags = [ 'webtoon', 'chinese' ]; - this.url = 'https://www.cocomanga.com'; - + this.url = 'https://www.colamanhua.com'; this.path = '/show?page='; this.pathSuffix = ''; this.queryMangaTitle = 'dl.fed-deta-info dd.fed-deta-content h1.fed-part-eone'; this.queryMangasPageCount = 'div.fed-page-info a.fed-show-sm-inline'; this.queryMangas = 'ul.fed-list-info li.fed-list-item a.fed-list-title'; this.queryChapters = 'div.all_data_list ul li a'; - this.queryPage = ` - new Promise(resolve => { - // from manga.read.js __cr.showPic(...) - const script = 'dlSxHGdzZuE8IyExLyP0MucxL4M4ZmIzZ4gmN1ZhciB1aFSzVFhkQmVuayzmT0ZFd0nvVTVkQFQXTWwnMkQERX9uQkZ5TUcwclIpUmVXQl7vVm9RTVnxNDMuQ4wPZSUwMVUxeDSQWS9HYlwaaFMVVuBWVE9oTVQNU0YvdSQNa0jxZF0Sd0SqaEZUVz9zU0cwdV9qQkVTQUZhTkMzQFIoPjkiVTS3VzdWd4IqcSdjbXQMYjQQMSYxbE0iLj2xYUdRLkVSeShVVjIyT0QnU4HxNWhhbSH4WVhRWWLxLUZiaj3yY4rZME3pbEZZbj3KZSdqVkkWWj3uRS9oUkZRT0IFcSkXaygvUTMaclQrLWSLLuSPUTItekSpMX9WWE3pVDSWT0EyRj8UQVYyVUQJVFMrNVkNLlM2ZUV1eFS3NW8NVkk5T0QWUlIWRj8XQXM5YyH0QkIURTBKLU30U4rMekPvZSQZa0I9TVZZLWQETuBXQEk5ZW9FbU0XUkQULW81YuMRRVIERkBWLSYxYVhvUz3YWmVWelgxVlj3MlVraSBZelQOZEQNbk9pY1dWLzZYYyMvTWHvLU3aalhpYWrqUlIHPTIMVkZSUTMaQE3XNDQVa4g4YW3NTSQHbyQhbSZ3VWwjTkVHcGZQbmAxUzVqTj0FZGkXWEZFYuSjQE8URl0LLuVZWjdWTj8UaDQuQj31Y43RRSdSdErXbjZrYuBvbkZqaEQhVlqvVG9WQEzwTkhWQmBrVF0FWlLvbGdaajI3UjdjbE3FQmhRVmBtVWrvR0SXayBVLXBGT0haaFQsayVhV09VVW9ILlSHPXhiVjS3Wl0aTWSEQjkuLjZIY0hNRlMYVXIiL485WVZMd0QUPTIiVlh3T0hJTU3HaGVhayVzWlraMWSGQXhXVFQrTjU0elISNTVMVjYxVzdRclHyaDSkVU2zVl9Jdj0XdGkMaz30TjZNYVjxcFSNL4PwUzZjeVYxbFrVLj9RVkdjbSnwQlwWVlQ0VUcwd0ExLGkVWFh4ZUQCb4PwcS9uWEYvTUdjVWIoUmVNVE9HUyBJWE3XUlhUbE32ZWqwMkdETTQTQyPvUTAwclIqcErjVXhJYuBqeS9oQjdMbE9HYkQRPlIUQj0iVXQTVXkzeFVWbEIZLDVrVEhjQlVXeDQQVFr5VEVQLkdFWuSMVEIXYWj3Qj3sTl0jbXBYWlrjWkVFPjrLekn0RyIFbFHxb19TVkIsVuIteSSXQmIhL47wZSVFRlVqbFriREnwUWwRRj3FPuZiWSIVVSV2aVdXbShQVTVKZUVnTz3VNVhQWSS1U0QWR0ZGLU8hbUk5ZUhaeWVSMW9LelQ5VEdRTU8HbSIPVDA8Iyr4YXHgd1QlLu0fX4MjZWMxeXBzJGdzZuEqHEMxeXBzbz9TKlVsYx3CYXMkMuPscFSxc4Und4hhdSQnZUZ0Y4q9KmQtU1QxaW3mJEMxeXBzbz9TKlVsYx3VdFY2JRj6ZXZhbCh1dFYxJTq='; - const totalimg = mh_info.enc_code1 ? parseInt(eval(base64.decode(script))) : mh_info.totalimg; - resolve(new Array(totalimg).fill().map((_, index) => new URL(__cr.getPicUrl(index + 1), window.location.origin).href)); - }); - `; - this.config.throttle = { label: 'Throttle Requests [ms]', description: 'Enter the timespan in [ms] to delay consecuitive HTTP requests.\nThe website may block images for to many consecuitive requests.', @@ -34,23 +24,4 @@ export default class CocoManHua extends ZYMK { }; } - async _getPages(chapter) { - const images = await super._getPages(chapter); - return images.map(image => { - return this.createConnectorURI({ - url: image, - referer: new URL(chapter.id, this.url).href - }); - }); - } - - async _handleConnectorURI(payload) { - const request = new Request(payload.url, this.requestOptions); - request.headers.set('x-referer', payload.referer); - const response = await fetch(request); - let data = await response.blob(); - data = await this._blobToBuffer(data); - this._applyRealMime(data); - return data; - } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/ComicBushi.mjs b/src/web/mjs/connectors/ComicBushi.mjs new file mode 100644 index 00000000000..69e8bd39c36 --- /dev/null +++ b/src/web/mjs/connectors/ComicBushi.mjs @@ -0,0 +1,26 @@ +import CoreView from './templates/CoreView.mjs'; + +export default class ComicBushi extends CoreView { + + constructor() { + super(); + super.id = 'comicbushi'; + super.label = 'コミックブシロードWEB (Comic Bushiroad WEB)'; + this.tags = ['manga', 'japanese']; + this.url = 'https://comicbushi-web.com'; + this.path = ['/']; + this.queryManga = 'section#lineup ul.lineup-list div.lineup-item a'; + this.queryMangaTitle = 'h5.title'; + } + + async _getMangasFromPage(page) { + const request = new Request(this.url + page, this.requestOptions); + const data = await this.fetchDOM(request, this.queryManga); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, request.url), + title: element.querySelector(this.queryMangaTitle).textContent.replace(/^完結 /, '').trim() + }; + }); + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/ComicExtra.mjs b/src/web/mjs/connectors/ComicExtra.mjs index 146d31525d6..b39ab44f3fb 100644 --- a/src/web/mjs/connectors/ComicExtra.mjs +++ b/src/web/mjs/connectors/ComicExtra.mjs @@ -8,7 +8,7 @@ export default class ComicExtra extends Connector { super.id = 'comicextra'; super.label = 'ComicExtra'; this.tags = ['comic', 'english']; - this.url = 'https://www.comicextra.com'; + this.url = 'https://ww1.comicextra.com'; this.path = '/comic-list/'; } @@ -80,4 +80,4 @@ export default class ComicExtra extends Connector { const data = await this.fetchDOM(request, '.chapter_img'); return data.map(element => this.getAbsolutePath(element, request.url)); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/ComicFuz.mjs b/src/web/mjs/connectors/ComicFuz.mjs new file mode 100644 index 00000000000..7ceadb7cf98 --- /dev/null +++ b/src/web/mjs/connectors/ComicFuz.mjs @@ -0,0 +1,117 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; + +export default class ComicFuz extends Connector { + + constructor() { + super(); + super.id = 'comicfuz'; + super.label = 'COMIC FUZ'; + this.tags = ['manga', 'japanese']; + this.url = 'https://comic-fuz.com'; + this.apiUrl = 'https://api.comic-fuz.com'; + this.imgUrl = 'https://img.comic-fuz.com'; + this.protoTypes = '/mjs/connectors/ComicFuz.proto'; + } + + async _getMangaFromURI(uri) { + const id = uri.pathname.split('/').pop(); + const data = await this._fetchMangaDetail(id); + return new Manga(this, id, data.manga.title); + } + + async _getMangas() { + const uri = new URL('v1/mangas_by_day_of_week', this.apiUrl); + const requestType = 'ComicFuz.MangasByDayOfWeekRequest'; + const responseType = 'ComicFuz.MangasByDayOfWeekResponse'; + const payload = { + deviceInfo: { + deviceType: 2 + }, + dayOfWeek: 0 + }; + const request = await this._createPROTORequest(uri, requestType, payload); + const data = await this.fetchPROTO(request, this.protoTypes, responseType); + return data.mangas; + } + + async _getChapters(manga) { + const data = await this._fetchMangaDetail(manga.id); + const chapters = []; + data.chapters.forEach(chapterGroup => { + chapters.push(...chapterGroup.chapters); + }); + return chapters; + } + + async _getPages(chapter) { + const uri = new URL('v1/manga_viewer', this.apiUrl); + const requestType = 'ComicFuz.MangaViewerRequest'; + const responseType = 'ComicFuz.MangaViewerResponse'; + const payload = { + deviceInfo: { + deviceType: 2 + }, + chapterId: chapter.id, + useTicket: false, + consumePoint: { + event: 0, + paid: 0 + } + }; + const request = await this._createPROTORequest(uri, requestType, payload); + let data; + try { + data = await this.fetchPROTO(request, this.protoTypes, responseType); + } catch (error) { + throw new Error(`The chapter '${chapter.title}' is neither public, nor purchased!`); + } + return data.pages + .filter(page => page.image && page.image.imageUrl) + .map(page => this.createConnectorURI(page.image)); + } + + async _handleConnectorURI(payload) { + const uri = new URL(payload.imageUrl, this.imgUrl); + const request = new Request(uri, this.requestOptions); + const response = await fetch(request); + const buffer = await response.arrayBuffer(); + if (payload.encryptionKey) { + const ivArray = new Uint8Array(payload.iv.match(/.{1,2}/g).map(e => parseInt(e, 16))); + const keyArray = new Uint8Array(payload.encryptionKey.match(/.{1,2}/g).map(e => parseInt(e, 16))); + const cryptoKey = await crypto.subtle.importKey('raw', keyArray, 'AES-CBC', false, ['decrypt']); + const data = await crypto.subtle.decrypt({ + name: 'AES-CBC', + iv: ivArray + }, cryptoKey, buffer); + return Buffer.from(data); + } else { + return Buffer.from(buffer); + } + } + + async _createPROTORequest(uri, rootType, payload) { + const root = await protobuf.load(this.protoTypes); + const messageType = root.lookupType(rootType); + const message = messageType.encode(payload); + return new Request(uri, { + ...this.requestOptions, + body: message.finish(), + method: 'POST' + }); + } + + async _fetchMangaDetail(id) { + const uri = new URL('v1/manga_detail', this.apiUrl); + const requestType = 'ComicFuz.MangaDetailRequest'; + const responseType = 'ComicFuz.MangaDetailResponse'; + const payload = { + deviceInfo: { + deviceType: 2, + }, + mangaId: id + }; + const request = await this._createPROTORequest(uri, requestType, payload); + return this.fetchPROTO(request, this.protoTypes, responseType); + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/ComicGrast.mjs b/src/web/mjs/connectors/ComicGrast.mjs new file mode 100644 index 00000000000..e0b04cf245f --- /dev/null +++ b/src/web/mjs/connectors/ComicGrast.mjs @@ -0,0 +1,269 @@ +import Manga from '../engine/Manga.mjs'; +import Connector from '../engine/Connector.mjs'; + +export default class ComicGrast extends Connector { + + constructor() { + super(); + super.id = 'comicgrast'; + super.label = 'comic グラスト (Comic Grast)'; + this.tags = ['manga', 'japanese']; + this.url = 'https://novema.jp'; + } + + async _getMangaFromURI(uri) { + const id = uri.pathname; + const request = new Request(uri, this.requestOptions); + const [data] = await this.fetchDOM(request, 'dt.comicTit'); + const title = data.textContent.trim(); + return new Manga(this, id, title); + } + + async _getMangas() { + const request = new Request(new URL('/comic/serial', this.url), this.requestOptions); + const data = await this.fetchDOM(request, 'ul.comicList a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.querySelector('dt p.line-clamp').textContent.trim() + }; + }); + } + + async _getChapters(manga) { + const request = new Request(new URL(manga.id, this.url), this.requestOptions); + const data = await this.fetchDOM(request, 'div.comicSerialList article a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.querySelector('span.storyTitle').textContent.trim() + }; + }); + } + + async _getPages(chapter) { + const comicData = await this._fetchComicData(chapter); + const comicContent = await this._fetchComicContent(comicData); + return comicContent.map(content => this.createConnectorURI({ + data: comicData, + content, + })); + } + + async _handleConnectorURI(payload) { + const uri = new URL(`/img/serial-comic/${payload.data.serial_comic_id}/${payload.data.story_number}/content/${payload.content.name}?t=${payload.data.story_updated_at}`, this.url); + const image = await this._loadImage(uri.href); + const size = Math.max(image.width, image.height) / payload.content.size; + const canvas = this._unscramble(image, size, payload.content.seed); + const blob = await this._canvasToBlob(canvas); + return this._blobToBuffer(blob); + } + + async _fetchComicData(chapter) { + const request = new Request(new URL(chapter.id, this.url), this.requestOptions); + const [data] = await this.fetchDOM(request, '#comic-data'); + return JSON.parse(data.textContent); + } + + async _fetchComicContent(comicData) { + const uri = new URL(`/img/serial-comic/${comicData.serial_comic_id}/${comicData.story_number}/content/index.json?t=${comicData.story_updated_at}`, this.url); + const request = new Request(uri, this.requestOptions); + return this.fetchJSON(request); + } + + async _canvasToBlob(canvas) { + return new Promise(resolve => { + canvas.toBlob(data => { + resolve(data); + }, Engine.Settings.recompressionFormat.value, parseFloat(Engine.Settings.recompressionQuality.value) / 100); + }); + } + + _loadImage(url) { + return new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => resolve(image); + image.onerror = error => reject(error); + image.src = url; + }); + } + _unscramble(image, sliceSize, seed) { + return unscrambleImg(image, sliceSize, seed); + } +} + +// from https://github.com/webcaetano/image-scramble/blob/master/unscrambleImg.js +function unscrambleImg(img, sliceSize, seed) { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext('2d'); + canvas.width = img.width; + canvas.height = img.height; + const totalParts = Math.ceil(img.width / sliceSize) * Math.ceil(img.height / sliceSize); + const inds = []; + for (let i = 0; i < totalParts; i++) { + inds.push(i); + } + + const slices = getSlices(img, sliceSize); + for (const g in slices) { + const group = getGroup(slices[g]); + let shuffleInd = []; + for (let i = 0; i < slices[g].length; i++) { + shuffleInd.push(i); + } + shuffleInd = _shuffleSeed_(shuffleInd, seed); + for (let i = 0; i < slices[g].length; i++) { + const s = shuffleInd[i]; + const row = Math.floor(s / group.cols); + const col = s - row * group.cols; + const x = col * slices[g][i].width; + const y = row * slices[g][i].height; + ctx.drawImage( + img, + group.x + x, + group.y + y, + slices[g][i].width, + slices[g][i].height, + slices[g][i].x, + slices[g][i].y, + slices[g][i].width, + slices[g][i].height + ); + } + } + return canvas; +} + +function getGroup(slices) { + const self = {}; + self.slices = slices.length; + self.cols = getColsInGroup(slices); + self.rows = slices.length / self.cols; + self.width = slices[0].width * self.cols; + self.height = slices[0].height * self.rows; + self.x = slices[0].x; + self.y = slices[0].y; + return self; +} + +function getSlices(img, sliceSize) { + const totalParts = Math.ceil(img.width / sliceSize) * Math.ceil(img.height / sliceSize); + const verticalSlices = Math.ceil(img.width / sliceSize); + const slices = {}; + for (let i = 0; i < totalParts; i++) { + const slice = {}; + const row = Math.floor(i / verticalSlices); + const col = i - row * verticalSlices; + slice.x = col * sliceSize; + slice.y = row * sliceSize; + slice.width = sliceSize - (slice.x + sliceSize <= img.width ? 0 : slice.x + sliceSize - img.width); + slice.height = sliceSize - (slice.y + sliceSize <= img.height ? 0 : slice.y + sliceSize - img.height); + const key = `${slice.width}-${slice.height}`; + if (slices[key] == null) { + slices[key] = []; + } + slices[key].push(slice); + } + return slices; +} + +function getColsInGroup(slices) { + if (slices.length == 1) { + return 1; + } + let t = 'init'; + for (let i = 0; i < slices.length; i++) { + if (t == 'init') { + t = slices[i].y; + } + if (t != slices[i].y) { + return i; + } + } + return slices.length; +} + +// from https://github.com/webcaetano/shuffle-seed +function _shuffleSeed_(arr, seed) { + const size = arr.length; + const rng = _seedrandom_(seed); + const resp = []; + const keys = []; + for (let i = 0; i < size; i++) keys.push(i); + for (let i = 0; i < size; i++) { + const r = Math.floor(rng() * keys.length); + const g = keys[r]; + keys.splice(r, 1); + resp.push(arr[g]); + } + return resp; +} + +// from https://github.com/davidbau/seedrandom +var width = 256, + chunks = 6, + digits = 52, + startdenom = Math.pow(width, chunks), + significance = Math.pow(2, digits), + overflow = significance * 2, + mask = width - 1; + +function _seedrandom_(seed) { + var key = []; + mixkey(seed, key); + var arc4 = new ARC4(key); + var prng = function () { + var n = arc4.g(chunks), + d = startdenom, + x = 0; + while (n < significance) { + n = (n + x) * width; + d *= width; + x = arc4.g(1); + } + while (n >= overflow) { + n /= 2; + d /= 2; + x >>>= 1; + } + return (n + x) / d; + }; + return prng; +} + +function ARC4(key) { + var t, keylen = key.length, + me = this, i = 0, j = me.i = me.j = 0, s = me.S = []; + + if (!keylen) { + key = [keylen++]; + } + + while (i < width) { + s[i] = i++; + } + for (i = 0; i < width; i++) { + s[i] = s[j = mask & j + key[i % keylen] + (t = s[i])]; + s[j] = t; + } + + (me.g = function (count) { + var t, r = 0, + i = me.i, j = me.j, s = me.S; + while (count--) { + t = s[i = mask & i + 1]; + r = r * width + s[mask & (s[i] = s[j = mask & j + t]) + (s[j] = t)]; + } + me.i = i; me.j = j; + return r; + })(width); +} + +function mixkey(seed, key) { + var stringseed = seed + '', smear, j = 0; + while (j < stringseed.length) { + key[mask & j] = + mask & (smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++); + } + return String.fromCharCode.apply(0, key); +} \ No newline at end of file diff --git a/src/web/mjs/connectors/ComicK.mjs b/src/web/mjs/connectors/ComicK.mjs index 954557747e8..c46ff383de4 100644 --- a/src/web/mjs/connectors/ComicK.mjs +++ b/src/web/mjs/connectors/ComicK.mjs @@ -8,8 +8,8 @@ export default class ComicK extends Connector { super.id = 'comick'; super.label = 'ComicK'; this.tags = [ 'manga', 'english' ]; - this.url = 'https://comick.fun'; - this.apiurl = 'https://api.comick.fun'; + this.url = 'https://comick.app'; + this.apiurl = 'https://api.comick.app'; } async _getEmbeddedJSON(uri) { @@ -104,4 +104,4 @@ export default class ComicK extends Connector { } } } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/Comico.mjs b/src/web/mjs/connectors/Comico.mjs index 09ffb827845..07fbfa34474 100644 --- a/src/web/mjs/connectors/Comico.mjs +++ b/src/web/mjs/connectors/Comico.mjs @@ -9,25 +9,33 @@ export default class Comico extends Connector { super.label = 'Comico (コミコ)'; this.tags = [ 'webtoon', 'japanese' ]; this.url = 'https://www.comico.jp'; - - this.categories = [ - 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', 'finish', - null // magic key for challenge endpoint - ]; - } - - _getTitleNumber(href) { - let uri = new URL(href, this.url); - return uri.searchParams.get('titleNo'); + this.api = 'https://api.comico.jp'; + this.links = { + login: 'https://www.comico.jp/login' + }; } - async _fetchPOST(path, parameters) { - let uri = new URL(path, this.url); + async _fetchPOST(path) { + const webkey = '9241d2f090d01716feac20ae08ba791a'; + const ip = '0.0.0.0'; + const tm = Math.round(new Date().getTime() / 1000); + const checksum = CryptoJS.SHA256(webkey + ip + tm).toString(); + let uri = new URL(path, this.api); let request = new Request(uri, { - method: 'POST', - body: new URLSearchParams(parameters).toString(), + method: 'GET', headers: { - 'content-type': 'application/x-www-form-urlencoded' + 'x-referer': this.url, + 'x-origin': this.url, + 'accept' : 'application/json, text/plain, */*', + 'Accept-Language': 'ja-JP', + 'X-comico-client-os': 'other', + 'X-comico-client-store': 'other', + 'X-comico-request-time': tm, + 'X-comico-check-sum': checksum, + 'X-comico-timezone-id': 'Europe/Paris', + 'X-comico-client-immutable-uid': '0.0.0.0', + 'X-comico-client-platform': 'web', + 'X-comico-client-accept-mature': 'Y', } }); let response = await fetch(request); @@ -35,54 +43,62 @@ export default class Comico extends Connector { } async _getMangaFromURI(uri) { - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, 'h1.article-hero09__ttl'); - let id = this._getTitleNumber(uri.href); - let title = data[0].textContent.trim(); + let id = uri.pathname; + const data = await this._fetchPOST(id); + let title = data.data.volume.content != null ? data.data.volume.content.name : data.data.episode.content.name; return new Manga(this, id, title); } async _getMangas() { let mangaList = []; - for(let category of this.categories) { - for(let page = 1, run = true; run; page++) { - let mangas = await this._getMangasFromPage(category, page); - mangas.length > 0 ? mangaList.push(...mangas) : run = false; - } + for(let page = 1, run = true; run; page++) { + let mangas = await this._getMangasFromPage(page); + mangas.length > 0 ? mangaList.push(...mangas) : run = false; } return mangaList; } - async _getMangasFromPage(day, page) { - let endpoint = day ? '/official' : '/challenge'; - let data = await this._fetchPOST(endpoint + '/updateList.nhn', { day, page }); - return !data.result ? [] : data.result.list.map(manga => { + async _getMangasFromPage(page) { + let data = await this._fetchPOST('/all_comic/new_release?pageNo='+page +'&pageSize=50'); + return !data.result ? [] : data.data.contents.map(manga => { return { - id: this._getTitleNumber(manga.title_url), - title: manga.title_name + id: '/'+manga.type+'/'+manga.id, + title: manga.name }; }); } async _getChapters(manga) { - let data = await this._fetchPOST('/api/getArticleList.nhn', { titleNo: manga.id }); - return data.result.list - .filter(chapter => chapter.isPurchased || chapter.freeFlg === 'Y') + const data = await this._fetchPOST(manga.id); + //episode or volume? + const element = data.data.episode.content != null ? data.data.episode.content : data.data.volume.content; + return element.chapters + .filter(chapter => chapter.activity.rented || chapter.activity.unlocked || chapter.salesConfig.free) .map(chapter => { return { - id: chapter.articleNo, - title: chapter.subtitle, + id: chapter.id, + title: chapter.name, language: '' }; }); } async _getPages(chapter) { - let uri = new URL('/detail.nhn', this.url); - uri.searchParams.set('titleNo', chapter.manga.id); - uri.searchParams.set('articleNo', chapter.id); - let request = new Request(uri, this.requestOptions); - let data = await this.fetchDOM(request, 'div.comic-image source.comic-image__image'); - return data.map(image => this.getAbsolutePath(image, request.url)); + const data = await this._fetchPOST(chapter.manga.id+'/chapter/' +chapter.id+'/product'); + if (data.data.content.chapterFileFormat == 'epub') { + throw Error('This chapter is an Epub :/'); + } + return data.data.chapter.images.map(image => this.cookPictureUrl(image)); + } + + cookPictureUrl(image) { + const AESKey = 'a7fc9dc89f2c873d79397f8a0028a4cd'; + const iv = CryptoJS.enc.Utf8.parse(CryptoJS.enc.Hex.parse('')); + const passPhrase = CryptoJS.enc.Utf8.parse(AESKey); + const decrypted = CryptoJS.AES.decrypt(image.url, passPhrase, { + iv, + mode: CryptoJS.mode.CBC + }); + return new URL(CryptoJS.enc.Utf8.stringify(decrypted))+'?'+image.parameter; } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/GammaPlus.mjs b/src/web/mjs/connectors/GammaPlus.mjs index bcd9d51a47e..0be76aebb85 100644 --- a/src/web/mjs/connectors/GammaPlus.mjs +++ b/src/web/mjs/connectors/GammaPlus.mjs @@ -1,4 +1,4 @@ -import TakeShobo from './templates/TakeShobo.mjs'; +import TakeShobo from './templates/TakeShoboNew.mjs'; export default class GammaPlus extends TakeShobo { From 77531c2346a83df6135e92bbece3a51f8939103b Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 15 Jan 2023 19:44:45 +0000 Subject: [PATCH 46/47] Add files via upload --- src/web/mjs/connectors/Hinapyon.mjs | 19 +++++++++++++++++++ src/web/mjs/connectors/HunterScan.mjs | 4 ++-- src/web/mjs/connectors/Ichijin-Plus.mjs | 4 +++- src/web/mjs/connectors/InfraFandub.mjs | 10 ++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/web/mjs/connectors/Hinapyon.mjs create mode 100644 src/web/mjs/connectors/InfraFandub.mjs diff --git a/src/web/mjs/connectors/Hinapyon.mjs b/src/web/mjs/connectors/Hinapyon.mjs new file mode 100644 index 00000000000..5e5f4582841 --- /dev/null +++ b/src/web/mjs/connectors/Hinapyon.mjs @@ -0,0 +1,19 @@ +import WordPressMangastream from './templates/WordPressMangastream.mjs'; + +export default class Hinapyon extends WordPressMangastream { + + constructor() { + super(); + super.id = 'hinapyon'; + super.label = 'HinaPyon'; + this.tags = [ 'hentai', 'indonesia' ]; + this.url = 'https://hinapyon.fun'; + this.path = '/list-doujin/?list'; + + this.queryMangas = 'div#container div.listpst ul li a'; + this.queryChapters = 'div#chapter_list div.epsleft span.lchx a'; + this.queryPages = 'div.reader-area img[src]:not([src=""])'; + this.queryChaptersTitle = undefined; + this.querMangaTitleFromURI = 'div#infoarea div.post-body h1.entry-title'; + } +} diff --git a/src/web/mjs/connectors/HunterScan.mjs b/src/web/mjs/connectors/HunterScan.mjs index 07335fcccb4..284a861de2b 100644 --- a/src/web/mjs/connectors/HunterScan.mjs +++ b/src/web/mjs/connectors/HunterScan.mjs @@ -7,6 +7,6 @@ export default class HunterScan extends WordPressMadara { super.id = 'hunterscan'; super.label = 'Hunters Scan'; this.tags = [ 'manga', 'webtoon', 'portuguese' ]; - this.url = 'https://www.hunterscan.tk'; + this.url = 'https://huntersscan.xyz/'; } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/Ichijin-Plus.mjs b/src/web/mjs/connectors/Ichijin-Plus.mjs index 9196ec6be8d..786b27743af 100644 --- a/src/web/mjs/connectors/Ichijin-Plus.mjs +++ b/src/web/mjs/connectors/Ichijin-Plus.mjs @@ -9,5 +9,7 @@ export default class IchijinPlus extends ToCoronaEx { this.url = "https://ichijin-plus.com"; this.apiurl = 'https://api.ichijin-plus.com'; this.cdnurl = 'https://cdn.ichijin-plus.com'; + this.apikey = 'GGXGejnSsZw-IxHKQp8OQKHH-NDItSbEq5PU0g2w1W4='; + this.requestOptions.headers.set('X-API-Environment-Key', this.apikey); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/InfraFandub.mjs b/src/web/mjs/connectors/InfraFandub.mjs new file mode 100644 index 00000000000..06f0b8cb07c --- /dev/null +++ b/src/web/mjs/connectors/InfraFandub.mjs @@ -0,0 +1,10 @@ +import WordPressMadara from './templates/WordPressMadara.mjs'; +export default class InfraFandub extends WordPressMadara { + constructor() { + super(); + super.id = 'infrafandub'; + super.label = 'Infra Fandub'; + this.tags = [ 'webtoon', 'manga', 'spanish' ]; + this.url = 'https://infrafandub.xyz'; + } +} \ No newline at end of file From c7df0ba3e5867e755da40ff61b6bd26ca77b32da Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Mon, 16 Jan 2023 16:36:28 +0000 Subject: [PATCH 47/47] Add files via upload --- src/web/mjs/connectors/MavAnimes.mjs | 37 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/web/mjs/connectors/MavAnimes.mjs b/src/web/mjs/connectors/MavAnimes.mjs index 80f842a93ef..c578496610e 100644 --- a/src/web/mjs/connectors/MavAnimes.mjs +++ b/src/web/mjs/connectors/MavAnimes.mjs @@ -10,14 +10,30 @@ export default class MavAnime extends Connector { super.id = 'mavanimes'; super.label = 'MavAnimes'; this.tags = [ 'anime', 'french', 'multi-lingual' ]; - this.url = 'https://mavanimes.co'; + this.url = 'https://www.mavanimes.co'; this.genres = ['/tous-les-animes-en-vf', '/tous-les-animes-en-vostfr-fullhd-2']; + this.config = { + resolution: { + label: 'Preferred Resolution', + description: 'Try to download video in the selected resolution.\nIf the resolution is not supported, depending on the mirror the download may fail, or a fallback resolution may be used!', + input: 'select', + options: [ + { value: '', name: 'Mirror\'s Default' }, + { value: '360p', name: '360p' }, + { value: '480p', name: '480p' }, + { value: '720p', name: '720p' }, + { value: '1080p', name: '1080p' } + ], + value: '' + } + }; + } async _getMangas() { let mangaslist = []; - for (let i = 0; i < this.genres.length; i++) { - let request = new Request( new URL(this.genres[i], this.url), this.requestOptions ); + for (let genre of this.genres) { + let request = new Request( new URL(genre, this.url), this.requestOptions ); let data = await this.fetchDOM( request, 'div#az-slider li a' ); let mangas = data.map(element => { return { @@ -50,10 +66,7 @@ export default class MavAnime extends Connector { // Somes film does not have a chapter page and just one page with video frames if (chapterslist.length == 0) { try{ - let testnode= document.createElement("a"); - testnode.href = new URL(manga.id, this.url); - testnode.text = manga.title; - chapterslist.push(...await this.getChapterPlayers(testnode)); + chapterslist.push(...await this.getChapterPlayers(new URL(manga.id, this.url), manga)); } catch(e) { // } @@ -64,8 +77,10 @@ export default class MavAnime extends Connector { return chapterslist; } - async getChapterPlayers(chapterNode) { - let request = new Request(chapterNode.href, this.requestOptions); + async getChapterPlayers(chapterNode, manga) { + const link = chapterNode.href ? chapterNode.href : chapterNode; + const title = chapterNode.text ? chapterNode.text.trim() : manga.title; + let request = new Request(link, this.requestOptions); let scriptPages = ` new Promise(resolve => { resolve([...document.querySelectorAll('iframe')].map(el => el.src)); @@ -76,7 +91,7 @@ export default class MavAnime extends Connector { let sourcesite = this.getWebsiteTag(element); return { id: element, - title : sourcesite + ' '+ chapterNode.text.replace(':•', '').trim() + title : sourcesite + ' '+ title.replace(':•', '').trim() }; }).filter(el => !el.title.match(/\[UNK\]/)); } @@ -87,7 +102,7 @@ export default class MavAnime extends Connector { switch(sourcesite) { case '[MAV]': { - let vid = await new Mav(this.url, chapter.id).getStream(); + let vid = await new Mav(this.url, chapter.id).getStream(this.config.resolution.value); return vid.type == 'mp4' ? {video: vid.file, subtitles: [] }: {hash: 'id,language,resolution', mirrors: [ vid.file ], subtitles: [], referer : chapter.id }; } case '[Streamtape]':