diff --git a/package.json b/package.json index 8fc06e07b8..e4efee51e5 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "asar": "latest", "discord-rpc": "latest", "electron": "8.3.4", - "eslint": "latest", + "eslint": "^8.57.0", "fs-extra": "latest", "innosetup-compiler": "latest", "jest": "latest", @@ -35,7 +35,7 @@ "start:build": "npm run build:web && electron . --update-url=DISABLED --cache-directory=./build/latest", "lint": "npm run lint:app && npm run lint:web", "lint:app": "eslint ./src/app --ignore-path .gitignore", - "lint:web": "eslint ./src/web/mjs/**/*.mjs --ignore-path .gitignore --ignore-pattern Connectors.mjs", + "lint:web": "eslint ./src/web/mjs/**/*.mjs --ignore-path .gitignore --parser-options ecmaVersion:2020", "format": "npm run format:app && npm run format:web", "format:app": "npm run lint:app -- --fix", "format:web": "npm run lint:web -- --fix", diff --git a/src/web/img/connectors/monzeekomik b/src/web/img/connectors/monzeekomik new file mode 100644 index 0000000000..07bec10de5 Binary files /dev/null and b/src/web/img/connectors/monzeekomik differ diff --git a/src/web/mjs/connectors/ComicExtra.mjs b/src/web/mjs/connectors/ComicExtra.mjs index 85f973816f..38c04fb575 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://comicextra.me'; + this.url = 'https://comicextra.org'; this.path = '/comic-list/'; } diff --git a/src/web/mjs/connectors/ComicK.mjs b/src/web/mjs/connectors/ComicK.mjs index cfafd9d776..0f551da2a2 100644 --- a/src/web/mjs/connectors/ComicK.mjs +++ b/src/web/mjs/connectors/ComicK.mjs @@ -9,15 +9,15 @@ export default class ComicK extends Connector { super.id = 'comick'; super.label = 'ComicK'; this.tags = [ 'manga', 'english' ]; - this.url = 'https://comick.cc'; - this.apiurl = 'https://api.comick.cc'; + this.url = 'https://comick.io'; + this.apiurl = 'https://api.comick.io'; this.requestOptions.headers.set('x-origin', this.url ); this.requestOptions.headers.set('x-referer', this.apiurl ); } canHandleURI(uri) { - return /https?:\/\/comick\.(app|ink|cc)/.test(uri.origin); + return /https?:\/\/comick\.(app|ink|cc|io)/.test(uri.origin); } async _getEmbeddedJSON(uri) { diff --git a/src/web/mjs/connectors/KomikAV.mjs b/src/web/mjs/connectors/KomikAV.mjs index 237ca6223c..0d5c2b6b6e 100644 --- a/src/web/mjs/connectors/KomikAV.mjs +++ b/src/web/mjs/connectors/KomikAV.mjs @@ -7,7 +7,7 @@ export default class KomikAV extends WordPressMangastream { super.id = 'komikav'; super.label = 'APKomik'; this.tags = [ 'manga', 'indonesian' ]; - this.url = 'https://apkomic.cc'; + this.url = 'https://apkomik.cc'; this.path = '/manga/list-mode/'; } } diff --git a/src/web/mjs/connectors/LxHentai.mjs b/src/web/mjs/connectors/LxHentai.mjs index a51937b102..21cd3abf48 100644 --- a/src/web/mjs/connectors/LxHentai.mjs +++ b/src/web/mjs/connectors/LxHentai.mjs @@ -7,7 +7,7 @@ export default class LxHentai extends MojoPortalComic { super.id = 'lxhentai'; super.label = 'LXHENTAI'; this.tags = [ 'manga', 'hentai', 'vietnamese' ]; - this.url = 'https://lxmanga.net'; + this.url = 'https://lxmanga.cc'; this.queryMangaTitle = 'head title'; this.queryChapter = 'div.justify-between ul.overflow-y-auto a'; diff --git a/src/web/mjs/connectors/MagKan.mjs b/src/web/mjs/connectors/MagKan.mjs index f4a26fc0f1..1dd16884c5 100644 --- a/src/web/mjs/connectors/MagKan.mjs +++ b/src/web/mjs/connectors/MagKan.mjs @@ -1,59 +1,12 @@ -import Connector from '../engine/Connector.mjs'; -import Manga from '../engine/Manga.mjs'; +import YoungChampion from './YoungChampion.mjs'; -export default class MagKan extends Connector { +export default class MagKan extends YoungChampion { constructor() { super(); super.id = 'magkan'; super.label = 'MagKan'; this.tags = [ 'manga', 'japanese' ]; - this.url = 'http://kansai.mag-garden.co.jp'; + this.url = 'https://kansai.mag-garden.co.jp'; } - - async _getMangaFromURI(uri) { - const request = new Request(uri, this.requestOptions); - const data = await this.fetchDOM(request, 'meta[property="og:title"]'); - return new Manga(this, uri.pathname, data[0].content.trim()); - } - - async _getMangas() { - const uri = new URL(this.url); - const request = new Request(uri, this.requestOptions); - const data = await this.fetchDOM(request, 'div#main div.panel div.box div.inner'); - return data.map(element => { - return { - id: this.getRootRelativeOrAbsoluteLink(element.querySelector('a'), this.url), - title: element.querySelector('h2.comic_name').textContent.trim() - }; - }); - } - - async _getChapters(manga) { - const uri = new URL(manga.id, this.url); - const request = new Request(uri, this.requestOptions); - const body = await this.fetchDOM(request); - const current = [...body.querySelectorAll('div#main div.update_summary div.exp ul.btn li a[href*="/assets/files/"]')].map(element => { - return { - id: this.getRootRelativeOrAbsoluteLink(element, this.url).replace(/\/HTML5\/?$/i, ''), - title: element.text.replace('を読む', '').trim() - }; - }); - const previous = [...body.querySelectorAll('div#main div.sam_exp div.exp')].map(element => { - return { - id: this.getRootRelativeOrAbsoluteLink(element.querySelector('ul.btn li a[href*="/assets/files/"]'), this.url).replace(/\/HTML5\/?$/i, ''), - title: element.querySelector('div.back_number_summary div.ttl').textContent.trim() - }; - }); - return [ ...current, ...previous ]; - } - - async _getPages(chapter) { - const uri = new URL(chapter.id + '/iPhone/ibook.xml', this.url); - const request = new Request(uri, this.requestOptions); - const response = await fetch(request); - const data = await response.text(); - const pages = parseInt(data.match(/(\d+)<\/total>/)[1]); - return new Array(pages).fill().map((_, index) => this.getAbsolutePath(`${chapter.id}/books/images/2/${index + 1}.jpg`, request.url)); - } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/MangaBat.mjs b/src/web/mjs/connectors/MangaBat.mjs index 9d195f8dd8..5a421b9c67 100644 --- a/src/web/mjs/connectors/MangaBat.mjs +++ b/src/web/mjs/connectors/MangaBat.mjs @@ -7,7 +7,7 @@ export default class MangaBat extends MangaNel { super.id = 'mangabat'; super.label = 'MangaBat'; this.tags = [ 'manga', 'webtoon', 'english' ]; - this.url = 'https://m.mangabat.com'; + this.url = 'https://h.mangabat.com'; this.path = '/manga-list-all/'; this.queryMangas = 'div.panel-list-story div.list-story-item h3 a.item-title'; @@ -16,6 +16,6 @@ export default class MangaBat extends MangaNel { canHandleURI(uri) { // Test: https://regex101.com/r/GlzAw2/2/tests - return /^(m\.|read\.)?mangabat\.com$/.test(uri.hostname); + return /^(m\.|read\.|h\.)?mangabat\.com$/.test(uri.hostname); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/MangaSail.mjs b/src/web/mjs/connectors/MangaSail.mjs index bfc66ad6a4..a022e6e596 100644 --- a/src/web/mjs/connectors/MangaSail.mjs +++ b/src/web/mjs/connectors/MangaSail.mjs @@ -7,7 +7,7 @@ export default class MangaSail extends Connector { super.id = 'mangasail'; super.label = 'MangaSail'; this.tags = [ 'manga', 'english' ]; - this.url = 'https://www.mangasail.net'; + this.url = 'https://sailmg.com'; this.config = { username: { diff --git a/src/web/mjs/connectors/MangaSect.mjs b/src/web/mjs/connectors/MangaSect.mjs index f5945dac76..9605bc3459 100644 --- a/src/web/mjs/connectors/MangaSect.mjs +++ b/src/web/mjs/connectors/MangaSect.mjs @@ -1,70 +1,40 @@ -import Connector from '../engine/Connector.mjs'; -import Manga from '../engine/Manga.mjs'; +import MojoPortalComic from './templates/MojoPortalComic.mjs'; -export default class MangaSect extends Connector { +export default class MangaSect extends MojoPortalComic { constructor() { super(); super.id = 'mangasect'; super.label = 'MangaSect'; this.tags = [ 'webtoon', 'english' ]; - this.url = 'https://mangasect.com'; + this.url = 'https://mangasect.net'; this.path = '/all-manga/'; } - async _getMangaFromURI(uri) { - const request = new Request(uri, this.requestOptions); - const data = await this.fetchDOM(request, 'header h1'); - return new Manga(this, uri.pathname, data[0].textContent.trim()); - } + async _getPages(chapter) { + const request = new Request(new URL(chapter.id, this.url), this.requestOptions); + const script = ` + new Promise(resolve => { - async _getMangas() { - let mangaList = []; - const uri = new URL(this.path, this.url); - const request = new Request(uri, this.requestOptions); - const data = await this.fetchDOM(request, 'div.blog-pager span:last-of-type a'); - const pageCount = parseInt(data[0].href.match(/\/(\d)+\//)[1]); - for(let page = 1; page <= pageCount; page++) { - const mangas = await this._getMangasFromPage(page); - mangaList.push(...mangas); - } - return mangaList; - } + function parseResults(data) { + const dom = new DOMParser().parseFromString(data, 'text/html'); + let nodes = [...dom.querySelectorAll('img')]; + resolve(nodes.map(element => element.dataset.original)); + } - 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, 'div.grid div.text-center > a'); - return data.map(element => { - return { - id: this.getRootRelativeOrAbsoluteLink(element, this.url), - title: element.text.trim() - }; - }); + const ajaxendpoint = new URL('/ajax/image/list/chap/' + CHAPTER_ID, window.location.href); + fetch(ajaxendpoint, { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + } + }) + .then(response => response.json()) + .then(jsonData => { + parseResults(jsonData.html); + }); + }); + `; + return Engine.Request.fetchUI(request, script); } - async _getChapters(manga) { - const uri = new URL(manga.id, this.url); - const request = new Request(uri, this.requestOptions); - const data = await this.fetchDOM(request, 'li.chapter > a'); - return data.map(element => { - return { - id: this.getRootRelativeOrAbsoluteLink(element, this.url), - title: element.text.trim() - }; - }); - } - - async _getPages(chapter) { - const referer = new URL(chapter.id, this.url); - const chapterid = chapter.id.match(/\/([\d]+)$/)[1]; - const uri = new URL ('/ajax/image/list/chap/' + chapterid, this.url); - const request = new Request(uri, this.requestOptions); - request.headers.set('x-referer', referer); - request.headers.set('X-Requested-With', 'XMLHttpRequest'); - const response = await this.fetchJSON(request); - const dom = this.createDOM(response.html); - const data = dom.querySelectorAll('source[data-src]'); - return Array.from(data).map(image => this.getAbsolutePath(image.dataset['src'], request.url)); - } } diff --git a/src/web/mjs/connectors/MangaStarz.mjs b/src/web/mjs/connectors/MangaStarz.mjs index 22e895a838..7e6dca81c5 100644 --- a/src/web/mjs/connectors/MangaStarz.mjs +++ b/src/web/mjs/connectors/MangaStarz.mjs @@ -7,6 +7,6 @@ export default class MangaStarz extends WordPressMadara { super.id = 'mangastarz'; super.label = 'مانجا ستارز (Mangastarz)'; this.tags = [ 'manga', 'webtoon', 'arabic' ]; - this.url = 'https://mangastarz.org'; + this.url = 'https://manga-starz.com'; } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/MangaTX.mjs b/src/web/mjs/connectors/MangaTX.mjs index 8857e45e7b..43fb39a6ec 100644 --- a/src/web/mjs/connectors/MangaTX.mjs +++ b/src/web/mjs/connectors/MangaTX.mjs @@ -7,6 +7,6 @@ export default class MangaTX extends WordPressMadara { super.id = 'mangatx'; super.label = 'Mangatx'; this.tags = [ 'webtoon', 'english' ]; - this.url = 'https://mangatx.com'; + this.url = 'https://mangatx.to'; } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/Mangalek.mjs b/src/web/mjs/connectors/Mangalek.mjs index cde3baa21f..2950438b50 100644 --- a/src/web/mjs/connectors/Mangalek.mjs +++ b/src/web/mjs/connectors/Mangalek.mjs @@ -7,7 +7,7 @@ export default class Mangalek extends WordPressMadara { super.id = 'mangalek'; super.label = 'مانجا ليك (Mangalek)'; this.tags = [ 'manga', 'webtoon', 'arabic' ]; - this.url = 'https://manga-lek.net'; + this.url = 'https://lekmanga.net'; this.queryTitleForURI = 'div.profile-manga div.post-title h1'; this.requestOptions.headers.set('x-referer', this.url); } diff --git a/src/web/mjs/connectors/MonzeeKomik.mjs b/src/web/mjs/connectors/MonzeeKomik.mjs new file mode 100644 index 0000000000..3ad2366838 --- /dev/null +++ b/src/web/mjs/connectors/MonzeeKomik.mjs @@ -0,0 +1,19 @@ +import WordPressMangastream from './templates/WordPressMangastream.mjs'; + +export default class MonzeeKomik extends WordPressMangastream { + + constructor() { + super(); + super.id = 'monzeekomik'; + super.label = 'MonzeeKomik'; + this.tags = ['manga', 'manhwa', 'indonesian']; + this.url = 'https://monzeekomik.my.id'; + this.path = '/manga/list-mode/'; + } + + async _getMangas() { + const mangas = await super._getMangas(); + mangas.forEach(manga => manga.title = manga.title.replace(/Bahasa Indonesia$/i, '').trim()); + return mangas; + } +} diff --git a/src/web/mjs/connectors/MoonWitchInLove.mjs b/src/web/mjs/connectors/MoonWitchInLove.mjs index 4e6af0bded..661d9ed26a 100644 --- a/src/web/mjs/connectors/MoonWitchInLove.mjs +++ b/src/web/mjs/connectors/MoonWitchInLove.mjs @@ -7,6 +7,6 @@ export default class MoonWitchInLove extends WordPressMadara { super.id = 'moonwitchinlove'; super.label = 'Moon Witch In Love'; this.tags = [ 'webtoon', 'portuguese', 'scanlation' ]; - this.url = 'https://moonwitchinlove.com'; + this.url = 'https://moonwitchinlovescan.com'; } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/Saikaiscan.mjs b/src/web/mjs/connectors/Saikaiscan.mjs index 0b3f21b4a2..8cec74c246 100644 --- a/src/web/mjs/connectors/Saikaiscan.mjs +++ b/src/web/mjs/connectors/Saikaiscan.mjs @@ -8,15 +8,15 @@ export default class SaikaiScan extends Connector { super.id = 'saikaiscan'; super.label = 'Saikaiscan'; this.tags = [ 'manga', 'portuguese', 'webtoon', 'novel' ]; - this.url = 'https://saikaiscan.com.br'; - this.api = 'https://api.saikai.com.br/api/stories'; - this.imagesurl = 'https://s3-alpha.saikai.com.br'; + this.url = 'https://saikaiscans.net'; + this.api = 'https://api.saikaiscans.net/api/stories'; + this.imagesurl = 'https://s3-alpha.saikaiscans.net'; this.novelContentQuery = 'div#leitor-serie-body'; this.novelFormat = 'image/png'; this.novelWidth = '56em'; this.novelPadding = '1.5em'; this.links = { - login: 'https://saikaiscan.com.br/login' + login: 'https://saikaiscan.net/login' }; } diff --git a/src/web/mjs/connectors/Siyahmelek.mjs b/src/web/mjs/connectors/Siyahmelek.mjs index 38b1a879e1..2752318fc8 100644 --- a/src/web/mjs/connectors/Siyahmelek.mjs +++ b/src/web/mjs/connectors/Siyahmelek.mjs @@ -7,9 +7,9 @@ export default class Siyahmelek extends WordPressMadara { super.id = 'siyahmelek'; super.label = 'Gri Melek (Siyahmelek)'; this.tags = [ 'manga', 'webtoon', 'turkish' ]; - this.url = 'https://grimelek.net'; + this.url = 'https://grimelek.co'; this.links = { - login : 'https://grimelek.net'//this website needs login to see content ! + login : 'https://grimelek.co'//this website needs login to see content ! }; } diff --git a/src/web/mjs/connectors/TopToon.mjs b/src/web/mjs/connectors/TopToon.mjs index 8934361579..4051063cf6 100644 --- a/src/web/mjs/connectors/TopToon.mjs +++ b/src/web/mjs/connectors/TopToon.mjs @@ -15,12 +15,12 @@ export default class TopToon extends Connector { } async _getMangaFromURI(uri) { const request = new Request(uri, this.requestOptions); - const data = await this.fetchDOM(request, 'div.bnr_episode_info p.tit_toon'); + const data = await this.fetchDOM(request, 'div.ep_comic_info span.comic_tit span'); return new Manga(this, uri.pathname, data[0].textContent.trim()); } async _getMangas() { - const req = new Request('https://toptoon.com/hashtag', this.requestOptions); + const req = new Request(new URL('/hashtag', this.url), this.requestOptions); const api = await this.fetchRegex(req, /fileUrl\s*:\s*'([^']+)'/g); const request = new Request(api[0], this.requestOptions); const data = await this.fetchJSON(request); @@ -34,11 +34,11 @@ export default class TopToon extends Connector { async _getChapters(manga) { const request = new Request(new URL(manga.id, this.url), this.requestOptions); - const data = await this.fetchDOM(request, 'div.episode_list ul a.episode-items'); + const data = await this.fetchDOM(request, 'div.eplist ul a.episode-items'); return data.map(element => { - let title = element.querySelector('p.episode_title').textContent.trim(); - const subtitle = element.querySelector('p.episode_stitle'); - title += subtitle ? ' - ' + subtitle.textContent.trim() : ''; + let title = element.querySelector('p.ep_title').textContent.trim(); + const subtitle = element.querySelector('p.ep_stitle'); + title += subtitle && subtitle.textContent.trim() != '' ? ' - ' + subtitle.textContent.trim() : ''; return { id: `/comic/ep_view/${element.dataset.comicId}/${element.dataset.episodeId}`, title: title @@ -51,4 +51,4 @@ export default class TopToon extends Connector { const data = await this.fetchDOM(request, 'div#viewerContentsWrap source.document_img'); return data.map(element => this.getAbsolutePath(element.dataset.src || element, request.url)); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/templates/MangaToon.mjs b/src/web/mjs/connectors/templates/MangaToon.mjs index 174db52cee..bde6896234 100644 --- a/src/web/mjs/connectors/templates/MangaToon.mjs +++ b/src/web/mjs/connectors/templates/MangaToon.mjs @@ -79,7 +79,7 @@ export default class MangaToon extends Connector { * Alternative mobile request (id, token and signature calculations are all handled within the WEEX + VUE application => to much effort to break in): * https://sg.mangatoon.mobi/api/content/episodes?sign=e9da6de28b76408e77040935fd221cd3&id=5&_=1557650222&_v=1.3.6&_language=en&_token=4f9b604ed0055dd569105a7b32b6489c10&_udid=1246361632e50c7a9daef1e187471778 */ - this.fetchDOM(this.baseURL + manga.id + '/episodes', 'div.episodes-wrap a.episode-item, div.episodes-wrap-new a.episode-item-new') + this.fetchDOM(this.baseURL + manga.id + '/episodes', 'div.episode-content-asc div.episodes-wrap a.episode-item, div.episode-content-asc div.episodes-wrap-new a.episode-item-new') .then( data => { let chapterList = data.map( element => { let title = element.querySelector('div.episode-title, div.episode-title-new:last-of-type').innerText.replace(/\s+/g, ' ').trim(); @@ -122,4 +122,4 @@ export default class MangaToon extends Connector { callback( error, undefined ); } ); } -} \ No newline at end of file +} diff --git a/src/web/mjs/connectors/winterscan.mjs b/src/web/mjs/connectors/winterscan.mjs index 6c0ba17162..7d0a621954 100644 --- a/src/web/mjs/connectors/winterscan.mjs +++ b/src/web/mjs/connectors/winterscan.mjs @@ -7,6 +7,6 @@ export default class winterscan extends WordPressMadara { super.id = 'winterscan'; super.label = 'Winter Scan'; this.tags = [ 'webtoon', 'portuguese', 'scanlation' ]; - this.url = 'https://winterscan.com.br'; + this.url = 'https://winterscan.com'; } -} \ No newline at end of file +} diff --git a/src/web/mjs/engine/Connectors.mjs b/src/web/mjs/engine/Connectors.mjs index ee6d094555..e09b7fd1fb 100644 --- a/src/web/mjs/engine/Connectors.mjs +++ b/src/web/mjs/engine/Connectors.mjs @@ -9,7 +9,7 @@ export default class Connectors { try { let response = await fetch(uri); let data = await response.json(); - return data.filter(plugin => !plugin.startsWith('.') && plugin.endsWith('.mjs')).map(plugin => uri + plugin) + return data.filter(plugin => !plugin.startsWith('.') && plugin.endsWith('.mjs')).map(plugin => uri + plugin); } catch(error) { //console.warn(error); return []; @@ -50,13 +50,13 @@ export default class Connectors { } } this._list.sort( ( a, b ) => { - return ( a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1 ); + return a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1; } ); } catch(error) { console.warn(`Failed to load connector`, error); } } - + async _onConnectorProtocolHandler(request) { try { let uri = new URL(request.url);