diff --git a/src/web/img/connectors/animeflv b/src/web/img/connectors/animeflv new file mode 100644 index 0000000000..72bb76c508 Binary files /dev/null and b/src/web/img/connectors/animeflv differ diff --git a/src/web/img/connectors/mangabook b/src/web/img/connectors/mangabook new file mode 100644 index 0000000000..72bb76c508 Binary files /dev/null and b/src/web/img/connectors/mangabook differ diff --git a/src/web/img/connectors/mangamovil b/src/web/img/connectors/mangamovil new file mode 100644 index 0000000000..9beae0edf6 Binary files /dev/null and b/src/web/img/connectors/mangamovil differ diff --git a/src/web/img/connectors/mundowebtoon b/src/web/img/connectors/mundowebtoon new file mode 100644 index 0000000000..7528f9684e Binary files /dev/null and b/src/web/img/connectors/mundowebtoon differ diff --git a/src/web/img/connectors/ravenmanga b/src/web/img/connectors/ravenmanga new file mode 100644 index 0000000000..afd74b0977 Binary files /dev/null and b/src/web/img/connectors/ravenmanga differ diff --git a/src/web/img/connectors/tuttoanimemanga b/src/web/img/connectors/tuttoanimemanga new file mode 100644 index 0000000000..8ba42a5ed6 Binary files /dev/null and b/src/web/img/connectors/tuttoanimemanga differ diff --git a/src/web/img/connectors/yaoiflix b/src/web/img/connectors/yaoiflix new file mode 100644 index 0000000000..72bb76c508 Binary files /dev/null and b/src/web/img/connectors/yaoiflix differ diff --git a/src/web/mjs/connectors/AnimeFLV.mjs b/src/web/mjs/connectors/AnimeFLV.mjs new file mode 100644 index 0000000000..03e3980342 --- /dev/null +++ b/src/web/mjs/connectors/AnimeFLV.mjs @@ -0,0 +1,98 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +import StreamTape from '../videostreams/StreamTape.mjs'; +import YourUpload from '../videostreams/YourUpload.mjs'; +import StreamSB from '../videostreams/StreamSB.mjs'; +import Fembed from '../videostreams/Fembed.mjs'; + +export default class AnimeFLV extends Connector { + constructor() { + super(); + super.id = 'AnimeFLV'; + super.label = 'AnimeFLV'; + this.tags = [ 'anime', 'spanish' ]; + this.url = 'https://www3.animeflv.net'; + } + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.Container h1.title'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); + } + async _getMangas() { + let mangaList = []; + const uri = new URL('/browse', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'ul.pagination li:nth-last-of-type(2) a'); + const pageCount = parseInt(data[0].text); + 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('/browse?page=' + page, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'article.Anime'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element.querySelector('a'), this.url), + title: element.querySelector('div.Description div.Title strong').textContent.trim() + }; + }); + } + async _getChapters(manga) { + const uri = new URL(manga.id, this.url); + let script = ` + new Promise(resolve => { + resolve({ + info : anime_info, episodes : episodes} + ); + }); + `; + let request = new Request(uri, this.requestOptions); + const data = await Engine.Request.fetchUI(request, script, 3000); + let chapterlist = []; + for (let episode of data.episodes) { + chapterlist.push(...await this.listVideos(episode, data.info)); + } + return chapterlist; + } + async listVideos(episode, info) { + const url = new URL('/ver/'+info[2] +'-'+ episode[0], this.url); + let script = ` + new Promise(resolve => { + resolve(videos); + }); + `; + let request = new Request(url, this.requestOptions); + const data = await Engine.Request.fetchUI(request, script, 3000); + return data.SUB.map(video => { + return { + id : video.code, + title : 'Episode '+ episode[0] + ' ['+video.title+']' + }; + }); + } + async _getPages(chapter) { + const uri = chapter.id; + const hoster = chapter.title.match(/(\[\w+\])/)[1]; + let vid = ''; + switch (hoster) { + case '[Stape]': + vid = await new StreamTape(uri).getStream(); + return {video : vid}; + case '[YourUpload]': + vid = await new YourUpload(uri).getStream(); + return {video : vid, referer : 'https://www.yourupload.com'}; + case '[SB]': + vid = await new StreamSB(uri).getStream(); + return {mirrors : [vid]}; + case '[Fembed]': + vid = await new Fembed(uri).getStream(); + return {mirrors : [vid], referrer : chapter.id}; + default: + throw new Error('Hoster '+ hoster + ' not supported :/'); + } + } +} diff --git a/src/web/mjs/connectors/MangaBook.mjs b/src/web/mjs/connectors/MangaBook.mjs new file mode 100644 index 0000000000..c18a97737b --- /dev/null +++ b/src/web/mjs/connectors/MangaBook.mjs @@ -0,0 +1,56 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class MangaBook extends Connector { + constructor() { + super(); + super.id = 'mangabook'; + super.label = 'MangaBook'; + this.tags = [ 'webtoon', 'russian', 'manga' ]; + this.url = 'https://mangabook.org'; + } + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div#fheader h1'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); + } + async _getMangas() { + let mangaList = []; + const uri = new URL('/manga-list', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'ul.pagination li:nth-last-of-type(2) a'); + const pageCount = parseInt(data[0].text); + 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('/filterList?page='+page + '&ftype[]=0&status[]=0', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.short-in'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element.querySelector('a'), this.url), + title: element.querySelector('div.sh-title').textContent.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, 'ul.chapters li h5 a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.text.trim() + }; + }); + } + async _getPages(chapter) { + const uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div#all source'); + return data.map(element => element.getAttribute('data-src')); + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/MangaMovil.mjs b/src/web/mjs/connectors/MangaMovil.mjs new file mode 100644 index 0000000000..98193b18ab --- /dev/null +++ b/src/web/mjs/connectors/MangaMovil.mjs @@ -0,0 +1,52 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class MangaMovil extends Connector { + constructor() { + super(); + super.id = 'mangamovil'; + super.label = 'MangaMovil'; + this.tags = [ 'manga', 'webtoon', 'spanish' ]; + this.url = 'https://mangamovil.net'; + } + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.video-card-body div.video-title h1'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); + } + async _getMangas() { + let mangaList = []; + for (let page = 1, run = true; run; page++) { + const mangas = await this._getMangasFromPage(page); + mangas.length > 0 ? mangaList.push(...mangas) : run = false; + } + return mangaList; + } + async _getMangasFromPage(page) { + const uri = new URL('/biblioteca?page=' + page, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.container-biblioteca div.card-body a.new-link.text-white'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.querySelector('h5').textContent.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, 'ul.list-group a.new-link'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.querySelector('li span.sa-series-link__number').textContent.trim() + }; + }); + } + async _getPages(chapter) { + const uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'source.img-fluid'); + return data.map(image => this.getAbsolutePath(image.getAttribute('data-src'), request.url)); + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/MundoWebtoon.mjs b/src/web/mjs/connectors/MundoWebtoon.mjs new file mode 100644 index 0000000000..15077fc47a --- /dev/null +++ b/src/web/mjs/connectors/MundoWebtoon.mjs @@ -0,0 +1,68 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; + +export default class MundoWebtoon extends Connector { + + constructor() { + super(); + super.id = 'mundowebtoon'; + super.label = 'MundoWebtoon'; + this.tags = [ 'webtoon', 'portuguese', 'manga' ]; + this.url = 'https://mundowebtoon.com'; + } + + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.mangaTitulo h3'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); + } + + async _getMangas() { + const uri = new URL('/mangas', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.andro_product-body a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.text.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, 'div.CapitulosListaItem > a:not([target])'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.querySelector('h5').textContent.replace(/(\(\S+\))/, '').trim() + }; + }); + } + + async _getPages(chapter) { + const uri = new URL('leitor_image.php', this.url); + const referer = new URL(chapter.id, this.url); + const slug = chapter.id.split('/')[2]; + const chap = chapter.id.split('/')[3]; + const request = new Request(uri, { + method: 'POST', + body: new URLSearchParams({ + data: slug, + num: chap, + modo: '1', + busca: 'img', + }) + .toString(), + headers: { + 'x-origin': this.url, + 'x-referer': referer, + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'X-Requested-With': 'XMLHttpRequest', + } + }); + const data = await this.fetchDOM(request, 'source'); + return data.map(element => this.getRootRelativeOrAbsoluteLink(element.getAttribute('src'), this.url)); + } +} diff --git a/src/web/mjs/connectors/RavenManga.mjs b/src/web/mjs/connectors/RavenManga.mjs new file mode 100644 index 0000000000..8c690e64a7 --- /dev/null +++ b/src/web/mjs/connectors/RavenManga.mjs @@ -0,0 +1,45 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class RavenManga extends Connector { + constructor() { + super(); + super.id = 'ravenmanga'; + super.label = 'RavenManga'; + this.tags = [ 'manga', 'spanish', 'webtoon' ]; + this.url = 'https://ravenmanga.xyz'; + this.requestOptions.headers.set('x-referer', this.url); + } + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.infoanime h1.entry-title'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); + } + async _getMangas() { + const uri = new URL('/biblioteca-de-series/?list', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.listttl ul li a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.text.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, 'div#chapter_list span.lchx a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.text.indexOf('–') != -1 ? element.text.split('–')[1].trim() : element.text.trim() + }; + }); + } + async _getPages(chapter) { + const uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.reader-area source#imagech'); + return data.map(image => this.getAbsolutePath(image, request.url)); + } +} diff --git a/src/web/mjs/connectors/TuttoAnimeManga.mjs b/src/web/mjs/connectors/TuttoAnimeManga.mjs new file mode 100644 index 0000000000..014dbe9463 --- /dev/null +++ b/src/web/mjs/connectors/TuttoAnimeManga.mjs @@ -0,0 +1,12 @@ +import PizzaReader from './templates/PizzaReader.mjs'; + +export default class TuttoAnimeManga extends PizzaReader { + + constructor() { + super(); + super.id = 'tuttoanimemanga'; + super.label = 'TuttoAnimeManga'; + this.tags = [ 'manga', 'italian', 'scanlation' ]; + this.url = 'https://tuttoanimemanga.net'; + } +} \ No newline at end of file diff --git a/src/web/mjs/connectors/YaoiFlix.mjs b/src/web/mjs/connectors/YaoiFlix.mjs new file mode 100644 index 0000000000..d74b63b883 --- /dev/null +++ b/src/web/mjs/connectors/YaoiFlix.mjs @@ -0,0 +1,52 @@ +import Connector from '../engine/Connector.mjs'; +import Manga from '../engine/Manga.mjs'; +export default class YaoiFlix extends Connector { + constructor() { + super(); + super.id = 'yaoiflix'; + super.label = 'YaoiFlix'; + this.tags = [ 'manga', 'turkish', 'webtoon' ]; + this.url = 'https://www.yaoiflix.be'; + } + async _getMangaFromURI(uri) { + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.inf h1.film'); + return new Manga(this, uri.pathname, data[0].textContent.trim()); + } + async _getMangas() { + let mangaList = []; + for (let page = 1, run = true; run; page++) { + const mangas = await this._getMangasFromPage(page); + mangas.length > 0 ? mangaList.push(...mangas) : run = false; + } + return mangaList; + } + async _getMangasFromPage(page) { + const uri = new URL('/dizi-arsivi/page/'+page+'/?sort=views', this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.movie-details.existing-details div.name a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.text.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, 'div#seasons div.movie-details.existing-details div.name a'); + return data.map(element => { + return { + id: this.getRootRelativeOrAbsoluteLink(element, this.url), + title: element.text.trim() + }; + }).reverse(); + } + async _getPages(chapter) { + const uri = new URL(chapter.id, this.url); + const request = new Request(uri, this.requestOptions); + const data = await this.fetchDOM(request, 'div.video-content source'); + return data.map(image => this.getAbsolutePath(image, request.url)); + } +} \ No newline at end of file