diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 6ffb8b6..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/node_modules - -Procfile \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index cfe5079..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 Rajarshee Chatterjee - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 3595844..0000000 --- a/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# LNReader Sources - -Sources for [LNReader](https://github.com/rajarsheechatterjee/lnreader) - -## Sources - -| Name | Source URL | -| ------------------ | ------------------------------------------ | -| BoxNovel | https://boxnovel.com/ | -| ReadLightNovel | https://www.readlightnovel.org | -| FastNovel | https://fastnovel.net/ | -| ReadNovelFull | https://readnovelfull.com/ | -| MTLNovel | https://www.mtlnovel.com/ | -| NovelHall | https://www.novelhall.com/ | -| WuxiaWorld | https://www.wuxiaworld.com/ | -| NovelFull | https://novelfull.com/ | -| NovelTrench | https://noveltrench.com/ | -| VipNovel | https://vipnovel.com/ | -| KissLightNovels | https://kisslightnovels.info/ | -| WuxiaWorldSite | https://wuxiaworldsite.co/ | -| FreeWebNovel | https://freewebnovel.com/ | -| JPMTL | https://jpmtl.com/ | -| LightNovelPub | https://www.lightnovelpub.com/ | -| WuxiaWorld.co | https://www.wuxiaworld.co/ | -| TapRead | http://www.tapread.com/ | -| NovelUpdates.cc | https://www.novelupdates.cc/ | -| ReadLightNovel.cc | https://m.readlightnovel.cc/ | -| WuxiaWorld.cloud | http://wuxiaworld.cloud/ | -| WoopRead | https://woopread.com/ | -| Foxaholic | https://foxaholic.com/ | -| Tunovelaligera | https://tunovelaligera.com/ | -| SkyNovels | https://www.skynovels.net/ | -| EinherjarProject | https://einherjarproject.net/ | -| NovelasLigera | https://novelasligera.com/ | -| ComradeMao | https://comrademao.com/ | -| YuukiTls | https://yuukitls.com/ | -| Hasu Translations | https://hasutl.wordpress.com/ | -| Oasis Translations | https://oasistranslations.wordpress.com/ | -| Novela Wuxia | http://www.novelawuxia.com/ | -| NovelPassion | https://www.novelpassion.com/ | -| Royal Road | https://www.royalroad.com/ | -| Scribble Hub | https://www.scribblehub.com/ | -| Syosetu | https://syosetu.com | -| LNMTL | https://lnmtl.com/ | -| SkyNovel | https://skynovel.org/ | -| NovelCake | https://novelcake.com/ | -| NovelsRock | https://novelsrock.com/ | -| ZinnNovel | https://zinnovel.com/ | -| NovelTranslate | https://noveltranslate.com/ | -| LunarLetters | https://www.lunarletters.com/ | -| SleepyTranslations | https://sleepytranslations.com/ | -| FreeNovelMe | https://freenovel.me/ | -| 1stKissNovel | https://1stkissnovel.love/ | -| DaoNovel | https://daonovel.com/ | -| Wuxia.Blog | https://www.wuxia.blog/ | -| Wuxia.City | https://wuxia.city/ | -| Novel Updates | https://www.novelupdates.com/ | -| Ranobes | https://ranobes.net/ | -| Yushubo | https://m.yushubo.com | -| KolNovel | https://kolnovel.com/ | -| RewayatAr | https://rewayat-ar.site/ | -| MostNovel | https://mostnovel.com/ | -| NovelMultiverse | https://www.novelmultiverse.com/ | -| LightNovelHeaven | https://lightnovelheaven.com/ | -| LightNovelsHub | https://lightnovelshub.com/ | -| ArNovel | https://arnovel.me/ | -| MeioNovel | https://meionovel.id/ | -| WebNovelLover | https://www.webnovelover.com/ | -| WLNUpdates | https://www.wlnupdates.com/ | -| BoxNovel.online | https://boxnovel.online/ | -| ClickNovel | https://clicknovel.net/ | -| ReadWebNovels | https://readwebnovels.net/ | -| WBNovel | https://wbnovel.com/ | -| ReaperScans | https://reaperscans.com/all-series/novels/ | -| Readwn.com | https://www.readwn.com/ | -| Ranobehub | https://ranobehub.org/ | -| Divine Dao Library | https://divinedaolibrary.com/ | - -## Setup locally - -Clone this repository - -``` -git clone https://github.com/rajarsheechatterjee/lnreader-extensions.git -``` - -Install dependencies - -``` -npm install -``` - -Start the server - -``` -npm start -``` diff --git a/index.js b/index.js deleted file mode 100644 index 14ae3ec..0000000 --- a/index.js +++ /dev/null @@ -1,78 +0,0 @@ -const express = require("express"); -const sources = require("./sources"); - -const app = express(); - -app.use( - express.json({ - extended: false, - }) -); - -app.get("/", (req, res) => - res.json({ - Author: "@rajarsheechatterjee", - Version: "1.0.0", - Github: "https://github.com/rajarsheechatterjee/lnreader-extensions", - }) -); - -// English -app.get("/api/", (req, res) => res.json(sources)); -app.use("/api/1/", require("./src/en/boxnovel/BoxNovel")); -app.use("/api/2/", require("./src/en/readlightnovel/ReadLightNovel")); -app.use("/api/3/", require("./src/en/fastnovel/FastNovel")); -app.use("/api/4/", require("./src/en/readnovelfull/ReadNovelFull")); -app.use("/api/5/", require("./src/en/mtlnovel/MTLNovel")); -app.use("/api/6/", require("./src/en/novelhall/Novelhall")); -app.use("/api/7/", require("./src/en/wuxiaworld/WuxiaWorld")); -app.use("/api/8/", require("./src/en/novelfull/NovelFull")); -app.use("/api/9/", require("./src/en/noveltrench/NovelTrench")); -app.use("/api/10/", require("./src/en/vipnovel/VipNovel")); -app.use("/api/11/", require("./src/en/kisslightnovels/KissLightNovels")); -app.use("/api/12/", require("./src/en/wuxiaworldsite/WuxiaWorldSite")); -app.use("/api/13/", require("./src/en/freewebnovel/FreeWebNovel")); -app.use("/api/14/", require("./src/en/jpmtl/JPMTL")); -app.use("/api/15/", require("./src/en/lightnovelpub/LightNovelPub")); -app.use("/api/16/", require("./src/en/wuxiaworldco/WuxiaWorldCo")); -app.use("/api/17/", require("./src/en/tapread/TapRead")); -app.use("/api/18/", require("./src/en/novelupdatescc/NovelUpdatesCc")); -app.use("/api/19/", require("./src/en/readlightnovelcc/ReadLightNovelCc")); -app.use("/api/20/", require("./src/en/wuxiaworldcloud/WuxiaWorldCloud")); -app.use("/api/21/", require("./src/en/woopread/WoopRead")); -app.use("/api/22/", require("./src/en/foxaholic/Foxaholic")); -app.use("/api/27/", require("./src/en/comrademao/ComradeMao")); -app.use("/api/33/", require("./src/en/novelpassion/NovelPassion")); -app.use("/api/34/", require("./src/en/royalroad/RoyalRoad")); -app.use("/api/35/", require("./src/en/scribblehub/ScribbleHub")); -app.use("/api/37/", require("./src/en/lnmtl/LNMTL")); -app.use("/api/48/", require("./src/en/wuxiablog/WuxiaBlog")); -app.use("/api/49/", require("./src/en/wuxiacity/WuxiaCity")); -app.use("/api/77/", require("./src/en/wnmtl/WNMTL")); - -// Spanish -app.use("/api/23/", require("./src/es/tunovelaligera/Tunovelaligera")); -app.use("/api/24/", require("./src/es/skynovels/Skynovels")); -app.use("/api/25/", require("./src/es/einherjarproject/EinherjarProject")); -app.use("/api/26/", require("./src/es/novelasligera/NovelasLigera")); -app.use("/api/28/", require("./src/es/yuukitls/YuukiTls")); -app.use("/api/29/", require("./src/es/hasutl/Hasutl")); -app.use("/api/30/", require("./src/es/oasistranslations/OasisTranslations")); -app.use("/api/31/", require("./src/es/novelawuxia/NovelaWuxia")); - -// Japanese -app.use("/api/36", require("./src/jp/syosetu/Syosetu")); - -// Chinese -app.use("/api/50/", require("./src/ch/yushubo/Yushubo")); - -// French -app.use("/api/76/", require("./src/fr/noveldeglace/NovelDeGlace")); - -// MultiSrc -app.use("/api/", require("./multisrc/madara/MadaraGenerator")); -app.use("/api/", require("./multisrc/wpmangastream/WPMangaStreamGenerator")); - -const PORT = process.env.PORT || 5000; - -app.listen(PORT, () => console.log(`Server started on port ${PORT}`)); diff --git a/multisrc/madara/MadaraGenerator.js b/multisrc/madara/MadaraGenerator.js deleted file mode 100644 index 6aafe6b..0000000 --- a/multisrc/madara/MadaraGenerator.js +++ /dev/null @@ -1,132 +0,0 @@ -const express = require("express"); -const MadaraScraper = require("./MadaraScraper"); - -const router = express.Router(); - -const getPath = (extensionId) => { - const path = { - 38: { novels: "manga", novel: "manga", chapter: "manga" }, - 39: { novels: "series", novel: "series", chapter: "series" }, - 40: { novels: "novels", novel: "novel", chapter: "novel" }, - 41: { novels: "manga", novel: "manga", chapter: "manga" }, - 42: { novels: "all-novels", novel: "manga", chapter: "manga" }, - 43: { novels: "series", novel: "series", chapter: "series" }, - 44: { novels: "series", novel: "series", chapter: "series" }, - 45: { novels: "novel", novel: "novel", chapter: "novel" }, - 46: { novels: "novel", novel: "novel", chapter: "novel" }, - 47: { novels: "novel-list", novel: "novel", chapter: "novel" }, - }; - - return path[extensionId]; -}; - -const madaraSources = [ - { - extensionId: 38, - scraper: new MadaraScraper( - 38, - "https://skynovel.org/", - "SkyNovel", - getPath(38) - ), - }, - { - extensionId: 39, - scraper: new MadaraScraper( - 39, - "https://novelcake.com/", - "NovelCake", - getPath(39) - ), - }, - { - extensionId: 40, - scraper: new MadaraScraper( - 40, - "https://novelsrock.com/", - "NovelsRock", - getPath(40) - ), - }, - { - extensionId: 41, - scraper: new MadaraScraper( - 41, - "https://zinnovel.com/", - "ZinNovel", - getPath(41) - ), - }, - { - extensionId: 42, - scraper: new MadaraScraper( - 42, - "https://noveltranslate.com/", - "NovelTranslate", - getPath(42) - ), - }, - { - extensionId: 43, - scraper: new MadaraScraper( - 43, - "https://www.lunarletters.com/", - "LunarLetters", - getPath(43) - ), - }, - { - extensionId: 44, - scraper: new MadaraScraper( - 44, - "https://sleepytranslations.com/", - "SleepyTranslations", - getPath(44) - ), - }, - { - extensionId: 45, - scraper: new MadaraScraper( - 45, - "https://freenovel.me/", - "FreeNovelMe", - getPath(45) - ), - }, - { - extensionId: 46, - scraper: new MadaraScraper( - 46, - "https://1stkissnovel.love/", - "1stKissNovel", - getPath(46) - ), - }, - { - extensionId: 47, - scraper: new MadaraScraper( - 47, - "https://daonovel.com/", - "DaoNovel", - getPath(47) - ), - }, -]; - -madaraSources.map((source) => { - router.get(`/${source.extensionId}/novels/`, source.scraper.novelsScraper); - - router.get( - `/${source.extensionId}/novel/:novelUrl`, - source.scraper.novelScraper - ); - - router.get( - `/${source.extensionId}/novel/:novelUrl/:chapterUrl`, - source.scraper.chapterScraper - ); - - router.get(`/${source.extensionId}/search/`, source.scraper.searchScraper); -}); - -module.exports = router; diff --git a/multisrc/madara/icons/1stkissnovel.png b/multisrc/madara/icons/1stkissnovel.png deleted file mode 100644 index d79e154..0000000 Binary files a/multisrc/madara/icons/1stkissnovel.png and /dev/null differ diff --git a/multisrc/madara/icons/arnovel.png b/multisrc/madara/icons/arnovel.png deleted file mode 100644 index b721e43..0000000 Binary files a/multisrc/madara/icons/arnovel.png and /dev/null differ diff --git a/multisrc/madara/icons/boxnovel.online.png b/multisrc/madara/icons/boxnovel.online.png deleted file mode 100644 index 4b60647..0000000 Binary files a/multisrc/madara/icons/boxnovel.online.png and /dev/null differ diff --git a/multisrc/madara/icons/clicknovel.png b/multisrc/madara/icons/clicknovel.png deleted file mode 100644 index f2e1c57..0000000 Binary files a/multisrc/madara/icons/clicknovel.png and /dev/null differ diff --git a/multisrc/madara/icons/daonovel.png b/multisrc/madara/icons/daonovel.png deleted file mode 100644 index 07509db..0000000 Binary files a/multisrc/madara/icons/daonovel.png and /dev/null differ diff --git a/multisrc/madara/icons/freenovel.png b/multisrc/madara/icons/freenovel.png deleted file mode 100644 index d5d1b30..0000000 Binary files a/multisrc/madara/icons/freenovel.png and /dev/null differ diff --git a/multisrc/madara/icons/lightnovelheaven.png b/multisrc/madara/icons/lightnovelheaven.png deleted file mode 100644 index 493802a..0000000 Binary files a/multisrc/madara/icons/lightnovelheaven.png and /dev/null differ diff --git a/multisrc/madara/icons/lightnovelshub.png b/multisrc/madara/icons/lightnovelshub.png deleted file mode 100644 index 65afd7b..0000000 Binary files a/multisrc/madara/icons/lightnovelshub.png and /dev/null differ diff --git a/multisrc/madara/icons/lunarletters.png b/multisrc/madara/icons/lunarletters.png deleted file mode 100644 index cd20625..0000000 Binary files a/multisrc/madara/icons/lunarletters.png and /dev/null differ diff --git a/multisrc/madara/icons/meionovel.png b/multisrc/madara/icons/meionovel.png deleted file mode 100644 index 5cece64..0000000 Binary files a/multisrc/madara/icons/meionovel.png and /dev/null differ diff --git a/multisrc/madara/icons/mostnovel.png b/multisrc/madara/icons/mostnovel.png deleted file mode 100644 index 395dc99..0000000 Binary files a/multisrc/madara/icons/mostnovel.png and /dev/null differ diff --git a/multisrc/madara/icons/mysticalseries.png b/multisrc/madara/icons/mysticalseries.png deleted file mode 100644 index 7a4e261..0000000 Binary files a/multisrc/madara/icons/mysticalseries.png and /dev/null differ diff --git a/multisrc/madara/icons/novelcake.png b/multisrc/madara/icons/novelcake.png deleted file mode 100644 index 2d9805b..0000000 Binary files a/multisrc/madara/icons/novelcake.png and /dev/null differ diff --git a/multisrc/madara/icons/novelmultiverse.png b/multisrc/madara/icons/novelmultiverse.png deleted file mode 100644 index bc2f107..0000000 Binary files a/multisrc/madara/icons/novelmultiverse.png and /dev/null differ diff --git a/multisrc/madara/icons/novelsrock.png b/multisrc/madara/icons/novelsrock.png deleted file mode 100644 index 545d3c7..0000000 Binary files a/multisrc/madara/icons/novelsrock.png and /dev/null differ diff --git a/multisrc/madara/icons/noveltranslate.png b/multisrc/madara/icons/noveltranslate.png deleted file mode 100644 index 3a4e47c..0000000 Binary files a/multisrc/madara/icons/noveltranslate.png and /dev/null differ diff --git a/multisrc/madara/icons/readwebnovels.png b/multisrc/madara/icons/readwebnovels.png deleted file mode 100644 index 4db8a00..0000000 Binary files a/multisrc/madara/icons/readwebnovels.png and /dev/null differ diff --git a/multisrc/madara/icons/skynovel.png b/multisrc/madara/icons/skynovel.png deleted file mode 100644 index d0fc289..0000000 Binary files a/multisrc/madara/icons/skynovel.png and /dev/null differ diff --git a/multisrc/madara/icons/sleepytranslations.png b/multisrc/madara/icons/sleepytranslations.png deleted file mode 100644 index f112559..0000000 Binary files a/multisrc/madara/icons/sleepytranslations.png and /dev/null differ diff --git a/multisrc/madara/icons/wbnovel.png b/multisrc/madara/icons/wbnovel.png deleted file mode 100644 index 8337233..0000000 Binary files a/multisrc/madara/icons/wbnovel.png and /dev/null differ diff --git a/multisrc/madara/icons/webnovellover.png b/multisrc/madara/icons/webnovellover.png deleted file mode 100644 index 6bbb495..0000000 Binary files a/multisrc/madara/icons/webnovellover.png and /dev/null differ diff --git a/multisrc/madara/icons/wuxiaworldsite.png b/multisrc/madara/icons/wuxiaworldsite.png deleted file mode 100644 index a2382e9..0000000 Binary files a/multisrc/madara/icons/wuxiaworldsite.png and /dev/null differ diff --git a/multisrc/madara/icons/zinnovel.png b/multisrc/madara/icons/zinnovel.png deleted file mode 100644 index 8cc36f7..0000000 Binary files a/multisrc/madara/icons/zinnovel.png and /dev/null differ diff --git a/multisrc/wpmangastream/WPMangaStreamGenerator.js b/multisrc/wpmangastream/WPMangaStreamGenerator.js deleted file mode 100644 index 4e10c5b..0000000 --- a/multisrc/wpmangastream/WPMangaStreamGenerator.js +++ /dev/null @@ -1,41 +0,0 @@ -const express = require("express"); -const WPMangaStreamScraper = require("./WPMangaStreamScraper"); - -const router = express.Router(); - -const madaraSources = [ - { - extensionId: 53, - scraper: new WPMangaStreamScraper( - 53, - "https://kolnovel.com/", - "KolNovel" - ), - }, - { - extensionId: 54, - scraper: new WPMangaStreamScraper( - 54, - "https://rewayat-ar.site/", - "Rewayat-Ar" - ), - }, -]; - -madaraSources.map((source) => { - router.get(`/${source.extensionId}/novels/`, source.scraper.novelsScraper); - - router.get( - `/${source.extensionId}/novel/:novelUrl`, - source.scraper.novelScraper - ); - - router.get( - `/${source.extensionId}/novel/:novelUrl/:chapterUrl`, - source.scraper.chapterScraper - ); - - router.get(`/${source.extensionId}/search/`, source.scraper.searchScraper); -}); - -module.exports = router; diff --git a/multisrc/wpmangastream/icons/kolnovel.png b/multisrc/wpmangastream/icons/kolnovel.png deleted file mode 100644 index 4d66909..0000000 Binary files a/multisrc/wpmangastream/icons/kolnovel.png and /dev/null differ diff --git a/multisrc/wpmangastream/icons/rewayatar.png b/multisrc/wpmangastream/icons/rewayatar.png deleted file mode 100644 index a7d3a1c..0000000 Binary files a/multisrc/wpmangastream/icons/rewayatar.png and /dev/null differ diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f841cc1..0000000 --- a/package-lock.json +++ /dev/null @@ -1,5560 +0,0 @@ -{ - "name": "lnreader-extensions", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "lnreader-extensions", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "cheerio": "^1.0.0-rc.5", - "chrome-aws-lambda": "^8.0.2", - "express": "^4.17.1", - "form-data": "^4.0.0", - "html-to-text": "^7.1.1", - "node-fetch": "^2.6.1", - "puppeteer": "^9.0.0", - "request": "^2.88.2", - "user-agents": "^1.0.654" - }, - "devDependencies": { - "nodemon": "^2.0.7" - } - }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@types/node": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", - "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==", - "optional": true - }, - "node_modules/@types/yauzl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", - "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, - "dependencies": { - "string-width": "^3.0.0" - } - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "node_modules/boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dev": true, - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "engines": { - "node": "*" - } - }, - "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", - "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", - "dependencies": { - "cheerio-select": "^1.5.0", - "dom-serializer": "^1.3.2", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", - "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", - "dependencies": { - "css-select": "^4.1.3", - "css-what": "^5.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0", - "domutils": "^2.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/chrome-aws-lambda": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/chrome-aws-lambda/-/chrome-aws-lambda-8.0.2.tgz", - "integrity": "sha512-a4VEX6DknxISPqGLksZa/JuEnjc4nLXaG0BMp5c9qERvJ/eBHPUJ7oCRQQC+SxSxuxsEwxbBj2RBlCQbivnihg==", - "dependencies": { - "lambdafs": "^2.0.3" - }, - "engines": { - "node": ">= 10.16" - }, - "peerDependencies": { - "puppeteer-core": "^8.0.0" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/css-select": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", - "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^5.0.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0", - "nth-check": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz", - "integrity": "sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/detect-indent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz", - "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/devtools-protocol": { - "version": "0.0.869402", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", - "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==" - }, - "node_modules/docopt": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/docopt/-/docopt-0.6.2.tgz", - "integrity": "sha1-so6eIiDaXsSffqW7JKR3h0Be6xE=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", - "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-json": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/dot-json/-/dot-json-1.2.2.tgz", - "integrity": "sha512-AKL+GsO4wSEU4LU+fAk/PqN4nQ6PB1vT3HpMiZous9xCzK5S0kh4DzfUY0EfU67jsIXLlu0ty71659N9Nmg+Tw==", - "dependencies": { - "detect-indent": "~6.0.0", - "docopt": "~0.6.2", - "underscore-keypath": "~0.0.22" - }, - "bin": { - "dot-json": "bin/dot-json.js" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/extract-zip/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "dev": true, - "dependencies": { - "ini": "1.3.7" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-to-text": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-7.1.1.tgz", - "integrity": "sha512-c9QWysrfnRZevVpS8MlE7PyOdSuIOjg8Bt8ZE10jMU/BEngA6j3llj4GRfAmtQzcd1FjKE0sWu5IHXRUH9YxIQ==", - "dependencies": { - "deepmerge": "^4.2.2", - "he": "^1.2.0", - "htmlparser2": "^6.1.0", - "minimist": "^1.2.5" - }, - "bin": { - "html-to-text": "bin/cli.js" - }, - "engines": { - "node": ">=10.23.2" - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, - "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", - "dev": true - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, - "dependencies": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "node_modules/is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/lambdafs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lambdafs/-/lambdafs-2.0.3.tgz", - "integrity": "sha512-5YWwZA/QKk09GdfcJ/ABVO+bpFoGlnTBa5jmyM8Kt9yIzl2lDDVBPLK+Aenq2UEcuDpqxXIYI5zLB7VZNepTrg==", - "bundleDependencies": [ - "tar-fs" - ], - "dependencies": { - "tar-fs": "^2.1.1" - }, - "bin": { - "lambdafs": "bin/brotli.js" - }, - "engines": { - "node": ">= 10.16" - } - }, - "node_modules/lambdafs/node_modules/base64-js": { - "version": "1.5.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/lambdafs/node_modules/bl": { - "version": "4.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/lambdafs/node_modules/bl/node_modules/readable-stream": { - "version": "3.6.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/lambdafs/node_modules/buffer": { - "version": "5.7.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/lambdafs/node_modules/chownr": { - "version": "1.1.4", - "inBundle": true, - "license": "ISC" - }, - "node_modules/lambdafs/node_modules/end-of-stream": { - "version": "1.4.4", - "inBundle": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/lambdafs/node_modules/fs-constants": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/lambdafs/node_modules/ieee754": { - "version": "1.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "BSD-3-Clause" - }, - "node_modules/lambdafs/node_modules/inherits": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC" - }, - "node_modules/lambdafs/node_modules/mkdirp-classic": { - "version": "0.5.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/lambdafs/node_modules/once": { - "version": "1.4.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/lambdafs/node_modules/pump": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/lambdafs/node_modules/string_decoder": { - "version": "1.1.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/lambdafs/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/lambdafs/node_modules/tar-fs": { - "version": "2.1.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/lambdafs/node_modules/tar-stream": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lambdafs/node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/lambdafs/node_modules/util-deprecate": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/lambdafs/node_modules/wrappy": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC" - }, - "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "dependencies": { - "package-json": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "dependencies": { - "mime-db": "1.44.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/nodemon": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", - "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.3", - "update-notifier": "^4.1.0" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/nodemon/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "dependencies": { - "escape-goat": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/puppeteer": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-9.0.0.tgz", - "integrity": "sha512-Avu8SKWQRC1JKNMgfpH7d4KzzHOL/A65jRYrjNU46hxnOYGwqe4zZp/JW8qulaH0Pnbm5qyO3EbSKvqBUlfvkg==", - "hasInstallScript": true, - "dependencies": { - "debug": "^4.1.0", - "devtools-protocol": "0.0.869402", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", - "pkg-dir": "^4.2.0", - "progress": "^2.0.1", - "proxy-from-env": "^1.1.0", - "rimraf": "^3.0.2", - "tar-fs": "^2.0.0", - "unbzip2-stream": "^1.3.3", - "ws": "^7.2.3" - }, - "engines": { - "node": ">=10.18.1" - } - }, - "node_modules/puppeteer/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "dependencies": { - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/string-width/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "node_modules/undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "dev": true, - "dependencies": { - "debug": "^2.2.0" - } - }, - "node_modules/underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" - }, - "node_modules/underscore-keypath": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/underscore-keypath/-/underscore-keypath-0.0.22.tgz", - "integrity": "sha1-SKUoOSu278QkvhyqVtpLX6zPJk0=", - "dependencies": { - "underscore": "*" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "dev": true, - "dependencies": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/user-agents": { - "version": "1.0.654", - "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.654.tgz", - "integrity": "sha512-auE1AmkOkjYbZvMFbakf3Je4dr/xEdjXCsJXOzz0GiUXKwScOt+/hp1s9gutRPgJnPFq4xCGM0LrfWaVWIGBAQ==", - "dependencies": { - "dot-json": "^1.2.2", - "lodash.clonedeep": "^4.5.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - }, - "dependencies": { - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/node": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", - "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==", - "optional": true - }, - "@types/yauzl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", - "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", - "optional": true, - "requires": { - "@types/node": "*" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, - "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } - } - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "cheerio": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", - "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", - "requires": { - "cheerio-select": "^1.5.0", - "dom-serializer": "^1.3.2", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" - } - }, - "cheerio-select": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", - "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", - "requires": { - "css-select": "^4.1.3", - "css-what": "^5.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0", - "domutils": "^2.7.0" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "chrome-aws-lambda": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/chrome-aws-lambda/-/chrome-aws-lambda-8.0.2.tgz", - "integrity": "sha512-a4VEX6DknxISPqGLksZa/JuEnjc4nLXaG0BMp5c9qERvJ/eBHPUJ7oCRQQC+SxSxuxsEwxbBj2RBlCQbivnihg==", - "requires": { - "lambdafs": "^2.0.3" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, - "css-select": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", - "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^5.0.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0", - "nth-check": "^2.0.0" - } - }, - "css-what": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz", - "integrity": "sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "detect-indent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz", - "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==" - }, - "devtools-protocol": { - "version": "0.0.869402", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", - "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==" - }, - "docopt": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/docopt/-/docopt-0.6.2.tgz", - "integrity": "sha1-so6eIiDaXsSffqW7JKR3h0Be6xE=" - }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" - }, - "domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", - "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dot-json": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/dot-json/-/dot-json-1.2.2.tgz", - "integrity": "sha512-AKL+GsO4wSEU4LU+fAk/PqN4nQ6PB1vT3HpMiZous9xCzK5S0kh4DzfUY0EfU67jsIXLlu0ty71659N9Nmg+Tw==", - "requires": { - "detect-indent": "~6.0.0", - "docopt": "~0.6.2", - "underscore-keypath": "~0.0.22" - } - }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" - }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "requires": { - "pend": "~1.2.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "dev": true, - "requires": { - "ini": "1.3.7" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, - "html-to-text": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-7.1.1.tgz", - "integrity": "sha512-c9QWysrfnRZevVpS8MlE7PyOdSuIOjg8Bt8ZE10jMU/BEngA6j3llj4GRfAmtQzcd1FjKE0sWu5IHXRUH9YxIQ==", - "requires": { - "deepmerge": "^4.2.2", - "he": "^1.2.0", - "htmlparser2": "^6.1.0", - "minimist": "^1.2.5" - } - }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "requires": { - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "lambdafs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lambdafs/-/lambdafs-2.0.3.tgz", - "integrity": "sha512-5YWwZA/QKk09GdfcJ/ABVO+bpFoGlnTBa5jmyM8Kt9yIzl2lDDVBPLK+Aenq2UEcuDpqxXIYI5zLB7VZNepTrg==", - "requires": { - "tar-fs": "^2.1.1" - }, - "dependencies": { - "base64-js": { - "version": "1.5.1", - "bundled": true - }, - "bl": { - "version": "4.1.0", - "bundled": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "bundled": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "buffer": { - "version": "5.7.1", - "bundled": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "chownr": { - "version": "1.1.4", - "bundled": true - }, - "end-of-stream": { - "version": "1.4.4", - "bundled": true, - "requires": { - "once": "^1.4.0" - } - }, - "fs-constants": { - "version": "1.0.0", - "bundled": true - }, - "ieee754": { - "version": "1.2.1", - "bundled": true - }, - "inherits": { - "version": "2.0.4", - "bundled": true - }, - "mkdirp-classic": { - "version": "0.5.3", - "bundled": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1" - } - }, - "pump": { - "version": "3.0.0", - "bundled": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "bundled": true - } - } - }, - "tar-fs": { - "version": "2.1.1", - "bundled": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "tar-stream": { - "version": "2.2.0", - "bundled": true, - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "bundled": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - } - } - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "requires": { - "package-json": "^6.3.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "nodemon": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", - "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", - "dev": true, - "requires": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.3", - "update-notifier": "^4.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true - }, - "nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", - "requires": { - "boolbase": "^1.0.0" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "requires": { - "parse5": "^6.0.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "requires": { - "find-up": "^4.0.0" - } - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "requires": { - "escape-goat": "^2.0.0" - } - }, - "puppeteer": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-9.0.0.tgz", - "integrity": "sha512-Avu8SKWQRC1JKNMgfpH7d4KzzHOL/A65jRYrjNU46hxnOYGwqe4zZp/JW8qulaH0Pnbm5qyO3EbSKvqBUlfvkg==", - "requires": { - "debug": "^4.1.0", - "devtools-protocol": "0.0.869402", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", - "pkg-dir": "^4.2.0", - "progress": "^2.0.1", - "proxy-from-env": "^1.1.0", - "rimraf": "^3.0.2", - "tar-fs": "^2.0.0", - "unbzip2-stream": "^1.3.3", - "ws": "^7.2.3" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - } - } - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "requires": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "dev": true, - "requires": { - "debug": "^2.2.0" - } - }, - "underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" - }, - "underscore-keypath": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/underscore-keypath/-/underscore-keypath-0.0.22.tgz", - "integrity": "sha1-SKUoOSu278QkvhyqVtpLX6zPJk0=", - "requires": { - "underscore": "*" - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "dev": true, - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - } - }, - "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, - "user-agents": { - "version": "1.0.654", - "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.654.tgz", - "integrity": "sha512-auE1AmkOkjYbZvMFbakf3Je4dr/xEdjXCsJXOzz0GiUXKwScOt+/hp1s9gutRPgJnPFq4xCGM0LrfWaVWIGBAQ==", - "requires": { - "dot-json": "^1.2.2", - "lodash.clonedeep": "^4.5.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "requires": {} - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index a0ee5d6..0000000 --- a/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "lnreader-extensions", - "version": "1.0.0", - "description": "Extensions for LNReader", - "main": "index.js", - "scripts": { - "start": "node index.js", - "server": "nodemon index.js" - }, - "author": "Rajarshee Chatterjee", - "license": "MIT", - "dependencies": { - "cheerio": "^1.0.0-rc.5", - "chrome-aws-lambda": "^8.0.2", - "express": "^4.17.1", - "form-data": "^4.0.0", - "html-to-text": "^7.1.1", - "node-fetch": "^2.6.1", - "puppeteer": "^9.0.0", - "request": "^2.88.2", - "user-agents": "^1.0.654" - }, - "devDependencies": { - "nodemon": "^2.0.7" - } -} diff --git a/sources.js b/sources.js deleted file mode 100644 index c71a600..0000000 --- a/sources.js +++ /dev/null @@ -1,471 +0,0 @@ -extensions = [ - /** - * English - */ - - { - sourceId: 1, - sourceUrl: "https://boxnovel.com/", - sourceName: "BoxNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/boxnovel/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 2, - sourceUrl: "https://www.readlightnovel.org/", - sourceName: "ReadLightNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/readlightnovel/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 3, - sourceUrl: "https://fastnovel.net/", - sourceName: "FastNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/fastnovel/icon.png?raw=true", - sourceLanguage: "English", - status: 0, - }, - { - sourceId: 4, - sourceUrl: "https://readnovelfull.com/", - sourceName: "ReadNovelFull", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/readnovelfull/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 5, - sourceUrl: "https://www.mtlnovel.com/", - sourceName: "MTLNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/mtlnovel/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 6, - sourceUrl: "https://www.novelhall.com/", - sourceName: "NovelHall", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/novelhall/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 7, - sourceUrl: "https://www.wuxiaworld.com/", - sourceName: "WuxiaWorld", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/wuxiaworld/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 8, - sourceUrl: "https://novelfull.com/", - sourceName: "NovelFull", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/novelfull/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 9, - sourceUrl: "https://noveltrench.com/", - sourceName: "NovelTrench", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/noveltrench/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 10, - sourceUrl: "https://vipnovel.com/", - sourceName: "VipNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/vipnovel/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 11, - sourceUrl: "https://kisslightnovels.info/", - sourceName: "KissLightNovels", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/kisslightnovels/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 12, - sourceUrl: "https://wuxiaworldsite.co/", - sourceName: "WuxiaWorldSite", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/wuxiaworldsite/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 13, - sourceUrl: "https://freewebnovel.com/", - sourceName: "FreeWebNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/freewebnovel/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 14, - sourceUrl: "https://jpmtl.com/", - sourceName: "JPMTL", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/jpmtl/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 15, - sourceUrl: "https://www.lightnovelpub.com/", - sourceName: "LightNovelPub", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/lightnovelpub/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 16, - sourceUrl: "https://www.wuxiaworld.co/", - sourceName: "WuxiaWorld.co", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/wuxiaworldco/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 17, - sourceUrl: "http://www.tapread.com/", - sourceName: "TapRead", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/tapread/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 18, - sourceUrl: "https://www.novelupdates.cc/", - sourceName: "NovelUpdates.cc", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/novelupdatescc/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 19, - sourceUrl: "https://m.readlightnovel.cc/", - sourceName: "ReadLightNovel.cc", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/readlightnovelcc/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 20, - sourceUrl: "http://wuxiaworld.cloud/", - sourceName: "WuxiaWorld.cloud", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/wuxiaworldcloud/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 21, - sourceUrl: "https://woopread.com/", - sourceName: "WoopRead", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/woopread/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 22, - sourceUrl: "https://foxaholic.com/", - sourceName: "Foxaholic", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/foxaholic/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 27, - sourceUrl: "https://comrademao.com/", - sourceName: "ComradeMao", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/comrademao/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 33, - sourceUrl: "https://www.novelpassion.com/", - sourceName: "NovelPassion", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/novelpassion/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 34, - sourceUrl: "https://www.royalroad.com/", - sourceName: "Royal Road", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/royalroad/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 35, - sourceUrl: "https://www.scribblehub.com/", - sourceName: "Scribble Hub", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/scribblehub/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 37, - sourceUrl: "https://lnmtl.com/", - sourceName: "LNMTL", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/lnmtl/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 38, - sourceUrl: "https://skynovel.org/", - sourceName: "SkyNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/skynovel.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 39, - sourceUrl: "https://novelcake.com/", - sourceName: "NovelCake", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/novelcake.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 40, - sourceUrl: "https://novelsrock.com/", - sourceName: "NovelsRock", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/novelsrock.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 41, - sourceUrl: "https://zinnovel.com/", - sourceName: "ZinNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/zinnovel.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 42, - sourceUrl: "https://noveltranslate.com/", - sourceName: "NovelTranslate", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/noveltranslate.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 43, - sourceUrl: "https://www.lunarletters.com/", - sourceName: "LunarLetters", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/lunarletters.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 44, - sourceUrl: "https://sleepytranslations.com/", - sourceName: "SleepyTranslations", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/sleepytranslations.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 45, - sourceUrl: "https://freenovel.me/", - sourceName: "FreeNovelMe", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/freenovel.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 46, - sourceUrl: "https://1stkissnovel.love/", - sourceName: "1stKissNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/1stkissnovel.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 47, - sourceUrl: "https://daonovel.com/", - sourceName: "DaoNovel", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/multisrc/madara/icons/daonovel.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 48, - sourceUrl: "https://www.wuxia.blog/", - sourceName: "Wuxia.Blog", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/wuxiablog/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - { - sourceId: 49, - sourceUrl: "https://wuxia.city/", - sourceName: "Wuxia.City", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/wuxiacity/icon.png?raw=true", - sourceLanguage: "English", - status: 1, - }, - - /** - * Spanish - */ - - { - sourceId: 23, - sourceUrl: "https://tunovelaligera.com/", - sourceName: "Tunovelaligera", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/es/tunovelaligera/icon.png?raw=true", - sourceLanguage: "Spanish", - status: 1, - }, - { - sourceId: 24, - sourceUrl: "https://www.skynovels.net/", - sourceName: "SkyNovels", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/es/skynovels/icon.png?raw=true", - sourceLanguage: "Spanish", - status: 1, - }, - { - sourceId: 25, - sourceUrl: "https://einherjarproject.net/", - sourceName: "EinherjarProject", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/es/einherjarproject/icon.png?raw=true", - sourceLanguage: "Spanish", - status: 1, - }, - { - sourceId: 26, - sourceUrl: "https://novelasligera.com/", - sourceName: "NovelasLigera", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/es/novelasligera/icon.png?raw=true", - sourceLanguage: "Spanish", - status: 1, - }, - { - sourceId: 28, - sourceUrl: "https://yuukitls.com/", - sourceName: "YuukiTls", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/es/yuukitls/icon.png?raw=true", - sourceLanguage: "Spanish", - status: 1, - }, - { - sourceId: 29, - sourceUrl: "https://hasutl.wordpress.com/", - sourceName: "Hasu Translations", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/es/hasutl/icon.png?raw=true", - sourceLanguage: "Spanish", - status: 1, - }, - { - sourceId: 30, - sourceUrl: "https://oasistranslations.wordpress.com/", - sourceName: "Oasis Translations", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/es/oasistranslations/icon.png?raw=true", - sourceLanguage: "Spanish", - status: 1, - }, - { - sourceId: 31, - sourceUrl: "http://www.novelawuxia.com/", - sourceName: "Novela Wuxia", - sourceCover: - "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/es/novelawuxia/icon.png?raw=true", - sourceLanguage: "Spanish", - status: 1, - }, - - /** - * Japanese - */ - { - sourceId: 36, - sourceUrl: "https://syosetsu.com", - sourceName: "Syosetsu", - sourceCover: - "https://github.com/skillgg/lnreader-sources/blob/main/src/jp/syosetu/icon.png?raw=true", - sourceLanguage: "Japanese", - status: 1, - }, - - /** - * Arabic - */ - { - sourceId: 53, - sourceUrl: "https://kolnovel.com/", - sourceName: "KolNovel", - sourceCover: - "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/wpmangastream/icons/kolnovel.png?raw=true", - sourceLanguage: "Arabic", - status: 1, - }, - { - sourceId: 54, - sourceUrl: "https://rewayat-ar.site/", - sourceName: "Rewayat-Ar", - sourceCover: - "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/wpmangastream/icons/rewayatar.png?raw=true", - sourceLanguage: "Arabic", - status: 1, - }, -]; - -module.exports = extensions; diff --git a/src/ar/arnovel.js b/src/ar/arnovel.js new file mode 100644 index 0000000..1a74eb9 --- /dev/null +++ b/src/ar/arnovel.js @@ -0,0 +1,186 @@ +import cheerio from "react-native-cheerio"; + +const sourceId = 59; +const sourceName = "ArNovel"; + +const baseUrl = "https://arnovel.me/"; + +const popularNovels = async (page) => { + let totalPages = 21; + let url = baseUrl + "novels/page/" + page; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".page-item-detail").each(function () { + const novelName = $(this).find(".h5 > a").text(); + const novelCover = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + + let novelUrl = $(this).find(".h5 > a").attr("href").split("/")[4]; + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}novel/${novelUrl}`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url, novelUrl }; + + novel.novelName = $(".post-title > h1") + .text() + .replace(/[\t\n]/g, "") + .trim(); + + novel.novelCover = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + + $(".post-content_item").each(function () { + detailName = $(this) + .find(".summary-heading > h5") + .text() + .replace(/[\t\n]/g, "") + .trim(); + detail = $(this) + .find(".summary-content") + .text() + .replace(/[\t\n]/g, "") + .trim(); + + switch (detailName) { + case "التصنيفات": + novel.genre = detail.trim().replace(/[\t\n]/g, ","); + break; + case "المؤلف (ين)": + novel.author = detail.trim(); + break; + case "الحالة": + novel.status = detail.trim(); + break; + } + }); + + $(".description-summary > div.summary__content").find("em").remove(); + + novel.summary = $(".description-summary > div.summary__content") + .text() + .trim(); + + let novelChapters = []; + + const novelId = $("#manga-chapters-holder").attr("data-id"); + + let formData = new FormData(); + formData.append("action", "manga_get_chapters"); + formData.append("manga", novelId); + + const data = await fetch("https://www.arnovel.me/wp-admin/admin-ajax.php", { + method: "POST", + body: formData, + }); + const text = await data.text(); + + $ = cheerio.load(text); + + $(".wp-manga-chapter").each(function () { + chapterName = $(this) + .find("a") + .text() + .replace(/[\t\n]/g, "") + .trim(); + + releaseDate = $(this).find("span").text().trim(); + + chapterUrl = $(this).find("a").attr("href").split("/"); + + chapterUrl[6] + ? (chapterUrl = chapterUrl[5] + "/" + chapterUrl[6]) + : (chapterUrl = chapterUrl[5]); + + novelChapters.push({ chapterName, releaseDate, chapterUrl }); + }); + + novel.chapters = novelChapters.reverse(); + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}novel/${novelUrl}/${chapterUrl}/`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + const chapterName = $("h1#chapter-heading").text(); + const chapterText = $(".reading-content").html(); + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".c-tabs-item__content").each(function () { + const novelName = $(this).find(".h4 > a").text(); + const novelCover = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + + let novelUrl = $(this).find(".h4 > a").attr("href").split("/")[4]; + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const ArNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default ArNovelScraper; diff --git a/src/ch/yushubo.js b/src/ch/yushubo.js new file mode 100644 index 0000000..440d358 --- /dev/null +++ b/src/ch/yushubo.js @@ -0,0 +1,183 @@ +import cheerio from "react-native-cheerio"; + +const baseUrl = "https://m.yushubo.com"; + +const popularNovels = async (page) => { + const url = `${baseUrl}/all/order/hits_week+desc.html?page=${page}`; + const totalPages = 5846; + + const result = await fetch(url, { + headers: { + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".clearfix > li").each(function (result) { + const novelUrl = + $(this).find("a").attr("href").replace(".html", "").substring(1) + + "/"; + + const novelName = $(this).find("a").text(); + let novelCover = $(this).find("img").attr("src"); + novelCover = novelCover.includes("https://tva1.sinaimg.cn/") + ? `${novelCover}` + : `${baseUrl}${novelCover}`; + + const novel = { + sourceId: 52, + novelUrl, + novelName, + novelCover, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}/${novelUrl.slice(0, -1)}.html`; + + const result = await fetch(url, { + headers: { + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = {}; + + novel.sourceId = 52; + + novel.sourceName = "Yushubo"; + + novel.url = url; + + novel.novelUrl = novelUrl; + + novel.novelName = $("dd > h2").text(); + + novel.novelCover = $("dt > img").attr("src"); + + novel.summary = $("div.content > p").text(); + + let info = []; + $(".info a").each(function (result) { + info.push($(this).text()); + }); + + novel.genre = info[0]; + + novel.author = info[1]; + + novel.artist = null; + + novel.status = "Unknown"; + + let chapters = []; + + $(".bookshelf-list a").each(function (result) { + let chapterUrl = $(this).attr("href").substring(1); + let chapterName = $(this).attr("title"); + let releaseDate = null; + + chapters.push({ + chapterName, + releaseDate, + chapterUrl, + }); + }); + + novel.chapters = chapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}/${chapterUrl}`; + + const result = await fetch(url, { + headers: { + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + const chapterName = $(".read-section h3").text(); + let chapterText = $(".read-section").html(); + + const chapter = { + sourceId: 52, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const searchUrl = `${baseUrl}/search.html`; + + const result = await fetch(searchUrl, { + method: "post", + body: JSON.stringify({ keyword: searchTerm }), + headers: { + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + "Content-Type": "application/json", + }, + }); + + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".clearfix > li").each(function (result) { + let novelUrl = + $(this).find("a").attr("href").replace(".html", "").substring(1) + + "/"; + + const novelName = $(this).find("a").text(); + let novelCover = $(this).find("img").attr("src"); + novelCover = novelCover.includes("https://tva1.sinaimg.cn/") + ? `${novelCover}` + : `${baseUrl}${novelCover}`; + + const novel = { + sourceId: 52, + novelUrl, + novelName, + novelCover, + }; + + novels.push(novel); + }); + + return novels; +}; + +const YushuboScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default YushuboScraper; diff --git a/src/ch/yushubo/Yushubo.js b/src/ch/yushubo/Yushubo.js deleted file mode 100644 index aa6ce88..0000000 --- a/src/ch/yushubo/Yushubo.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const YushuboScraper = require("./YushuboScraper"); - -const router = express.Router(); - -router.get("/novels/", YushuboScraper.novelsScraper); - -router.get("/novel/:novelUrl", YushuboScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", YushuboScraper.chapterScraper); - -router.get("/search/", YushuboScraper.searchScraper); - -module.exports = router; diff --git a/src/ch/yushubo/YushuboScraper.js b/src/ch/yushubo/YushuboScraper.js deleted file mode 100644 index 94ff359..0000000 --- a/src/ch/yushubo/YushuboScraper.js +++ /dev/null @@ -1,177 +0,0 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const UserAgent = require("user-agents"); - -const userAgent = new UserAgent(); - -const baseUrl = "https://m.yushubo.com"; - -const novelsScraper = async (req, res) => { - const url = `${baseUrl}/all/order/hits_week+desc.html`; - - const result = await fetch(url, { headers: { "User-Agent": userAgent } }); - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $(".clearfix > li").each(function (result) { - const novelUrl = - $(this).find("a").attr("href").replace(".html", "").substring(1) + "/"; - - const novelName = $(this).find("a").text(); - const novelCover = $(this).find("img").attr("src"); - - const novel = { - extensionId: 50, - novelUrl, - novelName, - novelCover, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}/${novelUrl}.html`; - - const result = await fetch(url, { headers: { "User-Agent": userAgent } }); - const body = await result.text(); - - $ = cheerio.load(body); - - let novel = {}; - - novel.extensionId = 50; - - novel.sourceName = "Yushubo"; - - novel.sourceUrl = url; - - novel.novelUrl = novelUrl + "/"; - - novel.novelName = $("dd > h2").text(); - - novel.novelCover = $("dt > img").attr("src"); - - novel.novelSummary = $("div.content > p").text(); - - let info = []; - $(".info a").each(function (result) { - info.push($(this).text()); - }); - - novel["Author(s)"] = info[0]; - - novel["Genre(s)"] = info[1]; - - novel["Artist(s)"] = null; - - novel.Status = "Unknown"; - - let chapters = []; - - $(".bookshelf-list a").each(function (result) { - let chapterUrl = $(this).attr("href").substring(1); - let chapterName = $(this).attr("title"); - let releaseDate = null; - - chapters.push({ - chapterName, - releaseDate, - chapterUrl, - }); - }); - - novel.novelChapters = chapters; - - res.json(novel); -}; - -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl.replace(".html/", ""); - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}/${chapterUrl}`; - - const result = await fetch(url, { headers: { "User-Agent": userAgent } }); - const body = await result.text(); - - $ = cheerio.load(body); - - const chapterName = $(".read-section h3").text(); - const chapterText = $(".read-section p").text(); - - let nextChapter = null; - if ($(".btn-next").attr("href")) { - nextChapter = $(".btn-prev").attr("href"); - } - - let prevChapter = null; - if ($(".btn-prev").attr("href")) { - prevChapter = $(".btn-prev").attr("href"); - } - - const chapter = { - extensionId: 50, - novelUrl, - chapterUrl, - chapterName, - chapterText, - nextChapter, - prevChapter, - }; - - res.json(chapter); -}; - -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - const searchUrl = `${baseUrl}/search.html`; - - const result = await fetch(searchUrl, { - method: "post", - body: JSON.stringify({ keyword: searchTerm }), - headers: { "User-Agent": userAgent, "Content-Type": "application/json" }, - }); - - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $(".clearfix > li").each(function (result) { - let novelUrl = - $(this).find("a").attr("href").replace(".html", "").substring(1) + "/"; - - const novelName = $(this).find("a").text(); - let novelCover = $(this).find("img").attr("src"); - novelCover = novelCover.startsWith("https://tva1.sinaimg.cn/") - ? `${novelCover}` - : `${baseUrl}${novelCover}`; - - const novel = { - extensionId: 50, - novelUrl, - novelName, - novelCover, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -module.exports = YushuboScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, -}; diff --git a/src/ch/yushubo/icon.png b/src/ch/yushubo/icon.png deleted file mode 100644 index 8b41c28..0000000 Binary files a/src/ch/yushubo/icon.png and /dev/null differ diff --git a/src/coverNotAvailable.jpg b/src/coverNotAvailable.jpg deleted file mode 100644 index 6b0ad16..0000000 Binary files a/src/coverNotAvailable.jpg and /dev/null differ diff --git a/src/en/boxnovel/BoxNovelScraper.js b/src/en/boxnovel.js similarity index 58% rename from src/en/boxnovel/BoxNovelScraper.js rename to src/en/boxnovel.js index 0ca5b11..941c7db 100644 --- a/src/en/boxnovel/BoxNovelScraper.js +++ b/src/en/boxnovel.js @@ -1,15 +1,13 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://boxnovel.com/novel"; const searchUrl = "https://boxnovel.com/"; -const novelsScraper = async (req, res) => { - const orderBy = req.params.o; - const pageNo = req.params.pageNo; +const sourceId = 1; - let url = `${baseUrl}/page/1/?m_orderby=${orderBy}`; +const popularNovels = async (page) => { + let url = `${baseUrl}/page/${page}/?m_orderby=rating`; + let totalPages = 73; const result = await fetch(url); const body = await result.text(); @@ -26,7 +24,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}/`, ""); const novel = { - extensionId: 1, + sourceId, novelName, novelCover, novelUrl, @@ -35,12 +33,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}/${novelUrl}/`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}/${novelUrl}`; console.log(url); @@ -53,13 +50,13 @@ const novelScraper = async (req, res) => { $(".post-title > h3 > span").remove(); - novel.extensionId = 1; + novel.sourceId = 1; novel.sourceName = "BoxNovel"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".post-title > h3") .text() @@ -80,44 +77,53 @@ const novelScraper = async (req, res) => { .replace(/[\t\n]/g, "") .trim(); - novel[detailName] = detail; + switch (detailName) { + case "Genre(s)": + novel.genre = detail.trim().replace(/[\t\n]/g, ","); + break; + case "Author(s)": + novel.author = detail.trim(); + break; + case "Artist(s)": + novel.status = detail.trim(); + break; + } }); $(".description-summary > div.summary__content").find("em").remove(); - novel.novelSummary = $(".description-summary > div.summary__content > div") + novel.summary = $(".description-summary > div.summary__content") .text() - .replace(/[\t\n]/g, ""); + .replace(/[\t\n]|Description|Summary/g, ""); let novelChapters = []; $(".wp-manga-chapter").each(function (result) { - chapterName = $(this) + const chapterName = $(this) .find("a") .text() .replace(/[\t\n]/g, "") .trim(); - releaseDate = $(this) + const releaseDate = $(this) .find("span") .text() .replace(/[\t\n]/g, "") .trim(); - chapterUrl = $(this).find("a").attr("href").replace(url, ""); + const chapterUrl = $(this).find("a").attr("href").replace(url, ""); - novelChapters.push({ chapterName, releaseDate, chapterUrl }); + const chapter = { chapterName, releaseDate, chapterUrl }; + + novelChapters.push(chapter); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -127,47 +133,19 @@ const chapterScraper = async (req, res) => { const chapterName = $("div.text-left > h3").text(); let chapterText = $(".reading-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($(".nav-next").length) { - nextChapter = $(".nav-next") - .find("a") - .attr("href") - .replace(baseUrl + "/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($(".nav-previous").length) { - prevChapter = $(".nav-previous") - .find("a") - .attr("href") - .replace(baseUrl + "/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - const chapter = { - extensionId: 1, + sourceId, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - const orderBy = req.query.o; - - const url = `${searchUrl}?s=${searchTerm}&post_type=wp-manga&m_orderby=${orderBy}`; +const searchNovels = async (searchTerm) => { + const url = `${searchUrl}?s=${searchTerm}&post_type=wp-manga&m_orderby=rating`; const result = await fetch(url); const body = await result.text(); @@ -184,7 +162,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}/`, ""); const novel = { - extensionId: 1, + sourceId, novelName, novelCover, novelUrl, @@ -193,12 +171,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = boxNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const BoxNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default BoxNovelScraper; diff --git a/src/en/boxnovel/BoxNovel.js b/src/en/boxnovel/BoxNovel.js deleted file mode 100644 index c741661..0000000 --- a/src/en/boxnovel/BoxNovel.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const boxNovelScraper = require("./BoxNovelScraper"); - -const router = express.Router(); - -router.get("/novels/:pageNo", boxNovelScraper.novelsScraper); - -router.get("/novel/:novelUrl", boxNovelScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", boxNovelScraper.chapterScraper); - -router.get("/search/", boxNovelScraper.searchScraper); - -module.exports = router; diff --git a/src/en/boxnovel/icon.png b/src/en/boxnovel/icon.png deleted file mode 100644 index 22b0620..0000000 Binary files a/src/en/boxnovel/icon.png and /dev/null differ diff --git a/src/en/comrademao.js b/src/en/comrademao.js new file mode 100644 index 0000000..b30aeb6 --- /dev/null +++ b/src/en/comrademao.js @@ -0,0 +1,204 @@ +import cheerio from "react-native-cheerio"; + +const sourceId = 27; + +const sourceName = "Comrade Mao"; + +const baseUrl = "https://comrademao.com/"; + +const popularNovels = async (page) => { + let url = baseUrl + "novel/page/" + page; + const totalPages = 111; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $("div.container") + .find("section") + .each(function () { + const novelName = $(this) + .find("div.columns > div:nth-child(2) > a") + .text(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this) + .find("div.columns > div:nth-child(2) > a") + .attr("href"); + novelUrl = novelUrl.replace(baseUrl + "novel/", ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { novels, totalPages }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = baseUrl + "novel/" + novelUrl; + console.log(url); + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url, novelUrl }; + + novel.novelName = $( + "#NovelInfo > div > div.column.is-one-third.has-text-centered > p" + ) + .text() + .trim(); + + novel.novelCover = $("img.attachment-post-thumbnail").attr("src"); + + novel.summary = $("#NovelInfo > div > div:nth-child(2) > p").text().trim(); + + novel.genre = $("#NovelInfo > p:nth-child(2)") + .text() + .replace(/Genre(s):|\s/g, ""); + + novel.status = $("#NovelInfo > p:nth-child(5)") + .text() + .replace(/Status:|\s/g, "") + .trim(); + + novel.author = $("#NovelInfo > p:nth-child(4)") + .text() + .replace(/Publisher:|\s/g, "") + .trim(); + + let novelChapters = []; + + // $("table > tbody") + // .find("tr") + // .each(function () { + // const releaseDate = $(this).find("span").text(); + + // const chapterName = $(this).find("a").text(); + // const chapterUrl = $(this).find("a").attr("href").split("/")[5]; + + // novelChapters.push({ + // chapterName, + // chapterUrl, + // releaseDate, + // }); + // }); + + // $("small").remove(); + + let chapterUrlPrefix = $("table > tbody") + .find("tr") + .first() + .find("a") + .attr("href") + .slice(0, -1) + .split("/") + .pop() + .split("-"); + + chapterUrlPrefix.pop(); + + chapterUrlPrefix = chapterUrlPrefix.join("-").toLowerCase(); + + let latestChapter = $("table > tbody") + .find("tr") + .first() + .find("a") + .text() + .replace(/^\D+/g, ""); + + for (let i = 1; i <= latestChapter; i++) { + const chapterName = "Chapter " + i; + const releaseDate = null; + const chapterUrl = chapterUrlPrefix + "-" + i + "/"; + + const chapter = { + chapterName, + releaseDate, + chapterUrl, + }; + + novelChapters.push(chapter); + } + + novel.chapters = novelChapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}mtl/${novelUrl}/${chapterUrl}`; + + console.log(url); + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = ""; + + let chapterText = $("#content").html(); + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = baseUrl + "?s=" + searchTerm + "&post_type=novel"; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".newbox") + .find("li") + .each(function () { + const novelName = $(this).find("h3").text(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this).find("a").attr("href"); + novelUrl = novelUrl.replace(baseUrl + "novel/", ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const ComradeMaoScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default ComradeMaoScraper; diff --git a/src/en/comrademao/ComradeMao.js b/src/en/comrademao/ComradeMao.js deleted file mode 100644 index 253a256..0000000 --- a/src/en/comrademao/ComradeMao.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const ComradeMaoScraper = require("./ComradeMaoScraper"); - -const router = express.Router(); - -router.get("/novels/", ComradeMaoScraper.novelsScraper); - -router.get("/novel/:novelUrl", ComradeMaoScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", ComradeMaoScraper.chapterScraper); - -router.get("/search/", ComradeMaoScraper.searchScraper); - -module.exports = router; diff --git a/src/en/comrademao/ComradeMaoScraper.js b/src/en/comrademao/ComradeMaoScraper.js deleted file mode 100644 index 649ec26..0000000 --- a/src/en/comrademao/ComradeMaoScraper.js +++ /dev/null @@ -1,211 +0,0 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); - -const baseUrl = "https://comrademao.com/"; - -const novelsScraper = async (req, res) => { - let url = baseUrl; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $("article.blog-article").each(function (result) { - const novelName = $(this).find("h5 > a").text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this).find("h5 > a").attr("href"); - novelUrl = novelUrl.replace(baseUrl + "novel/", ""); - - const novel = { - extensionId: 27, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = baseUrl + "novel/" + novelUrl; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novel = {}; - - novel.extensionId = 27; - - novel.sourceName = "Comrade Mao"; - - novel.sourceUrl = url; - - novel.novelUrl = novelUrl + "/"; - - function getNovelName(y) { - return y - .replace("-", " ") - .replace(/(?:^|\s)\S/g, (a) => a.toUpperCase()); - } - - novelName = getNovelName(novelUrl); - novel.novelName = novelName; - - novel.novelCover = $("#thumbnail > img").attr("src"); - - let novelSummary = $("div#Description").html(); - novel.novelSummary = htmlToText(novelSummary); - - novel["Genre(s)"] = $("div#Genre") - .text() - .replace(/Genre:|\s/g, ""); - - novel.Status = $("div#Status") - .text() - .replace(/Status:|\s/g, ""); - - novel["Author(s)"] = $("div#Publisher") - .text() - .replace(/Publisher:|\s/g, ""); - - // novel['Genre(s)'] = $("div#Genre"). - - let novelChapters = []; - - let chapterUrlPrefix = $("tbody > tr") - .first() - .next() - .find("td > a") - .text() - .split(" "); - - chapterUrlPrefix.pop(); - - chapterUrlPrefix = chapterUrlPrefix.join("-").toLowerCase(); - - const latestChapter = $("tbody > tr") - .first() - .next() - .find("td > a") - .text() - .replace(/^\D+/g, ""); - - for (let i = 1; i <= latestChapter; i++) { - const chapterName = "Chapter " + i; - const releaseDate = null; - const chapterUrl = chapterUrlPrefix + "-" + i + "/"; - - const chapter = { - chapterName, - releaseDate, - chapterUrl, - }; - - novelChapters.push(chapter); - } - - novel.novelChapters = novelChapters; - - res.json(novel); -}; - -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}mtl/${novelUrl}/${chapterUrl}`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let chapterName = $(".entry-title").text(); - - let chapterText = $(".entry-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($(".nav-next").find("a").length) { - nextChapter = $(".nav-next") - .find("a") - .attr("href") - .replace(baseUrl + "mtl/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($(".nav-previous").find("a").length) { - prevChapter = $(".nav-previous") - .find("a") - .attr("href") - .replace(baseUrl + "mtl/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - - const chapter = { - extensionId: 27, - novelUrl, - chapterUrl, - chapterName, - chapterText, - nextChapter, - prevChapter, - }; - - res.json(chapter); -}; - -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - - const url = baseUrl + "?s=" + searchTerm + "&post_type=novel"; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $("article.blog-article").each(function (result) { - const novelName = $(this).find("h5 > a").text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this).find("h5 > a").attr("href"); - novelUrl = novelUrl.replace(baseUrl + "novel/", ""); - - const novel = { - extensionId: 27, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, -}; diff --git a/src/en/comrademao/icon.png b/src/en/comrademao/icon.png deleted file mode 100644 index f02d1b9..0000000 Binary files a/src/en/comrademao/icon.png and /dev/null differ diff --git a/src/en/divinedaolibrary.js b/src/en/divinedaolibrary.js new file mode 100644 index 0000000..bb15231 --- /dev/null +++ b/src/en/divinedaolibrary.js @@ -0,0 +1,152 @@ +import cheerio from "react-native-cheerio"; + +const sourceId = 70; + +const sourceName = "Divine Dao Library"; + +const baseUrl = "https://www.divinedaolibrary.com/"; + +const popularNovels = async (page) => { + let url = baseUrl + "novels"; + const totalPages = 1; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $("#main") + .find("li") + .each(function () { + const novelName = $(this).find("a").text(); + const novelCover = null; + const novelUrl = $(this).find(" a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { novels, totalPages }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = novelUrl; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url, novelUrl }; + + novel.novelName = $("h1.entry-title").text().trim(); + + novel.novelCover = + $(".entry-content").find("img").attr("src") || + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + + novel.summary = $("#main > article > div > p:nth-child(6)").text().trim(); + + novel.genre = null; + + novel.status = null; + + novel.author = $("#main > article > div > h3:nth-child(2)") + .text() + .replace(/Author:/g, "") + .trim(); + + let novelChapters = []; + + $("#main") + .find("li > span > a") + .each(function () { + const chapterName = $(this).text().trim(); + const releaseDate = null; + const chapterUrl = $(this).attr("href"); + + novelChapters.push({ chapterName, releaseDate, chapterUrl }); + }); + + novel.chapters = novelChapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = chapterUrl; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = $(".entry-title").text().trim(); + + let chapterText = $(".entry-content").html(); + + if (!chapterText) { + chapterText = $(".page-header").html(); + } + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + let url = baseUrl + "novels"; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $("#main") + .find("li") + .each(function () { + const novelName = $(this).find("a").text(); + const novelCover = null; + const novelUrl = $(this).find(" a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + novels = novels.filter((novel) => + novel.novelName.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + return novels; +}; + +const DivineDaoLibraryScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default DivineDaoLibraryScraper; diff --git a/src/en/divinedaolibrary/icon.png b/src/en/divinedaolibrary/icon.png deleted file mode 100644 index 8438f2b..0000000 Binary files a/src/en/divinedaolibrary/icon.png and /dev/null differ diff --git a/src/en/fastnovel/FastNovelScraper.js b/src/en/fastnovel.js similarity index 56% rename from src/en/fastnovel/FastNovelScraper.js rename to src/en/fastnovel.js index 7f28e4f..698d9e5 100644 --- a/src/en/fastnovel/FastNovelScraper.js +++ b/src/en/fastnovel.js @@ -1,13 +1,14 @@ -const cheerio = require("cheerio"); -const { scraper } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://fastnovel.net"; const searchUrl = `https://fastnovel.net/search/`; -const novelsScraper = async (req, res) => { - const url = `${baseUrl}/list/most-popular.html`; +const popularNovels = async (page) => { + let totalPages = 39; + const url = `${baseUrl}/list/most-popular.html?page=${page}`; - const body = await scraper(url); + const result = await fetch(url); + const body = await result.text(); $ = cheerio.load(body); @@ -19,7 +20,7 @@ const novelsScraper = async (req, res) => { const novelUrl = $(this).find("a").attr("href").substring(1); const novel = { - extensionId: 3, + sourceId: 3, novelName, novelCover, novelUrl, @@ -28,50 +29,59 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}/${novelUrl}/`; - const body = await scraper(url); + const result = await fetch(url); + const body = await result.text(); $ = cheerio.load(body); let novel = {}; - novel.extensionId = 3; + novel.sourceId = 3; novel.sourceName = "FastNovel"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $("h1").text(); novel.novelCover = $(".book-cover").attr("data-original"); - novel.novelSummary = $("div.film-content > p").text(); + novel.summary = $("div.film-content > p").text(); - novel["Author(s)"] = $("li > label") + novel.author = $("li > label") .filter(function () { return $(this).text().trim() === "Author:"; }) .next() .text(); - novel["Genre(s)"] = $("li") + novel.genre = $("li") .filter(function () { return $(this).find("label").text().trim() === "Genre:"; }) .text() .replace("Genre:", ""); - novel["Artist(s)"] = null; + novel.artist = null; - novel.Status = null; + novel.status = null; + + novel.status = $("li") + .filter(function () { + return $(this).find("label").text().trim() === "Status:"; + }) + .text() + .includes("Completed") + ? "Completed" + : "Ongoing"; let novelChapters = []; @@ -90,64 +100,38 @@ const novelScraper = async (req, res) => { novelChapters.push(novel); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; - const body = await scraper(url); + const result = await fetch(url); + const body = await result.text(); $ = cheerio.load(body); const chapterName = $(".episode-name").text(); - const chapterText = $("#chapter-body").text(); - - let nextChapter = null; - - nextChapter = $("div.btn-group > a") - .filter(function () { - return $(this).text().trim() === "Next Chapter"; - }) - .attr("href") - .replace("/", ""); - - let prevChapter = null; - - prevChapter = $("div.btn-group > a") - .filter(function () { - return $(this).text().trim() === "Prev Chapter"; - }) - .attr("href") - .replace("/", ""); - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; + let chapterText = $("#chapter-body").html(); const chapter = { - extensionId: 3, + sourceId: 3, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${searchUrl}${searchTerm}`; - const body = await scraper(url); + const result = await fetch(url); + const body = await result.text(); $ = cheerio.load(body); @@ -159,7 +143,7 @@ const searchScraper = async (req, res) => { const novelUrl = $(this).find("a").attr("href").substring(1); const novel = { - extensionId: 3, + sourceId: 3, novelName, novelCover, novelUrl, @@ -168,12 +152,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = fastNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const fastNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default fastNovelScraper; diff --git a/src/en/fastnovel/FastNovel.js b/src/en/fastnovel/FastNovel.js deleted file mode 100644 index 0d4c725..0000000 --- a/src/en/fastnovel/FastNovel.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const fastNovelScraper = require("./FastNovelScraper"); - -const router = express.Router(); - -router.get("/novels/", fastNovelScraper.novelsScraper); - -router.get("/novel/:novelUrl", fastNovelScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", fastNovelScraper.chapterScraper); - -router.get("/search/", fastNovelScraper.searchScraper); - -module.exports = router; diff --git a/src/en/fastnovel/icon.png b/src/en/fastnovel/icon.png deleted file mode 100644 index 8130f91..0000000 Binary files a/src/en/fastnovel/icon.png and /dev/null differ diff --git a/src/en/foxaholic/FoxaholicScraper.js b/src/en/foxaholic.js similarity index 58% rename from src/en/foxaholic/FoxaholicScraper.js rename to src/en/foxaholic.js index 4fa8364..839b0e3 100644 --- a/src/en/foxaholic/FoxaholicScraper.js +++ b/src/en/foxaholic.js @@ -1,12 +1,10 @@ -const cheerio = require("cheerio"); -const FormData = require("form-data"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://foxaholic.com/"; -const novelsScraper = async (req, res) => { - let url = baseUrl + "novel/?m_orderby=rating"; +const popularNovels = async (page) => { + let totalPages = 5; + let url = baseUrl + "novel/page/" + page + "/?m_orderby=rating"; const result = await fetch(url); const body = await result.text(); @@ -23,7 +21,7 @@ const novelsScraper = async (req, res) => { novelUrl += "/"; const novel = { - extensionId: 22, + sourceId: 22, novelName, novelCover, novelUrl, @@ -32,12 +30,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}novel/${novelUrl}/`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}novel/${novelUrl}`; const result = await fetch(url); const body = await result.text(); @@ -46,13 +43,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 22; + novel.sourceId = 22; novel.sourceName = "Foxaholic"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".post-title > h1") .text() @@ -73,22 +70,24 @@ const novelScraper = async (req, res) => { .replace(/[\t\n]/g, "") .trim(); - novel[detailName] = detail; + switch (detailName) { + case "Genre": + novel.genre = detail.trim().replace(/[\t\n]/g, ","); + break; + case "Author": + novel.author = detail.trim(); + break; + case "Novel": + novel.status = detail.trim(); + break; + } }); - novel["Genre(s)"] = novel.Genre.replace(/, /g, ","); - novel["Author(s)"] = novel.Author; - novel.Status = null; - novel["Artist(s)"] = null; - - delete novel.Genre; - delete novel.Author; - $(".description-summary > div.summary__content").find("em").remove(); - novel.novelSummary = $(".description-summary > div.summary__content") + novel.summary = $(".description-summary > div.summary__content") .text() - .replace(/[\t\n]/g, ""); + .trim(); let novelChapters = []; @@ -127,25 +126,13 @@ const novelScraper = async (req, res) => { novelChapters.push({ chapterName, releaseDate, chapterUrl }); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - let volumeUrl = req.params.volumeUrl; - - let optionalUrl; - - if (volumeUrl) { - optionalUrl = volumeUrl + "/"; - } else { - optionalUrl = ""; - } - - const url = `${baseUrl}novel/${novelUrl}/${optionalUrl}${chapterUrl}/`; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}novel/${novelUrl}${chapterUrl}/`; const result = await fetch(url); const body = await result.text(); @@ -154,45 +141,18 @@ const chapterScraper = async (req, res) => { const chapterName = $("h1#chapter-heading").text(); let chapterText = $(".reading-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($(".nav-next").length) { - nextChapter = $(".nav-next").find("a").attr("href").split("/"); - nextChapter[6] - ? (nextChapter = nextChapter[5] + "/" + nextChapter[6]) - : (nextChapter = nextChapter[5]); - } - - let prevChapter = null; - - if ($(".nav-previous").length) { - prevChapter = $(".nav-previous").find("a").attr("href").split("/"); - prevChapter[6] - ? (prevChapter = prevChapter[5] + "/" + prevChapter[6]) - : (prevChapter = prevChapter[5]); - } - - novelUrl += "/"; - chapterUrl = optionalUrl + chapterUrl; - const chapter = { - extensionId: 22, + sourceId: 22, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga`; const result = await fetch(url); @@ -209,7 +169,7 @@ const searchScraper = async (req, res) => { let novelUrl = $(this).find(".h4 > a").attr("href").split("/")[4]; novelUrl += "/"; const novel = { - extensionId: 22, + sourceId: 22, novelName, novelCover, novelUrl, @@ -218,12 +178,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = novelTrenchScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const FoxaHolicScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default FoxaHolicScraper; diff --git a/src/en/foxaholic/Foxaholic.js b/src/en/foxaholic/Foxaholic.js deleted file mode 100644 index 066403f..0000000 --- a/src/en/foxaholic/Foxaholic.js +++ /dev/null @@ -1,16 +0,0 @@ -const express = require("express"); -const FoxaholicScraper = require("./FoxaholicScraper"); - -const router = express.Router(); - -router.get("/novels/", FoxaholicScraper.novelsScraper); - -router.get("/novel/:novelUrl", FoxaholicScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:volumeUrl?/:chapterUrl", - FoxaholicScraper.chapterScraper -); -router.get("/search/", FoxaholicScraper.searchScraper); - -module.exports = router; diff --git a/src/en/foxaholic/icon.png b/src/en/foxaholic/icon.png deleted file mode 100644 index ec5e021..0000000 Binary files a/src/en/foxaholic/icon.png and /dev/null differ diff --git a/src/en/freewebnovel.js b/src/en/freewebnovel.js new file mode 100644 index 0000000..14016b1 --- /dev/null +++ b/src/en/freewebnovel.js @@ -0,0 +1,183 @@ +import cheerio from "react-native-cheerio"; + +const baseUrl = "https://freewebnovel.com/"; + +const popularNovels = async (page) => { + let totalPages = 30; + let url = baseUrl + "completed-novel/" + page; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".li-row").each(function (result) { + const novelName = $(this).find(".tit").text(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this) + .find("h3 > a") + .attr("href") + .replace(".html", "") + .slice(1); + + const novel = { + sourceId: 13, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}${novelUrl.replace("/", "")}.html`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = {}; + + novel.sourceId = 13; + + novel.sourceName = "FreeWebNovel"; + + novel.url = url; + + novel.novelUrl = novelUrl; + + novel.novelName = $("h1.tit").text(); + + novel.novelCover = $(".pic > img").attr("src"); + + novel.genre = $("[title=Genre]") + .next() + .text() + .replace(/[\t\n]/g, ""); + + novel.author = $("[title=Author]") + .next() + .text() + .replace(/[\t\n]/g, ""); + + novel.artist = null; + + novel.status = $("[title=Status]") + .next() + .text() + .replace(/[\t\n]/g, ""); + + let novelSummary = $(".inner").text().trim(); + novel.summary = novelSummary; + + let novelChapters = []; + + let latestChapter; + + $("h3.tit").each(function (res) { + if ($(this).find("a").text() === novel.novelName) { + latestChapter = $(this).next().find("span.s3").text().match(/\d+/); + } + }); + + latestChapter = latestChapter[0]; + + for (let i = 1; i <= parseInt(latestChapter); i++) { + const chapterName = "Chapter " + i; + + const releaseDate = null; + + const chapterUrl = "chapter-" + i; + + const chapter = { chapterName, releaseDate, chapterUrl }; + + novelChapters.push(chapter); + } + + novel.chapters = novelChapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + let novelId = novelUrl.replace("/", ""); + + const url = `${baseUrl}${novelId}/${chapterUrl}.html`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = $("h1.tit").text(); + + let chapterText = $("div.txt").html(); + + const chapter = { + sourceId: 13, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = baseUrl + "search/"; + + const formData = new FormData(); + formData.append("searchkey", searchTerm); + + const result = await fetch(url, { + method: "POST", + body: formData, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".li-row > .li > .con").each(function (result) { + const novelName = $(this).find(".tit").text(); + const novelCover = $(this).find(".pic > a > img").attr("data-cfsrc"); + + let novelUrl = $(this) + .find("h3 > a") + .attr("href") + .replace(".html", "") + .slice(1); + + novelUrl += "/"; + + const novel = { + sourceId: 13, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const FreeWebNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default FreeWebNovelScraper; diff --git a/src/en/freewebnovel/FreeWebNovel.js b/src/en/freewebnovel/FreeWebNovel.js deleted file mode 100644 index 6daa3ab..0000000 --- a/src/en/freewebnovel/FreeWebNovel.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const freeWebNovelScraper = require("./FreeWebNovelScraper"); - -const router = express.Router(); - -router.get("/novels/", freeWebNovelScraper.novelsScraper); - -router.get("/novel/:novelUrl", freeWebNovelScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", freeWebNovelScraper.chapterScraper); - -router.get("/search/", freeWebNovelScraper.searchScraper); - -module.exports = router; diff --git a/src/en/freewebnovel/FreeWebNovelScraper.js b/src/en/freewebnovel/FreeWebNovelScraper.js deleted file mode 100644 index 6b6ca68..0000000 --- a/src/en/freewebnovel/FreeWebNovelScraper.js +++ /dev/null @@ -1,218 +0,0 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const request = require("request"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); - -const baseUrl = "https://freewebnovel.com/"; - -const novelsScraper = async (req, res) => { - let url = baseUrl + "most-popular-novel/"; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $(".li-row").each(function (result) { - const novelName = $(this).find(".tit").text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this) - .find("h3 > a") - .attr("href") - .replace(".html", "") - .slice(1); - - novelUrl += "/"; - - const novel = { - extensionId: 13, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}${novelUrl}.html`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novel = {}; - - novel.extensionId = 13; - - novel.sourceName = "FreeWebNovel"; - - novel.sourceUrl = url; - - novel.novelUrl = `${novelUrl.replace(".html", "")}/`; - - novel.novelName = $("h1.tit").text(); - - novel.novelCover = $(".pic > img").attr("src"); - - novel["Genre(s)"] = $("[title=Genre]") - .next() - .text() - .replace(/[\t\n]/g, ""); - - novel["Author(s)"] = $("[title=Author]") - .next() - .text() - .replace(/[\t\n]/g, ""); - - novel["Artist(s)"] = null; - - novel.Status = $("[title=Status]") - .next() - .text() - .replace(/[\t\n]/g, ""); - - novel.Alternative = $("[title='Alternative names']") - .next() - .text() - .replace(/[\t\n]/g, ""); - - let novelSummary = $(".inner").html(); - novel.novelSummary = htmlToText(novelSummary); - - let novelChapters = []; - - let latestChapter; - - $("h3.tit").each(function (res) { - if ($(this).find("a").text() === novel.novelName) { - // console.log($(this).find("a").text()); - // console.log(novel.novelName); - latestChapter = $(this).next().find("span.s3").text().match(/\d+/); - } - }); - - latestChapter = latestChapter[0]; - - for (let i = 1; i <= parseInt(latestChapter); i++) { - const chapterName = "Chapter " + i; - - const releaseDate = null; - - const chapterUrl = "chapter-" + i; - - const chapter = { chapterName, releaseDate, chapterUrl }; - - novelChapters.push(chapter); - } - - novel.novelChapters = novelChapters; - - res.json(novel); -}; - -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}${novelUrl}/${chapterUrl}.html`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let chapterName = $("h1.tit").text(); - - let chapterText = $("div.txt").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($("[title='Read Next chapter']").length) { - nextChapter = $("[title='Read Next chapter']") - .attr("href") - .replace("/" + novelUrl + "/", "") - .replace(".html", ""); - } - - let prevChapter = null; - - if ($("[title='Read Privious Chapter']").length) { - prevChapter = $("[title='Read Privious Chapter']") - .attr("href") - .replace("/" + novelUrl + "/", "") - .replace(".html", ""); - } - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl; - - const chapter = { - extensionId: 13, - novelUrl, - chapterUrl, - chapterName, - chapterText, - nextChapter, - prevChapter, - }; - - res.json(chapter); -}; - -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - - const url = baseUrl + "search/"; - - request.post( - { url, form: { searchkey: searchTerm } }, - (err, response, body) => { - $ = cheerio.load(body); - - let novels = []; - - $(".li-row > .li > .con").each(function (result) { - const novelName = $(this).find(".tit").text(); - const novelCover = $(this) - .find(".pic > a > img") - .attr("data-cfsrc"); - - let novelUrl = $(this) - .find("h3 > a") - .attr("href") - .replace(".html", "") - .slice(1); - - novelUrl += "/"; - - const novel = { - extensionId: 13, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); - } - ); -}; - -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, -}; diff --git a/src/en/freewebnovel/icon.png b/src/en/freewebnovel/icon.png deleted file mode 100644 index 64bd197..0000000 Binary files a/src/en/freewebnovel/icon.png and /dev/null differ diff --git a/src/en/jpmtl/JPMTLScraper.js b/src/en/jpmtl.js similarity index 58% rename from src/en/jpmtl/JPMTLScraper.js rename to src/en/jpmtl.js index a31ee7e..7b52144 100644 --- a/src/en/jpmtl/JPMTLScraper.js +++ b/src/en/jpmtl.js @@ -1,12 +1,13 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://jpmtl.com/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 157; let url = - "https://jpmtl.com/v2/book/show/browse?query=&categories=&content_type=0&direction=0&page=2&limit=20&type=5&status=all&language=3&exclude_categories="; + "https://jpmtl.com/v2/book/show/browse?query=&categories=&content_type=0&direction=0&page=" + + page + + "&limit=20&type=5&status=all&language=3&exclude_categories="; const result = await fetch(url); const body = await result.json(); @@ -18,16 +19,15 @@ const novelsScraper = async (req, res) => { const novelCover = item.cover; const novelUrl = item.id + "/"; - const novel = { extensionId: 14, novelName, novelCover, novelUrl }; + const novel = { sourceId: 14, novelName, novelCover, novelUrl }; novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}books/${novelUrl}`; const result = await fetch(url); @@ -37,13 +37,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 14; + novel.sourceId = 14; novel.sourceName = "JPMTL"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $("h1.book-sidebar__title").text(); @@ -64,18 +64,28 @@ const novelScraper = async (req, res) => { novel[detailName] = detail; }); - novel.novelSummary = $(".main-book__synopsis").text(); + novel.summary = $(".main-book__synopsis").text(); - novel["Genre(s)"] = ""; + novel.genre = ""; $("a.main-book__category").each(function (result) { - novel["Genre(s)"] += $(this).text(); + novel.genre += $(this).text(); }); - novel["Author(s)"] = $(".book-sidebar__author > .book-sidebar__info") + function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); + } + + const info = $(".book-sidebar__author > .book-sidebar__info") .text() - .replace(/[\t\n]/g, "") - .trim(); + .trim() + .split("・"); + + if (info) { + novel.author = info[0]; + + novel.status = capitalizeFirstLetter(info[1]); + } let novelChapters = []; @@ -91,7 +101,7 @@ const novelScraper = async (req, res) => { const chapterUrl = chapter.id; const obj = { - extensionId: 14, + sourceId: 14, chapterName, releaseDate, chapterUrl, @@ -101,60 +111,35 @@ const novelScraper = async (req, res) => { }); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}books/${novelUrl}/${chapterUrl}`; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}books/${novelUrl}${chapterUrl}`; const result = await fetch(url); const body = await result.text(); - console.log(url); - $ = cheerio.load(body); const chapterName = $(".chapter-content__title").text(); let chapterText = $(".chapter-content__content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - - $(".chapter-wrapper__nav").each(function (result) { - const chId = $(this) - .attr("href") - .replace("/books/" + novelUrl + "/", ""); - if (chId < chapterUrl) { - prevChapter = chId; - } else { - nextChapter = chId; - } - }); - novelUrl += "/"; const chapter = { - extensionId: 14, + sourceId: 14, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `https://jpmtl.com/v2/book/show/browse?query=${searchTerm}&categories=&content_type=2&direction=0&page=1&limit=20&type=5&status=all&language=3&exclude_categories=`; const result = await fetch(url); @@ -167,17 +152,19 @@ const searchScraper = async (req, res) => { const novelCover = item.cover; const novelUrl = item.id + "/"; - const novel = { extensionId: 14, novelName, novelCover, novelUrl }; + const novel = { sourceId: 14, novelName, novelCover, novelUrl }; novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = novelTrenchScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const JPMTLScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default JPMTLScraper; diff --git a/src/en/jpmtl/JPMTL.js b/src/en/jpmtl/JPMTL.js deleted file mode 100644 index 800ffdf..0000000 --- a/src/en/jpmtl/JPMTL.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const JPMTLScraper = require("./JPMTLScraper"); - -const router = express.Router(); - -router.get("/novels/", JPMTLScraper.novelsScraper); - -router.get("/novel/:novelUrl", JPMTLScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", JPMTLScraper.chapterScraper); - -router.get("/search/", JPMTLScraper.searchScraper); - -module.exports = router; diff --git a/src/en/jpmtl/icon.png b/src/en/jpmtl/icon.png deleted file mode 100644 index 9f6dbe7..0000000 Binary files a/src/en/jpmtl/icon.png and /dev/null differ diff --git a/src/en/kisslightnovels/KissLightNovelsScraper.js b/src/en/kisslightnovels.js similarity index 63% rename from src/en/kisslightnovels/KissLightNovelsScraper.js rename to src/en/kisslightnovels.js index f2e581f..faa37a4 100644 --- a/src/en/kisslightnovels/KissLightNovelsScraper.js +++ b/src/en/kisslightnovels.js @@ -1,11 +1,12 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://kisslightnovels.info/"; -const novelsScraper = async (req, res) => { - const result = await fetch(baseUrl); +const popularNovels = async (page) => { + let totalPages = 26; + const url = `${baseUrl}light-novel/page/${page}`; + + const result = await fetch(url); const body = await result.text(); $ = cheerio.load(body); @@ -20,7 +21,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}novel/`, ""); const novel = { - extensionId: 11, + sourceId: 11, novelName, novelCover, novelUrl, @@ -29,12 +30,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}novel/${novelUrl}/`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}novel/${novelUrl}`; const result = await fetch(url); const body = await result.text(); @@ -43,13 +43,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 11; + novel.sourceId = 11; novel.sourceName = "KissLightNovels"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".post-title > h1") .text() @@ -70,12 +70,22 @@ const novelScraper = async (req, res) => { .replace(/[\t\n]/g, "") .trim(); - novel[detailName] = detail; + switch (detailName) { + case "Genre(s)": + novel.genre = detail.trim().replace(/[\t\n]/g, ","); + break; + case "Author(s)": + novel.author = detail.trim(); + break; + case "Artist(s)": + novel.status = detail.trim(); + break; + } }); $(".description-summary > div.summary__content").find("em").remove(); - novel.novelSummary = $(".description-summary > div.summary__content") + novel.summary = $(".description-summary > div.summary__content") .text() .replace(/[\t\n]/g, ""); @@ -99,15 +109,12 @@ const novelScraper = async (req, res) => { novelChapters.push({ chapterName, releaseDate, chapterUrl }); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}novel/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -118,46 +125,20 @@ const chapterScraper = async (req, res) => { $("#snippet-box").remove(); const chapterName = $("div.text-left > h4").text(); + let chapterText = $(".reading-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($(".nav-next").length) { - nextChapter = $(".nav-next") - .find("a") - .attr("href") - .replace(baseUrl + "novel/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($(".nav-previous").length) { - prevChapter = $(".nav-previous") - .find("a") - .attr("href") - .replace(baseUrl + "novel/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - const chapter = { - extensionId: 11, + sourceId: 11, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga&m_orderby=rating`; const result = await fetch(url); @@ -175,7 +156,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}novel/`, ""); const novel = { - extensionId: 11, + sourceId: 11, novelName, novelCover, novelUrl, @@ -184,12 +165,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = kissLightNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const kissLightNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default kissLightNovelScraper; diff --git a/src/en/kisslightnovels/KissLightNovels.js b/src/en/kisslightnovels/KissLightNovels.js deleted file mode 100644 index 1f9bf40..0000000 --- a/src/en/kisslightnovels/KissLightNovels.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const kissLightNovelsScraper = require("./KissLightNovelsScraper"); - -const router = express.Router(); - -router.get("/novels/", kissLightNovelsScraper.novelsScraper); - -router.get("/novel/:novelUrl", kissLightNovelsScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:chapterUrl", - kissLightNovelsScraper.chapterScraper -); - -router.get("/search/", kissLightNovelsScraper.searchScraper); - -module.exports = router; diff --git a/src/en/kisslightnovels/icon.png b/src/en/kisslightnovels/icon.png deleted file mode 100644 index aa0b68d..0000000 Binary files a/src/en/kisslightnovels/icon.png and /dev/null differ diff --git a/src/en/lightnovelpub.js b/src/en/lightnovelpub.js new file mode 100644 index 0000000..7dd317c --- /dev/null +++ b/src/en/lightnovelpub.js @@ -0,0 +1,215 @@ +import cheerio from "react-native-cheerio"; + +const baseUrl = "https://www.lightnovelpub.com/"; + +const popularNovels = async (page) => { + let totalPages = 33; + let url = baseUrl + "browse/all/popular/all/" + page; + + let headers = new Headers({ + Accept: "application/json", + "Content-Type": "application/json", + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }); + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".novel-item.ads").remove(); + + $(".novel-item").each(function () { + const novelName = $(this) + .find(".novel-title") + .text() + .replace(/[\t\n]/g, ""); + + const novelCover = $(this).find("img").attr("data-src"); + + let novelUrl = $(this) + .find(".novel-title > a") + .attr("href") + .replace(`/novel/`, ""); + novelUrl += "/"; + + const novel = { + sourceId: 15, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}novel/${novelUrl}/`; + + let headers = new Headers({ + Accept: "application/json", + "Content-Type": "application/json", + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }); + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = {}; + + novel.sourceId = 15; + + novel.sourceName = "LightNovelPub"; + + novel.url = url; + + novel.novelUrl = novelUrl; + + novel.novelName = $("h1.novel-title") + .text() + .replace(/[\t\n]/g, "") + .trim(); + + novel.novelCover = $("figure.cover > img").attr("data-src"); + + novel.genre = ""; + + $("div.categories > ul > li").each(function () { + novel.genre += + $(this) + .text() + .replace(/[\t\n]/g, "") + ","; + }); + + $("div.header-stats > span").each(function () { + if ($(this).find("small").text() === "Status") { + novel.status = $(this).find("strong").text(); + } + }); + + novel.genre = novel.genre.slice(0, -1); + + novel.author = $(".author > a > span").text(); + + novel.summary = $(".summary > .content").text().trim(); + + let novelChapters = []; + + let totalChapters; + + totalChapters = $(".header-stats > span").first().text().match(/\d+/)[0]; + + for (let i = 1; i <= totalChapters; i++) { + const chapterName = "Chapter " + i; + + const releaseDate = null; + + const chapterUrl = "chapter-" + i; + + const chapter = { chapterName, releaseDate, chapterUrl }; + + novelChapters.push(chapter); + } + + novel.chapters = novelChapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}novel/${novelUrl}/${chapterUrl}`; + + let headers = new Headers({ + Accept: "application/json", + "Content-Type": "application/json", + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }); + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); + $ = cheerio.load(body); + + const chapterName = $("h2").text(); + let chapterText = $("#chapter-container").html(); + const chapter = { + sourceId: 15, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = `${baseUrl}lnwsearchlive?inputContent=${searchTerm}`; + + let headers = new Headers({ + Accept: "application/json", + "Content-Type": "application/json", + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }); + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + let results = JSON.parse($("body").text()); + + $ = cheerio.load(results.resultview); + + $(".novel-item").each(function () { + const novelName = $(this).find("h4.novel-title").text(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this).find("a").attr("href").replace(`/novel/`, ""); + novelUrl += "/"; + + const novel = { + sourceId: 15, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const lightNovelPubScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default lightNovelPubScraper; diff --git a/src/en/lightnovelpub/LightNovelPub.js b/src/en/lightnovelpub/LightNovelPub.js deleted file mode 100644 index aa3ef56..0000000 --- a/src/en/lightnovelpub/LightNovelPub.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const lightNovelPubScraper = require("./LightNovelPubScraper"); - -const router = express.Router(); - -router.get("/novels/", lightNovelPubScraper.novelsScraper); - -router.get("/novel/:novelUrl", lightNovelPubScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", lightNovelPubScraper.chapterScraper); - -router.get("/search/", lightNovelPubScraper.searchScraper); - -module.exports = router; diff --git a/src/en/lightnovelpub/LightNovelPubScraper.js b/src/en/lightnovelpub/LightNovelPubScraper.js deleted file mode 100644 index ef41cef..0000000 --- a/src/en/lightnovelpub/LightNovelPubScraper.js +++ /dev/null @@ -1,204 +0,0 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); -const { scraper } = require("../../helper"); - -const baseUrl = "https://www.lightnovelpub.com/"; - -const novelsScraper = async (req, res) => { - let url = baseUrl + "browse/all/popular/all/1"; - - const body = await scraper(url); - - $ = cheerio.load(body); - - let novels = []; - - $(".novel-item.ads").remove(); - - $(".novel-item").each(function (result) { - const novelName = $(this) - .find(".novel-title") - .text() - .replace(/[\t\n]/g, ""); - - const novelCover = $(this).find("img").attr("data-src"); - - let novelUrl = $(this) - .find(".novel-title > a") - .attr("href") - .replace(`/novel/`, ""); - novelUrl += "/"; - - const novel = { - extensionId: 15, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}novel/${novelUrl}/`; - - const body = await scraper(url); - - $ = cheerio.load(body); - - let novel = {}; - - novel.extensionId = 15; - - novel.sourceName = "LightNovelPub"; - - novel.sourceUrl = url; - - novel.novelUrl = `${novelUrl}/`; - - novel.novelName = $("h1.novel-title") - .text() - .replace(/[\t\n]/g, "") - .trim(); - - novel.novelCover = $("figure.cover > img").attr("src"); - - novel["Genre(s)"] = ""; - - $("div.categories > ul > li").each(function (result) { - novel["Genre(s)"] += - $(this) - .text() - .replace(/[\t\n]/g, "") + ","; - }); - - $("div.header-stats > span").each(function (result) { - if ($(this).find("small").text() === "Status") { - novel.Status = $(this).find("strong").text(); - } - }); - - novel["Genre(s)"] = novel["Genre(s)"].slice(0, -1); - - novel["Author(s)"] = $(".author > a > span").text(); - - novelSummary = $(".summary > .content").html(); - novel.novelSummary = htmlToText(novelSummary); - - let novelChapters = []; - - let firstChapterNo, totalChapters; - - totalChapters = $(".header-stats > span").first().text().match(/\d+/)[0]; - firstChapterNo = $(".chapter-no").first().text().match(/\d+/)[0]; - - for (let i = firstChapterNo; i <= totalChapters; i++) { - const chapterName = "Chapter " + i; - - const releaseDate = null; - - const chapterUrl = "chapter-" + i; - - const chapter = { chapterName, releaseDate, chapterUrl }; - - novelChapters.push(chapter); - } - - novel.novelChapters = novelChapters; - - res.json(novel); -}; - -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}novel/${novelUrl}/${chapterUrl}`; - - const body = await scraper(url); - - $ = cheerio.load(body); - - const chapterName = $("h2").text(); - let chapterText = $("#chapter-container").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if (!$("a.nextchap.isDisabled").length) { - nextChapter = $("a.nextchap") - .attr("href") - .replace("/novel/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if (!$("a.prevchap.isDisabled").length) { - prevChapter = $("a.prevchap") - .attr("href") - .replace("/novel/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - - const chapter = { - extensionId: 15, - novelUrl, - chapterUrl, - chapterName, - chapterText, - nextChapter, - prevChapter, - }; - - res.json(chapter); -}; - -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - - const url = `${baseUrl}lnwsearchlive?inputContent=${searchTerm}`; - - const result = await scraper(url); - - $ = cheerio.load(result); - - let novels = []; - - let results = JSON.parse($("body").text()); - - $ = cheerio.load(results.resultview); - - $(".novel-item").each(function (result) { - const novelName = $(this).find("h4.novel-title").text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this).find("a").attr("href").replace(`/novel/`, ""); - novelUrl += "/"; - - const novel = { - extensionId: 15, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -module.exports = lightNovelPubScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, -}; diff --git a/src/en/lightnovelpub/icon.png b/src/en/lightnovelpub/icon.png deleted file mode 100644 index 1ffe73e..0000000 Binary files a/src/en/lightnovelpub/icon.png and /dev/null differ diff --git a/src/en/lightnovelupdates.js b/src/en/lightnovelupdates.js new file mode 100644 index 0000000..c651bc9 --- /dev/null +++ b/src/en/lightnovelupdates.js @@ -0,0 +1,159 @@ +import cheerio from "react-native-cheerio"; +import NovelUpdatesScraper from "./novelupdates"; + +const sourceId = 72; +const sourceName = "LightNovelUpdates"; +const baseUrl = "https://www.lightnovelupdates.com/"; + +const popularNovels = async (page) => { + let url = `${baseUrl}novel/page/${page}/?m_orderby=rating`; + let totalPages = 500; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".page-item-detail").each(function () { + const novelName = $(this).find(".h5 > a").text(); + const novelCover = $(this).find("img").attr("src"); + const novelUrl = $(this).find(".h5 > a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = novelUrl; + + console.log(url); + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, novelUrl, url }; + + novel.novelName = $(".post-title > h1").text().trim(); + novel.novelCover = + $(".summary_image > a > img").attr("src") || + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + + $(".post-content_item").each(function (result) { + detailName = $(this).find(".summary-heading > h5").text().trim(); + detail = $(this).find(".summary-content").text().trim(); + + switch (detailName) { + case "Genre(s)": + novel.genre = detail.trim().replace(/[\t\n]/g, ","); + break; + case "Author(s)": + novel.author = detail.trim(); + break; + case "Artist(s)": + novel.status = detail.trim(); + break; + } + }); + + novel.summary = $("div.summary__content").text(); + + let novelChapters = []; + + const novelId = $(".rating-post-id").attr("value"); + + let formData = new FormData(); + formData.append("action", "manga_get_chapters"); + formData.append("manga", novelId); + + const data = await fetch( + "https://www.lightnovelupdates.com/wp-admin/admin-ajax.php", + { + method: "POST", + body: formData, + } + ); + const text = await data.text(); + + $ = cheerio.load(text); + + $(".wp-manga-chapter").each(function () { + const chapterName = $(this).find("a").text().trim(); + const releaseDate = $(this).find("span").text().trim(); + const chapterUrl = $(this).find("a").attr("href"); + + const chapter = { chapterName, releaseDate, chapterUrl }; + + novelChapters.push(chapter); + }); + + novel.chapters = novelChapters.reverse(); + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const chapterName = ""; + const chapterText = ( + await NovelUpdatesScraper.parseChapter(novelUrl, chapterUrl) + ).chapterText; + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga&m_orderby=rating`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".c-tabs-item__content").each(function (result) { + const novelName = $(this).find(".h4 > a").text(); + const novelCover = $(this).find("img").attr("src"); + const novelUrl = $(this).find(".h4 > a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const LightNovelUpdatesScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default LightNovelUpdatesScraper; diff --git a/src/en/lightnovelupdates/icon.png b/src/en/lightnovelupdates/icon.png deleted file mode 100644 index a74e12d..0000000 Binary files a/src/en/lightnovelupdates/icon.png and /dev/null differ diff --git a/src/en/lnmtl/LNMTLScraper.js b/src/en/lnmtl.js similarity index 73% rename from src/en/lnmtl/LNMTLScraper.js rename to src/en/lnmtl.js index a740af4..b306656 100644 --- a/src/en/lnmtl/LNMTLScraper.js +++ b/src/en/lnmtl.js @@ -1,11 +1,12 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; +import { showToast } from "../../hooks/showToast"; +import { htmlToText } from "../helpers/htmlToText"; const baseUrl = "https://lnmtl.com/"; -const novelsScraper = async (req, res) => { - let url = baseUrl + "novel?page=1"; // + page; +const popularNovels = async (page) => { + const totalPages = 63; + let url = baseUrl + "novel?page=" + page; const result = await fetch(url); const body = await result.text(); @@ -22,7 +23,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace(baseUrl + "novel/", ""); const novel = { - extensionId: 37, + sourceId: 37, novelName, novelCover, novelUrl, @@ -31,11 +32,12 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { + showToast("LNMTL might take around 20-30 seconds."); + const url = baseUrl + "novel/" + novelUrl; const result = await fetch(url); @@ -45,11 +47,11 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 37; + novel.sourceId = 37; novel.sourceName = "LNMTL"; - novel.sourceUrl = url; + novel.url = url; novel.novelUrl = novelUrl; @@ -57,19 +59,19 @@ const novelScraper = async (req, res) => { novel.novelCover = $("div.novel").find("img").attr("src"); - novel.novelSummary = $("div.description").text().trim(); + novel.summary = $("div.description").text().trim(); - novel["Author(s)"] = $( + novel.author = $( "main > div:nth-child(3) > div > div.col-lg-3.col-md-4 > div:nth-child(2) > div.panel-body > dl:nth-child(1) > dd > span" ).text(); - novel.Status = $( + novel.status = $( "main > div:nth-child(3) > div > div.col-lg-3.col-md-4 > div:nth-child(2) > div.panel-body > dl:nth-child(2) > dd" ) .text() .trim(); - novel["Genre(s)"] = $( + novel.genre = $( "main > div.container > div > div.col-lg-3.col-md-4 > div:nth-child(4) > div.panel-body > ul" ) .text() @@ -112,17 +114,13 @@ const novelScraper = async (req, res) => { } } - novel.novelChapters = chapters; + novel.chapters = chapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}chapter/${chapterUrl}`; - console.log(url); const result = await fetch(url); const body = await result.text(); @@ -134,27 +132,28 @@ const chapterScraper = async (req, res) => { $(".original").remove(); let chapterText = $(".chapter-body").html(); - chapterText = chapterName + "\n\n" + parseHtml(chapterText); - let nextChapter = null; - let prevChapter = null; + if (!chapterText) { + chapterText = $(".alert.alert-warning").text(); + } + + chapterText = + chapterName + + "\n\n" + + htmlToText(chapterText, { removeLineBreaks: false }); const chapter = { - extensionId: 37, + sourceId: 37, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = "https://lnmtl.com/term"; const result = await fetch(url); @@ -172,22 +171,24 @@ const searchScraper = async (req, res) => { novels = JSON.parse(novels); novels = novels.filter((novel) => - novel.name.toLowerCase().includes(searchTerm) + novel.name.toLowerCase().includes(searchTerm.toLowerCase()) ); novels = novels.map((novel) => ({ - extensionId: 37, + sourceId: 37, novelName: novel.name, novelUrl: novel.slug, novelCover: novel.image, })); - res.json(novels); + return novels; }; -module.exports = lnmtlScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const LNMTLScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default LNMTLScraper; diff --git a/src/en/lnmtl/LNMTL.js b/src/en/lnmtl/LNMTL.js deleted file mode 100644 index 513db6a..0000000 --- a/src/en/lnmtl/LNMTL.js +++ /dev/null @@ -1,11 +0,0 @@ -const express = require("express"); -const lnmtlScraper = require("./LNMTLScraper"); - -const router = express.Router(); - -router.get("/novels/", lnmtlScraper.novelsScraper); -router.get("/novel/:novelUrl", lnmtlScraper.novelScraper); -router.get("/novel/:novelUrl/:chapterUrl", lnmtlScraper.chapterScraper); -router.get("/search/", lnmtlScraper.searchScraper); - -module.exports = router; diff --git a/src/en/lnmtl/icon.png b/src/en/lnmtl/icon.png deleted file mode 100644 index 162b1b3..0000000 Binary files a/src/en/lnmtl/icon.png and /dev/null differ diff --git a/src/en/mtlnovel/MTLNovelScraper.js b/src/en/mtlnovel.js similarity index 56% rename from src/en/mtlnovel/MTLNovelScraper.js rename to src/en/mtlnovel.js index 1d39ab3..4996c7c 100644 --- a/src/en/mtlnovel/MTLNovelScraper.js +++ b/src/en/mtlnovel.js @@ -1,16 +1,24 @@ -const cheerio = require("cheerio"); -const FormData = require("form-data"); -const fetch = require("node-fetch"); -const UserAgent = require("user-agents"); - -const { scraper, parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://www.mtlnovel.com"; -const novelsScraper = async (req, res) => { - const url = `${baseUrl}/alltime-rank/`; +const popularNovels = async (page) => { + let totalPages = 10; + const url = `${baseUrl}/alltime-rank/page/${page}`; - const body = await scraper(url); + console.log(url); + + let headers = new Headers({ + referer: "https://www.mtlnovel.com/", + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }); + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); $ = cheerio.load(body); @@ -24,7 +32,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace("https://www.mtlnovel.com/", ""); const novel = { - extensionId: 5, + sourceId: 5, novelUrl, novelName, novelCover, @@ -33,35 +41,43 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}/${novelUrl}`; - const body = await scraper(url); + let headers = new Headers({ + referer: "https://www.mtlnovel.com/alltime-rank/", + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }); + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); $ = cheerio.load(body); let novel = {}; - novel.extensionId = 5; + novel.sourceId = 5; novel.sourceName = "MTLNovel"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $("h1.entry-title").text(); novel.novelCover = $(".nov-head > amp-img").attr("src"); - novel.novelSummary = $("div.desc > h2").next().text(); + novel.summary = $("div.desc > h2").next().text(); - novel["Author(s)"] = $("tr > td") + novel.author = $("tr > td") .filter(function () { return $(this).prev().text().trim() === "Author"; }) @@ -69,7 +85,7 @@ const novelScraper = async (req, res) => { .text() .replace("Auhtor:", ""); - novel.Status = $("tr > td") + novel.status = $("tr > td") .filter(function () { return $(this).prev().text().trim() === "Status"; }) @@ -77,7 +93,7 @@ const novelScraper = async (req, res) => { .text() .replace("Status:", ""); - novel["Genre(s)"] = $("td") + novel.genre = $("td") .filter(function () { return $(this).prev().text().trim() === "Genre"; }) @@ -85,12 +101,13 @@ const novelScraper = async (req, res) => { .text() .replace("Genre:", ""); - novel["Artist(s)"] = null; + novel.artist = null; const chapterListUrl = url + "/chapter-list/"; const getChapters = async () => { - const body = await scraper(chapterListUrl); + const result = await fetch(chapterListUrl); + const body = await result.text(); $ = cheerio.load(body); @@ -104,7 +121,7 @@ const novelScraper = async (req, res) => { let chapterUrl = $(this).attr("href"); chapterUrl = chapterUrl.replace( - `https://www.mtlnovel.com/${novelUrl}/`, + `https://www.mtlnovel.com/${novelUrl}`, "" ); @@ -114,60 +131,44 @@ const novelScraper = async (req, res) => { releaseDate, }); }); - return novelChapters; + return novelChapters.reverse(); }; - novel.novelChapters = await getChapters(); + novel.chapters = await getChapters(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}/${novelUrl}${chapterUrl}`; + console.log(url); - const url = `${baseUrl}/${novelUrl}/${chapterUrl}/`; - - const body = await scraper(url); + const result = await fetch(url); + const body = await result.text(); $ = cheerio.load(body); const chapterName = $("h1.main-title").text(); let chapterText = $("div.par").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - if ($("a.next").attr("href")) { - nextChapter = $("a.next").attr("href").replace(`/${novelUrl}/`, ""); - } - - let prevChapter = null; - if ($("a.prev").attr("href")) { - prevChapter = $("a.prev").attr("href").replace(`/${novelUrl}/`, ""); - } - - chapter = { - extensionId: 5, + const chapter = { + sourceId: 5, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const searchUrl = "https://www.mtlnovel.com/wp-admin/admin-ajax.php?action=autosuggest&q=" + searchTerm + "&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com"; - const result = await scraper(searchUrl); + const res = await fetch(searchUrl); + const result = await res.text(); $ = cheerio.load(result); @@ -183,17 +184,19 @@ const searchScraper = async (req, res) => { "" ); - const novel = { extensionId: 5, novelName, novelCover, novelUrl }; + const novel = { sourceId: 5, novelName, novelCover, novelUrl }; novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = mtlNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const mtlNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default mtlNovelScraper; diff --git a/src/en/mtlnovel/MTLNovel.js b/src/en/mtlnovel/MTLNovel.js deleted file mode 100644 index 241823c..0000000 --- a/src/en/mtlnovel/MTLNovel.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const mtlNovelScraper = require("./MTLNovelScraper"); - -const router = express.Router(); - -router.get("/novels/", mtlNovelScraper.novelsScraper); - -router.get("/novel/:novelUrl", mtlNovelScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", mtlNovelScraper.chapterScraper); - -router.get("/search/", mtlNovelScraper.searchScraper); - -module.exports = router; diff --git a/src/en/mtlnovel/icon.png b/src/en/mtlnovel/icon.png deleted file mode 100644 index c700c30..0000000 Binary files a/src/en/mtlnovel/icon.png and /dev/null differ diff --git a/src/en/novelfull/NovelFullScraper.js b/src/en/novelfull.js similarity index 65% rename from src/en/novelfull/NovelFullScraper.js rename to src/en/novelfull.js index 4502cc1..375ec43 100644 --- a/src/en/novelfull/NovelFullScraper.js +++ b/src/en/novelfull.js @@ -1,11 +1,10 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://novelfull.com"; -const novelsScraper = async (req, res) => { - const url = `${baseUrl}/most-popular`; +const popularNovels = async (page) => { + let totalPages = 42; + const url = `${baseUrl}/most-popular?page=${page}`; const result = await fetch(url); const body = await result.text(); @@ -28,7 +27,7 @@ const novelsScraper = async (req, res) => { novelUrl = `${novelUrl}/`; const novel = { - extensionId: 8, + sourceId: 8, novelUrl, novelName, novelCover, @@ -37,12 +36,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}/${novelUrl}.html`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}/${novelUrl.slice(0, -1)}.html`; const result = await fetch(url); const body = await result.text(); @@ -51,37 +49,37 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 8; + novel.sourceId = 8; novel.sourceName = "NovelFull"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $("div.book > img").attr("alt"); novel.novelCover = baseUrl + $("div.book > img").attr("src"); - novel.novelSummary = $("div.desc-text").text(); + novel.summary = $("div.desc-text").text().trim(); - novel["Author(s)"] = $("div.info > div > h3") + novel.author = $("div.info > div > h3") .filter(function () { return $(this).text().trim() === "Author:"; }) .siblings() .text(); - novel["Genre(s)"] = $("div.info > div") + novel.genre = $("div.info > div") .filter(function () { return $(this).find("h3").text().trim() === "Genre:"; }) .text() .replace("Genre:", ""); - novel["Artist(s)"] = null; + novel.artist = null; - novel.Status = $("div.info > div > h3") + novel.status = $("div.info > div > h3") .filter(function () { return $(this).text().trim() === "Status:"; }) @@ -105,7 +103,7 @@ const novelScraper = async (req, res) => { let chapterName = $(this).text(); let releaseDate = null; let chapterUrl = $(this).attr("value"); - chapterUrl = chapterUrl.replace(`/${novelUrl}/`, ""); + chapterUrl = chapterUrl.replace(`/${novelUrl}`, ""); novelChapters.push({ chapterName, @@ -116,16 +114,13 @@ const novelScraper = async (req, res) => { return novelChapters; }; - novel.novelChapters = await getChapters(novelId); + novel.chapters = await getChapters(novelId); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - const novelUrl = req.params.novelUrl.replace(".html/", ""); - const chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}/${novelUrl}${chapterUrl}`; const result = await fetch(url); const body = await result.text(); @@ -134,37 +129,18 @@ const chapterScraper = async (req, res) => { const chapterName = $(".chapter-title").attr("title"); let chapterText = $("#chapter-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - if ($("a#next_chap").attr("href")) { - nextChapter = $("a#next_chap") - .attr("href") - .replace(`/${novelUrl}/`, ""); - } - - let prevChapter = null; - if ($("a#prev_chap").attr("href")) { - prevChapter = $("a#prev_chap") - .attr("href") - .replace(`/${novelUrl}/`, ""); - } - const chapter = { - extensionId: 8, + sourceId: 8, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; +const searchNovels = async (searchTerm) => { const searchUrl = `https://novelfull.com/search?keyword=`; const url = `${searchUrl}${searchTerm}`; @@ -188,7 +164,7 @@ const searchScraper = async (req, res) => { const novelCover = baseUrl + $(this).find("img").attr("src"); const novel = { - extensionId: 8, + sourceId: 8, novelUrl, novelName, novelCover, @@ -197,12 +173,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = novelFullScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const novelFullScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default novelFullScraper; diff --git a/src/en/novelfull/NovelFull.js b/src/en/novelfull/NovelFull.js deleted file mode 100644 index 949ab32..0000000 --- a/src/en/novelfull/NovelFull.js +++ /dev/null @@ -1,13 +0,0 @@ -const express = require("express"); -const novelFullScraper = require("./NovelFullScraper"); -const router = express.Router(); - -router.get("/novels/", novelFullScraper.novelsScraper); - -router.get("/novel/:novelUrl", novelFullScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", novelFullScraper.chapterScraper); - -router.get("/search/", novelFullScraper.searchScraper); - -module.exports = router; diff --git a/src/en/novelfull/icon.png b/src/en/novelfull/icon.png deleted file mode 100644 index 491c609..0000000 Binary files a/src/en/novelfull/icon.png and /dev/null differ diff --git a/src/en/novelhall/NovelhallScraper.js b/src/en/novelhall.js similarity index 62% rename from src/en/novelhall/NovelhallScraper.js rename to src/en/novelhall.js index fe1f8b8..2bb3a57 100644 --- a/src/en/novelhall/NovelhallScraper.js +++ b/src/en/novelhall.js @@ -1,10 +1,9 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://www.novelhall.com/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; const result = await fetch(baseUrl); const body = await result.text(); @@ -20,7 +19,7 @@ const novelsScraper = async (req, res) => { const novelUrl = $(this).find("a").attr("href").substring(1); const novel = { - extensionId: 6, + sourceId: 6, novelName, novelCover, novelUrl, @@ -29,11 +28,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}${novelUrl}/`; const result = await fetch(url); @@ -43,29 +41,29 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 6; + novel.sourceId = 6; novel.sourceName = "NovelHall"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $("h1").text(); novel.novelCover = $("div.book-img > img").attr("src"); - novel.novelSummary = $("div.intro") + novel.summary = $("div.intro") .text() .replace(/[\t\n]/g, ""); - novel["Author(s)"] = $("span.blue").first().text().replace("Author:", ""); + novel.author = $("span.blue").first().text().replace("Author:", ""); - novel["Genre(s)"] = $("a.red").text(); + novel.genre = $("a.red").text(); - novel["Artist(s)"] = null; + novel.artist = null; - novel.Status = $("span.blue").first().next().text().replace("Status:", ""); + novel.status = $("span.blue").first().next().text().replace("Status:", ""); let novelChapters = []; @@ -91,15 +89,12 @@ const novelScraper = async (req, res) => { novelChapters.push(chapter); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl.replace(".html/", ""); - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -109,38 +104,18 @@ const chapterScraper = async (req, res) => { const chapterName = $("h1").text(); let chapterText = $("div.entry-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - nextChapter = $('a[rel="next"]').attr("href"); - if (nextChapter) { - nextChapter = nextChapter.replace(`/${req.params.novelUrl}/`, ""); - } - - let prevChapter = null; - prevChapter = $('a[rel="prev"]').attr("href"); - if (prevChapter) { - prevChapter = prevChapter.replace(`/${req.params.novelUrl}/`, ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - chapter = { - extensionId: 6, + sourceId: 6, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; +const searchNovels = async (searchTerm) => { const searchUrl = `${baseUrl}index.php?s=so&module=book&keyword=`; const url = `${searchUrl}${searchTerm}`; @@ -164,7 +139,7 @@ const searchScraper = async (req, res) => { } novel = { - extensionId: 6, + sourceId: 6, novelName, novelCover, novelUrl, @@ -173,12 +148,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = novelhallScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const novelhallScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default novelhallScraper; diff --git a/src/en/novelhall/Novelhall.js b/src/en/novelhall/Novelhall.js deleted file mode 100644 index 717ec36..0000000 --- a/src/en/novelhall/Novelhall.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const novelhallScraper = require("./NovelhallScraper"); - -const router = express.Router(); - -router.get("/novels/", novelhallScraper.novelsScraper); - -router.get("/novel/:novelUrl", novelhallScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", novelhallScraper.chapterScraper); - -router.get("/search/", novelhallScraper.searchScraper); - -module.exports = router; diff --git a/src/en/novelhall/icon.png b/src/en/novelhall/icon.png deleted file mode 100644 index 9e08d83..0000000 Binary files a/src/en/novelhall/icon.png and /dev/null differ diff --git a/src/en/novelonlinefull.js b/src/en/novelonlinefull.js new file mode 100644 index 0000000..402d6e7 --- /dev/null +++ b/src/en/novelonlinefull.js @@ -0,0 +1,158 @@ +import cheerio from "react-native-cheerio"; +import { Status } from "../helpers/constants"; + +const sourceId = 71; +const sourceName = "NovelOnlineFull.com"; +const baseUrl = "https://novelonlinefull.com/"; + +const popularNovels = async (page) => { + let url = + baseUrl + "novel_list?type=topview&category=all&state=all&page=" + page; + const totalPages = 2155; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".update_item.list_category").each(function () { + const novelName = $(this).find("h3").text().trim(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this).find("h3 > a").attr("href"); + novelUrl = novelUrl.replace(baseUrl + "novel/", ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { novels, totalPages }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = baseUrl + "novel/" + novelUrl; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url, novelUrl }; + + novel.novelName = $("h1").text().trim(); + + novel.novelCover = $(".info_image > img").attr("src"); + + novel.summary = $("#noidungm").text().trim(); + + novel.genre = $("div.truyen_if_wrap > ul > li:nth-child(3)") + .text() + .replace("GENRES: ", "") + .replace(/ - /g, ",") + .trim(); + + console.log(novel.genre); + + novel.status = + $(" div.truyen_if_wrap > ul > li:nth-child(4) > a") + .text() + .replace("STATUS", "") + .trim() === "ONGOING" + ? Status.ONGOING + : Status.COMPLETED; + + novel.author = $("div.truyen_if_wrap > ul > li:nth-child(2)") + .text() + .replace("Author(s):", "") + .trim(); + + let novelChapters = []; + + $(".chapter-list") + .find(".row") + .each(function () { + const releaseDate = $(this).find("span:nth-child(2)").text(); + + const chapterName = $(this).find("span:nth-child(1) > a").text(); + const chapterUrl = $(this).find("a").attr("href"); + + novelChapters.push({ + chapterName, + chapterUrl, + releaseDate, + }); + }); + + novel.chapters = novelChapters.reverse(); + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = chapterUrl; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = $(".name_chapter").text(); + let chapterText = $("#vung_doc").html(); + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = baseUrl + "search_novels/" + searchTerm; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".update_item.list_category").each(function () { + const novelName = $(this).find("h3").text(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this).find("h3 > a").attr("href"); + novelUrl = novelUrl.replace(baseUrl + "novel/", ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const NovelOnlineFullScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default NovelOnlineFullScraper; diff --git a/src/en/novelonlinefull/icon.png b/src/en/novelonlinefull/icon.png deleted file mode 100644 index 56e992a..0000000 Binary files a/src/en/novelonlinefull/icon.png and /dev/null differ diff --git a/src/en/novelpassion/NovelPassionScraper.js b/src/en/novelpassion.js similarity index 68% rename from src/en/novelpassion/NovelPassionScraper.js rename to src/en/novelpassion.js index 63253fb..4ed884d 100644 --- a/src/en/novelpassion/NovelPassionScraper.js +++ b/src/en/novelpassion.js @@ -1,12 +1,12 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; + +import moment from "moment"; const baseUrl = "https://www.novelpassion.com/"; -const novelsScraper = async (req, res) => { - let url = baseUrl + "category/all?p=1&s=1&f=4"; +const popularNovels = async (page) => { + const totalPages = 42; + let url = baseUrl + "category/all?p=" + page + "&s=1&f=4"; const result = await fetch(url); const body = await result.text(); @@ -27,7 +27,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace("/novel/", ""); const novel = { - extensionId: 33, + sourceId: 33, novelName, novelCover, novelUrl, @@ -36,11 +36,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = baseUrl + "novel/" + novelUrl; const result = await fetch(url); @@ -50,11 +49,11 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 33; + novel.sourceId = 33; novel.sourceName = "NovelPassion"; - novel.sourceUrl = url; + novel.url = url; novel.novelUrl = novelUrl; @@ -63,8 +62,8 @@ const novelScraper = async (req, res) => { novel.novelCover = baseUrl + $("i.g_thumb > img").attr("src"); - let novelSummary = $("div.j_synopsis").html(); - novel.novelSummary = htmlToText(novelSummary); + let novelSummary = $("div.j_synopsis").text().trim(); + novel.summary = novelSummary; $("address.lh20.m14.pr").each(function (result) { detailName = $(this).find("p.ell.dib.vam").text().trim(); @@ -73,15 +72,15 @@ const novelScraper = async (req, res) => { switch (detailName) { case "Categories:": - novel["Genre(s)"] = detail.trim().replace(/ - /g, ","); + novel.genre = detail.trim().replace(/ - /g, ","); break; case "Author:": - novel["Author(s)"] = detail.trim().replace(/ - /g, ", "); + novel.author = detail.trim().replace(/ - /g, ", "); break; } if (detailName.includes("Status:")) { - novel.Status = detailName.replace("Status:", "").trim(); + novel.status = detailName.replace("Status:", "").trim(); } }); @@ -92,7 +91,8 @@ const novelScraper = async (req, res) => { .each(function (result) { const chapterName = $(this).find("span.sp1").text(); - const releaseDate = $(this).find("i.sp2").text(); + let releaseDate = $(this).find("i.sp2").text(); + releaseDate = moment(new Date(releaseDate)).format("MM/DD/YY"); const chapterUrl = $(this) .find("a") @@ -106,15 +106,12 @@ const novelScraper = async (req, res) => { }); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}novel/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -125,27 +122,18 @@ const chapterScraper = async (req, res) => { let chapterName = $("div.cha-content").find("strong").text(); let chapterText = $("div.cha-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - const chapter = { - extensionId: 33, + sourceId: 33, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = baseUrl + "search?keyword=" + searchTerm; const result = await fetch(url); @@ -167,7 +155,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl.replace("/novel/", ""); const novel = { - extensionId: 33, + sourceId: 33, novelName, novelCover, novelUrl, @@ -176,12 +164,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const NovelPassionScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default NovelPassionScraper; diff --git a/src/en/novelpassion/NovelPassion.js b/src/en/novelpassion/NovelPassion.js deleted file mode 100644 index 9c9027c..0000000 --- a/src/en/novelpassion/NovelPassion.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const NovelPassionScraper = require("./NovelPassionScraper"); - -const router = express.Router(); - -router.get("/novels/", NovelPassionScraper.novelsScraper); - -router.get("/novel/:novelUrl", NovelPassionScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", NovelPassionScraper.chapterScraper); - -router.get("/search/", NovelPassionScraper.searchScraper); - -module.exports = router; diff --git a/src/en/novelpassion/icon.png b/src/en/novelpassion/icon.png deleted file mode 100644 index 51b5028..0000000 Binary files a/src/en/novelpassion/icon.png and /dev/null differ diff --git a/src/en/noveltrench/NovelTrenchScraper.js b/src/en/noveltrench.js similarity index 62% rename from src/en/noveltrench/NovelTrenchScraper.js rename to src/en/noveltrench.js index 3e2a7df..94a6f1c 100644 --- a/src/en/noveltrench/NovelTrenchScraper.js +++ b/src/en/noveltrench.js @@ -1,11 +1,10 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://noveltrench.com/"; -const novelsScraper = async (req, res) => { - let url = baseUrl; +const popularNovels = async (page) => { + let totalPages = 270; + let url = baseUrl + "manga/page/" + page; const result = await fetch(url); const body = await result.text(); @@ -22,7 +21,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}manga/`, ""); const novel = { - extensionId: 9, + sourceId: 9, novelName, novelCover, novelUrl, @@ -31,12 +30,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}manga/${novelUrl}/`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}manga/${novelUrl}`; const result = await fetch(url); const body = await result.text(); @@ -45,13 +43,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 9; + novel.sourceId = 9; novel.sourceName = "NovelTrench"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".post-title > h1") .text() @@ -72,12 +70,22 @@ const novelScraper = async (req, res) => { .replace(/[\t\n]/g, "") .trim(); - novel[detailName] = detail; + switch (detailName) { + case "Genre(s)": + novel.genre = detail.trim().replace(/[\t\n]/g, ","); + break; + case "Author(s)": + novel.author = detail.trim(); + break; + case "Artist(s)": + novel.status = detail.trim(); + break; + } }); $(".description-summary > div.summary__content").find("em").remove(); - novel.novelSummary = $(".description-summary > div.summary__content") + novel.summary = $(".description-summary > div.summary__content") .text() .replace(/[\t\n]/g, ""); @@ -97,20 +105,16 @@ const novelScraper = async (req, res) => { .trim(); chapterUrl = $(this).find("a").attr("href").replace(url, ""); - novelChapters.push({ chapterName, releaseDate, chapterUrl }); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}manga/${novelUrl}/${chapterUrl}`; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}manga/${novelUrl}${chapterUrl}`; const result = await fetch(url); const body = await result.text(); @@ -119,45 +123,18 @@ const chapterScraper = async (req, res) => { const chapterName = $("h1#chapter-heading").text(); let chapterText = $(".reading-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($(".nav-next").length) { - nextChapter = $(".nav-next") - .find("a") - .attr("href") - .replace(baseUrl + "manga/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($(".nav-previous").length) { - prevChapter = $(".nav-previous") - .find("a") - .attr("href") - .replace(baseUrl + "manga/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - const chapter = { - extensionId: 9, + sourceId: 9, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga`; const result = await fetch(url); @@ -175,7 +152,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}manga/`, ""); const novel = { - extensionId: 9, + sourceId: 9, novelName, novelCover, novelUrl, @@ -184,12 +161,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = novelTrenchScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const novelTrenchScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default novelTrenchScraper; diff --git a/src/en/noveltrench/NovelTrench.js b/src/en/noveltrench/NovelTrench.js deleted file mode 100644 index dd422f0..0000000 --- a/src/en/noveltrench/NovelTrench.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const novelTrenchScraper = require("./NovelTrenchScraper"); - -const router = express.Router(); - -router.get("/novels/", novelTrenchScraper.novelsScraper); - -router.get("/novel/:novelUrl", novelTrenchScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", novelTrenchScraper.chapterScraper); - -router.get("/search/", novelTrenchScraper.searchScraper); - -module.exports = router; diff --git a/src/en/noveltrench/icon.png b/src/en/noveltrench/icon.png deleted file mode 100644 index b54160b..0000000 Binary files a/src/en/noveltrench/icon.png and /dev/null differ diff --git a/src/en/novelupdates.js b/src/en/novelupdates.js new file mode 100644 index 0000000..db68dd8 --- /dev/null +++ b/src/en/novelupdates.js @@ -0,0 +1,310 @@ +import cheerio from "react-native-cheerio"; + +const sourceId = 50; + +const sourceName = "Novel Updates"; + +const baseUrl = "https://www.novelupdates.com/"; + +let headers = new Headers({ + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", +}); + +const popularNovels = async (page) => { + const totalPages = 100; + let url = `${baseUrl}series-ranking/?rank=week&pg=` + page; + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); + + const $ = cheerio.load(body); + + const novels = []; + + $("div.search_main_box_nu").each(function (res) { + const novelCover = $(this).find("img").attr("src"); + const novelName = $(this).find(".search_title > a").text(); + const novelUrl = $(this) + .find(".search_title > a") + .attr("href") + .split("/")[4]; + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}series/${novelUrl}`; + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { + sourceId, + sourceName, + url, + novelUrl, + }; + + novel.novelName = $(".seriestitlenu").text(); + + novel.novelCover = $(".seriesimg > img").attr("src"); + + novel.author = $("#showauthors").text().trim(); + + novel.genre = $("#seriesgenre").text().trim().replace(/\s/g, ","); + + novel.status = $("#editstatus").text().includes("Ongoing") + ? "Ongoing" + : "Completed"; + + let type = $("#showtype").text().trim(); + + let summary = $("#editdescription").text().trim(); + + novel.summary = summary + `\n\nType: ${type}`; + + let novelChapters = []; + + const novelId = $("input#mypostid").attr("value"); + + let formData = new FormData(); + formData.append("action", "nd_getchapters"); + formData.append("mygrr", 0); + formData.append("mypostid", parseInt(novelId)); + + const data = await fetch( + "https://www.novelupdates.com/wp-admin/admin-ajax.php", + { + method: "POST", + headers, + body: formData, + } + ); + const text = await data.text(); + + $ = cheerio.load(text); + + $("li.sp_li_chp").each(function () { + const chapterName = $(this).text().trim(); + + const releaseDate = null; + + const chapterUrl = + "https:" + $(this).find("a").first().next().attr("href"); + + novelChapters.push({ chapterName, releaseDate, chapterUrl }); + }); + + novel.chapters = novelChapters.reverse(); + + return novel; +}; + +function getLocation(href) { + var match = href.match( + /^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/ + ); + return match && `${match[1]}//${match[3]}`; +} + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = chapterUrl; + + // console.log("Original URL: ", url); + + let result, body; + + let chapterName = ""; + + try { + result = await fetch(url, { + method: "GET", + headers: headers, + }); + body = await result.text(); + + // console.log(result.url); + + // console.log("Redirected URL: ", result.url); + + $ = cheerio.load(body); + + let isWuxiaWorld = result.url.toLowerCase().includes("wuxiaworld"); + + let isBlogspot = result.url.toLowerCase().includes("blogspot"); + + let isTumblr = result.url.toLowerCase().includes("tumblr"); + + /** + * Checks if its a wwordpress site + */ + let isWordPress = + $('meta[name="generator"]').attr("content") || $("footer").text(); + + if (isWordPress) { + isWordPress = + isWordPress.toLowerCase().includes("wordpress") || + isWordPress.includes("Site Kit by Google") || + $(".powered-by").text().toLowerCase().includes("wordpress"); + } + + let isRainOfSnow = result.url.toLowerCase().includes("rainofsnow"); + + let isWebNovel = result.url.toLowerCase().includes("webnovel"); + + let isHostedNovel = result.url.toLowerCase().includes("hostednovel"); + + let isScribbleHub = result.url.toLowerCase().includes("scribblehub"); + + if (isWuxiaWorld) { + chapterText = $("#chapter-content").html(); + } else if (isRainOfSnow) { + chapterText = $("div.content").html(); + } else if (isTumblr) { + chapterText = $(".post").html(); + } else if (isBlogspot) { + $(".post-share-buttons").remove(); + + chapterText = $(".entry-content").html(); + } else if (isHostedNovel) { + chapterText = $(".chapter").html(); + } else if (isScribbleHub) { + chapterText = $("div.chp_raw").html(); + } else if (isWordPress) { + /** + * Remove wordpress bloat tags + */ + + const bloatClasses = [ + ".c-ads", + "#madara-comments", + "#comments", + ".content-comments", + ".sharedaddy", + ".wp-dark-mode-switcher", + ".wp-next-post-navi", + ".wp-block-buttons", + ".wp-block-columns", + ".post-cats", + ".sidebar", + ".author-avatar", + ".ezoic-ad", + ]; + + bloatClasses.map((tag) => $(tag).remove()); + + chapterText = + $(".entry-content").html() || + $(".single_post").html() || + $(".post-entry").html() || + $(".main-content").html() || + $("article.post").html() || + $(".content").html() || + $("#content").html(); + } else if (isWebNovel) { + chapterText = $(".cha-words").html(); + + if (!chapterText) { + chapterText = $("._content").html(); + } + } else { + /** + * Remove unnecessary tags + */ + const tags = ["nav", "header", "footer", ".hidden"]; + + tags.map((tag) => $(tag).remove()); + + chapterText = $("body").html(); + } + + if (!chapterText) { + chapterText = + "Chapter not available.\n\nReport if it's available in webview."; + } else { + /** + * Convert relative urls to absolute + */ + chapterText = chapterText.replace( + /href="\//g, + `href="${getLocation(result.url)}/` + ); + } + } catch (error) { + chapterText = `Chapter not available (Error: ${error.message}).\n\nReport if it's available in webview.`; + } + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = + "https://www.novelupdates.com/?s=" + + searchTerm + + "&post_type=seriesplans"; + + const res = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await res.text(); + + $ = cheerio.load(body); + + let novels = []; + + $("div.search_main_box_nu").each(function (res) { + const novelCover = $(this).find("img").attr("src"); + const novelName = $(this).find(".search_title > a").text(); + const novelUrl = $(this) + .find(".search_title > a") + .attr("href") + .split("/")[4]; + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + return novels; +}; + +const NovelUpdatesScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default NovelUpdatesScraper; diff --git a/src/en/novelupdates/NovelUpdates.js b/src/en/novelupdates/NovelUpdates.js deleted file mode 100644 index bfe97c1..0000000 --- a/src/en/novelupdates/NovelUpdates.js +++ /dev/null @@ -1,14 +0,0 @@ -// const express = require("express"); -// const novelUpdatesScraper = require("./NovelUpdatesScraper"); - -// const router = express.Router(); - -// router.get("/novels/", novelUpdatesScraper.novelsScraper); - -// router.get("/novel/:novelUrl", novelUpdatesScraper.novelScraper); - -// router.get("/novel/:novelUrl/:chapterUrl", novelUpdatesScraper.chapterScraper); - -// router.get("/search/", novelUpdatesScraper.searchScraper); - -// module.exports = router; diff --git a/src/en/novelupdates/NovelUpdatesScraper.js b/src/en/novelupdates/NovelUpdatesScraper.js deleted file mode 100644 index ba4804c..0000000 --- a/src/en/novelupdates/NovelUpdatesScraper.js +++ /dev/null @@ -1,194 +0,0 @@ -// import cheerio from "react-native-cheerio"; -// import { htmlToText } from "../helpers/htmlToText"; -// import moment from "moment"; - -// const sourceId = 50; - -// const sourceName = "Novel Updates"; - -// const baseUrl = "https://www.novelupdates.com/"; - -// let headers = new Headers({ -// "User-Agent": -// "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", -// }); - -// const popularNovels = async (page) => { -// const totalPages = 100; -// let url = `${baseUrl}series-ranking/?rank=sixmonths&pg=` + page; - -// const result = await fetch(url, { -// method: "GET", -// headers: headers, -// }); -// const body = await result.text(); - -// const $ = cheerio.load(body); - -// const novels = []; - -// $("div.search_main_box_nu").each(function (res) { -// const novelCover = $(this).find("img").attr("src"); -// const novelName = $(this).find(".search_title > a").text(); -// const novelUrl = $(this) -// .find(".search_title > a") -// .attr("href") -// .split("/")[4]; - -// const novel = { -// sourceId, -// novelName, -// novelCover, -// novelUrl, -// }; - -// novels.push(novel); -// }); - -// return { totalPages, novels }; -// }; - -// const parseNovelAndChapters = async (novelUrl) => { -// const url = `${baseUrl}series/${novelUrl}`; - -// const result = await fetch(url, { -// method: "GET", -// headers: headers, -// }); -// const body = await result.text(); - -// $ = cheerio.load(body); - -// let novel = { -// sourceId, -// sourceName, -// url, -// novelUrl, -// }; - -// novel.novelName = $(".seriestitlenu").text(); - -// novel.novelCover = $(".seriesimg > img").attr("src"); - -// novel.author = $("#showauthors").text().trim(); - -// novel.genre = $(".book-generes").text().trim(); - -// novel.status = $("#editstatus").text().includes("Ongoing") -// ? "Ongoing" -// : "Completed"; - -// novel.summary = $("#editdescription").text().trim(); - -// let novelChapters = []; - -// const novelId = $("input#mypostid").attr("value"); - -// let formData = new FormData(); -// formData.append("action", "nd_getchapters"); -// formData.append("mygrr", 0); -// formData.append("mypostid", parseInt(novelId)); - -// const data = await fetch( -// "https://www.novelupdates.com/wp-admin/admin-ajax.php", -// { -// method: "POST", -// headers, -// body: formData, -// } -// ); -// const text = await data.text(); - -// $ = cheerio.load(text); - -// $("li.sp_li_chp").each(function () { -// const chapterName = $(this).text().trim(); - -// const releaseDate = null; - -// const chapterUrl = -// "https:" + $(this).find("a").first().next().attr("href"); - -// novelChapters.push({ chapterName, releaseDate, chapterUrl }); -// }); - -// novel.chapters = novelChapters.reverse(); - -// return novel; -// }; - -// const parseChapter = async (novelUrl, chapterUrl) => { -// const url = chapterUrl; - -// const result = await fetch(url, { -// method: "GET", -// headers: headers, -// }); -// const body = await result.text(); - -// $ = cheerio.load(body); - -// let chapterName = ""; - -// let chapterText = body; - -// let nextChapter = null; -// let prevChapter = null; - -// const chapter = { -// sourceId, -// novelUrl, -// chapterUrl, -// chapterName, -// chapterText, -// nextChapter, -// prevChapter, -// }; - -// return chapter; -// }; - -// const searchNovels = async (searchTerm) => { -// const url = -// "https://www.novelupdates.com/?s=" + -// searchTerm + -// "&post_type=seriesplans"; - -// const res = await fetch(url, { -// method: "GET", -// headers: headers, -// }); -// const body = await res.text(); - -// $ = cheerio.load(body); - -// let novels = []; - -// $("div.search_main_box_nu").each(function (res) { -// const novelCover = $(this).find("img").attr("src"); -// const novelName = $(this).find(".search_title > a").text(); -// const novelUrl = $(this) -// .find(".search_title > a") -// .attr("href") -// .split("/")[4]; - -// const novel = { -// sourceId, -// novelName, -// novelCover, -// novelUrl, -// }; - -// novels.push(novel); -// }); -// return novels; -// }; - -// const NovelUpdatesScraper = { -// popularNovels, -// parseNovelAndChapters, -// parseChapter, -// searchNovels, -// }; - -// export default NovelUpdatesScraper; diff --git a/src/en/novelupdates/icon.png b/src/en/novelupdates/icon.png deleted file mode 100644 index 85558d3..0000000 Binary files a/src/en/novelupdates/icon.png and /dev/null differ diff --git a/src/en/novelupdatescc/NovelUpdatesCcScraper.js b/src/en/novelupdatescc.js similarity index 57% rename from src/en/novelupdatescc/NovelUpdatesCcScraper.js rename to src/en/novelupdatescc.js index 1265ea0..9d58e1a 100644 --- a/src/en/novelupdatescc/NovelUpdatesCcScraper.js +++ b/src/en/novelupdatescc.js @@ -1,11 +1,9 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://www.novelupdates.cc/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; const result = await fetch(baseUrl); const body = await result.text(); @@ -20,7 +18,7 @@ const novelsScraper = async (req, res) => { let novelUrl = $(this).find("a").attr("href").slice(1); const novel = { - extensionId: 18, + sourceId: 18, novelName, novelCover, novelUrl, @@ -29,11 +27,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}${novelUrl}/`; const result = await fetch(url); @@ -43,26 +40,26 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 18; + novel.sourceId = 18; novel.sourceName = "NovelUpdates.cc"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".book-name").text(); novel.novelCover = $("div.book-img > img").attr("src"); - novel["Genre(s)"] = $("div.book-catalog > span.txt").text(); + novel.genre = $("div.book-catalog > span.txt").text(); - novel.Status = $("div.book-state > span.txt").text(); + novel.status = $("div.book-state > span.txt").text(); - novel["Author(s)"] = $("div.author > span.name").text(); + novel.author = $("div.author > span.name").text(); - novelSummary = $("div.content > p.desc").html(); - novel.novelSummary = htmlToText(novelSummary); + novelSummary = $("div.content > p.desc").text(); + novel.summary = novelSummary.trim(); let novelChapters = []; @@ -78,15 +75,12 @@ const novelScraper = async (req, res) => { }); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}${chapterUrl}`; const result = await fetch(url); @@ -96,39 +90,18 @@ const chapterScraper = async (req, res) => { const chapterName = $("h1.chapter-title").text(); let chapterText = $("div.chapter-entity").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($("a.next").length) { - nextChapter = $("a.next").attr("href").slice(1); - } - - let prevChapter = null; - - if ($("a.prev").length) { - prevChapter = $("a.prev").attr("href").slice(1); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - const chapter = { - extensionId: 18, + sourceId: 18, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}search/${searchTerm}/1`; const result = await fetch(url); @@ -147,7 +120,7 @@ const searchScraper = async (req, res) => { let novelUrl = $(this).find("a").attr("href").slice(1); const novel = { - extensionId: 18, + sourceId: 18, novelName, novelCover, novelUrl, @@ -156,12 +129,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = wuxiaWorldCoScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const novelUpdatesCcScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default novelUpdatesCcScraper; diff --git a/src/en/novelupdatescc/NovelUpdatesCc.js b/src/en/novelupdatescc/NovelUpdatesCc.js deleted file mode 100644 index bdd321e..0000000 --- a/src/en/novelupdatescc/NovelUpdatesCc.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const NovelUpdatesCcScraper = require("./NovelUpdatesCcScraper"); - -const router = express.Router(); - -router.get("/novels/", NovelUpdatesCcScraper.novelsScraper); - -router.get("/novel/:novelUrl", NovelUpdatesCcScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:chapterUrl", - NovelUpdatesCcScraper.chapterScraper -); - -router.get("/search/", NovelUpdatesCcScraper.searchScraper); - -module.exports = router; diff --git a/src/en/novelupdatescc/icon.png b/src/en/novelupdatescc/icon.png deleted file mode 100644 index b732fa7..0000000 Binary files a/src/en/novelupdatescc/icon.png and /dev/null differ diff --git a/src/en/rainofsnow.js b/src/en/rainofsnow.js new file mode 100644 index 0000000..aa2576f --- /dev/null +++ b/src/en/rainofsnow.js @@ -0,0 +1,140 @@ +import cheerio from "react-native-cheerio"; +import { Status } from "../helpers/constants"; + +const sourceId = 78; + +const sourceName = "RainOfSnow"; + +const baseUrl = "https://rainofsnow.com/"; + +const popularNovels = async (page) => { + let url = baseUrl + "novels/page/" + page; + + const totalPages = 5; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".minbox").each(function () { + const novelName = $(this).find("h3").text(); + const novelCover = $(this).find("img").attr("data-src"); + const novelUrl = $(this).find("h3 > a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { novels, totalPages }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = novelUrl; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url, novelUrl }; + + novel.novelName = $("h2").text().trim(); + + novel.novelCover = $(".imagboca1 > img").attr("data-src"); + + novel.summary = $("#synop").text().trim(); + + novel.genre = $( + "body > div.queen > div > div > div.row > div.col-md-12.col-lg-7 > div > div.backcolor1 > ul > li:nth-child(5) > small" + ) + .text() + .trim(); + + novel.author = $( + "body > div.queen > div > div > div.row > div.col-md-12.col-lg-7 > div > div.backcolor1 > ul > li:nth-child(2) > small" + ).text(); + + novel.status = Status.UNKNOWN; + + let novelChapters = []; + + $("#chapter") + .find("li") + .each(function () { + const chapterName = $(this).find(".chapter").first().text().trim(); + const releaseDate = $(this).find("small").text(); + const chapterUrl = $(this).find("a").attr("href"); + + novelChapters.push({ chapterName, releaseDate, chapterUrl }); + }); + + novel.chapters = novelChapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const result = await fetch(chapterUrl); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = $(".content > h2").text(); + let chapterText = $(".content").html(); + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + let url = baseUrl + "?s=" + searchTerm; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".minbox").each(function () { + const novelName = $(this).find("h3").text(); + const novelCover = $(this).find("img").attr("data-src"); + const novelUrl = $(this).find("h3 > a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const RainOfSnowScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default RainOfSnowScraper; diff --git a/src/en/rainofsnow/icon.png b/src/en/rainofsnow/icon.png deleted file mode 100644 index 82b36da..0000000 Binary files a/src/en/rainofsnow/icon.png and /dev/null differ diff --git a/src/en/ranobes.js b/src/en/ranobes.js new file mode 100644 index 0000000..a9b724d --- /dev/null +++ b/src/en/ranobes.js @@ -0,0 +1,228 @@ +import cheerio from "react-native-cheerio"; + +import moment from "moment"; +import { showToast } from "../../hooks/showToast"; + +const sourceId = 51; + +const sourceName = "Ranobes"; + +const baseUrl = "https://ranobes.net/"; + +const popularNovels = async (page) => { + const totalPages = 152; + let url = `${baseUrl}novels/page/${page}`; + + let headers = new Headers({ + referer: "https://ranobes.net/", + "user-agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }); + + const result = await fetch(url, { method: "GET", headers: headers }); + const body = await result.text(); + + let $ = cheerio.load(body); + + let novels = []; + + $("article.block.story.shortstory.mod-poster").each(function () { + const novelName = $(this).find("h2.title").text(); + let novelCover = $(this).find("figure").attr("style"); + + let novelUrl = $(this) + .find("a") + .attr("href") + .replace(baseUrl + "novels/", ""); + + novelCover = novelCover + .match(/background-image: url(.*);/)[1] + .slice(1, -1); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + showToast("Ranobes: Only has latest 25 chapters."); + + const url = `${baseUrl}novels/${novelUrl}`; + + const result = await fetch(url); + const body = await result.text(); + + let $ = cheerio.load(body); + + let novel = { + sourceId, + sourceName, + url, + novelUrl, + }; + + novel.novelName = $('meta[property="og:title"]').attr("content"); + + novel.novelCover = baseUrl + $(".poster > a > img").attr("src"); + + novel.author = $("dl.author > dd").text(); + + novel.genre = $(".book-generes").text().trim(); + + novel.summary = $('div[itemprop="description"]').text().trim(); + + novel.author = $('span[itemprop="author"]').text(); + novel.genre = $('span[itemprop="genre"]').text().replace(", ", ","); + + let tempNovelUrl = $(".r-fullstory-chapters-foot") + .find("a") + .next() + .next() + .attr("href") + .split("/")[2]; + + const chapterListUrl = `${baseUrl}up/${tempNovelUrl}/page/1`; + + const chaptersHtml = await fetch(chapterListUrl); + const chapterHtmlToString = await chaptersHtml.text(); + + $ = cheerio.load(chapterHtmlToString); + + // let lastPage = $("div.pages > a").last().text(); + + let novelChapters = []; + + $("#dle-content > div.cat_block.cat_line").each(function () { + const chapterName = $(this).find(".title").text(); + + let releaseDate = new Date(); + + let timeAgo = $(this).find("small").text(); + + timeAgo = timeAgo.match(/\d+/)[0]; + + if (timeAgo.includes("hours ago")) { + releaseDate.setTime(releaseDate.getTime() - timeAgo); + } + + if (timeAgo.includes("months ago")) { + releaseDate.setMonth(releaseDate.getMonth() - timeAgo); + } + + if (timeAgo.includes("days ago")) { + releaseDate.setDate(releaseDate.getDate() - timeAgo); + } + + releaseDate = moment(releaseDate).format("MM/DD/YY"); + + const chapterUrl = $(this) + .find("a") + .attr("href") + .replace(baseUrl + "up/", ""); + + const chapter = { + chapterName, + releaseDate, + chapterUrl, + }; + + novelChapters.push(chapter); + }); + + // for (i = 1; i <= lastPage; i++) { + // const chapterPageUrl = `${baseUrl}up/${tempNovelUrl}/page/${i}`; + + // const chaptersHtml = await fetch(chapterPageUrl); + // const chapterHtmlToString = await chaptersHtml.text(); + // $ = cheerio.load(chapterHtmlToString); + + // $("#dle-content > div.cat_block.cat_line").each(function () { + // const chapterName = $(this).find(".title").text(); + + // let releaseDate = new Date(); + + // let timeAgo = $(this).find("small").text(); + + // timeAgo = timeAgo.match(/\d+/)[0]; + + // if (timeAgo.includes("hours ago")) { + // releaseDate.setTime(releaseDate.getTime() - timeAgo); + // } + + // if (timeAgo.includes("months ago")) { + // releaseDate.setMonth(releaseDate.getMonth() - timeAgo); + // } + + // if (timeAgo.includes("days ago")) { + // releaseDate.setDate(releaseDate.getDate() - timeAgo); + // } + + // releaseDate = moment(releaseDate).format("MM/DD/YY"); + + // const chapterUrl = $(this) + // .find("a") + // .attr("href") + // .replace(baseUrl + "up/", ""); + + // const chapter = { + // chapterName, + // releaseDate, + // chapterUrl, + // }; + + // novelChapters.push(chapter); + // }); + // } + + novel.chapters = novelChapters.reverse(); + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}up/${chapterUrl}`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = $("h1.title").text(); + + let chapterText = $("#arrticle").html(); + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + /** + * TODO + */ + + showToast("Ranobes: Search does not work"); + return []; +}; + +const RanobesScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default RanobesScraper; diff --git a/src/en/ranobes/icon.png b/src/en/ranobes/icon.png deleted file mode 100644 index 3633d25..0000000 Binary files a/src/en/ranobes/icon.png and /dev/null differ diff --git a/src/en/readlightnovel.js b/src/en/readlightnovel.js new file mode 100644 index 0000000..57d4614 --- /dev/null +++ b/src/en/readlightnovel.js @@ -0,0 +1,214 @@ +import cheerio from "react-native-cheerio"; + +const sourceId = 2; + +const sourceName = "ReadLightNovel"; + +const lang = "en"; + +const baseUrl = "https://www.readlightnovel.me"; + +const searchUrl = "https://www.readlightnovel.me/detailed-search"; + +const popularNovels = async (page) => { + let totalPages = 1751; + const url = `${baseUrl}/top-novel/${page}`; + + const result = await fetch(url); + const body = await result.text(); + + const $ = cheerio.load(body); + + const novels = []; + + $(".top-novel-block").each(function (result) { + const novelName = $(this).find("h2 > a").text(); + const novelCover = $(this).find("img").attr("src"); + const novelUrl = $(this) + .find("h2 > a") + .attr("href") + .replace(`${baseUrl}/`, ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}/${novelUrl}`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel; + + const novelName = $(".block-title > h1").text(); + + const novelCover = $(".novel-cover > a > img").attr("src"); + + let author, artist, genre, summary, status; + + $(".novel-detail-item").each(function (result) { + const detailName = $(this).find(".novel-detail-header > h6").text(); + const detail = $(this).find(".novel-detail-body").text().trim(); + + switch (detailName) { + case "Genre": + genre = detail.trim().replace(/\s{2,}/g, ","); + + break; + case "Author(s)": + author = detail; + break; + case "Artist(s)": + artist = detail; + break; + case "Description": + summary = detail; + break; + case "Status": + status = detail; + break; + } + }); + + let chapters = []; + + $(".panel").each(function () { + let volumeName = $(this).find("h4.panel-title").text(); + + $(this) + .find("ul.chapter-chs > li") + .each(function () { + let chapterName = $(this).find("a").text(); + + const releaseDate = null; + + const chapterUrl = $(this) + .find("a") + .attr("href") + .replace(`${baseUrl}/${novelUrl}/`, ""); + + if (volumeName.includes("Volume")) { + chapterName = volumeName + " " + chapterName; + } + + const chapter = { + chapterName, + releaseDate, + chapterUrl, + }; + + chapters.push(chapter); + }); + }); + + novel = { + sourceId, + sourceName, + url, + novelUrl, + novelName, + novelCover, + genre, + author, + status, + artist, + summary, + chapters, + }; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}/${novelUrl}/${chapterUrl}/`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + $(".block-title > h1").find("a").remove(); + + const chapterName = $(".block-title > h1").text().replace(" - ", ""); + + $(".alert").remove(); + $(".hidden").remove(); + $("iframe").remove(); + $("button").remove(); + $(".hid").remove(); + $("center").remove(); + $( + 'div[style="float: left; margin-top: 20px; font-style: italic;margin-left: 50px; font-size: 14px;"]' + ).remove(); + $('div[style="float:left;margin-top:15px;margin-bottom:15px;"]').remove(); + + let chapterText = $(".desc").html(); + + const chapter = { + sourceId: 2, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const formData = new FormData(); + formData.append("keyword", searchTerm); + formData.append("search", 1); + + const result = await fetch(searchUrl, { + method: "POST", + body: formData, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".top-novel-block").each(function (result) { + const novelName = $(this).find(".top-novel-header > h2 > a").text(); + const novelCover = $(this).find("img").attr("src"); + + const novelUrl = $(this) + .find(".top-novel-header > h2 > a") + .attr("href") + .replace(`${baseUrl}/`, ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const ReadLightNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default ReadLightNovelScraper; diff --git a/src/en/readlightnovel/ReadLightNovel.js b/src/en/readlightnovel/ReadLightNovel.js deleted file mode 100644 index bb5e049..0000000 --- a/src/en/readlightnovel/ReadLightNovel.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const readLightNovelScraper = require("./ReadLightNovelScraper.js"); - -const router = express.Router(); - -router.get("/novels/", readLightNovelScraper.novelsScraper); - -router.get("/novel/:novelUrl", readLightNovelScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:chapterUrl/:volumeUrl?", - readLightNovelScraper.chapterScraper -); - -router.get("/search/", readLightNovelScraper.searchScraper); - -module.exports = router; diff --git a/src/en/readlightnovel/ReadLightNovelScraper.js b/src/en/readlightnovel/ReadLightNovelScraper.js deleted file mode 100644 index b0f8727..0000000 --- a/src/en/readlightnovel/ReadLightNovelScraper.js +++ /dev/null @@ -1,238 +0,0 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const request = require("request"); -const { parseHtml } = require("../../helper"); - -const baseUrl = "https://www.readlightnovel.org"; -const searchUrl = "https://www.readlightnovel.org/detailed-search"; - -const novelsScraper = async (req, res) => { - const url = `${baseUrl}/top-novel?change_type=top_rated`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $(".top-novel-block").each(function (result) { - const novelName = $(this).find(".top-novel-header > h2 > a").text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this) - .find(".top-novel-header > h2 > a") - .attr("href") - .replace(`${baseUrl}/`, ""); - novelUrl = novelUrl + "/"; - - const novel = { - extensionId: 2, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}/${novelUrl}`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novel = {}; - - novel.extensionId = 2; - - novel.sourceName = "ReadLightNovel"; - - novel.sourceUrl = url; - - novel.novelUrl = novelUrl + "/"; - - novel.novelName = $(".block-title > h1").text(); - - novel.novelCover = $(".novel-cover > a > img").attr("src"); - - $(".novel-detail-item").each(function (result) { - const detailName = $(this) - .find(".novel-detail-header > h6") - .text() - .replace(/[\t\n]/g, "") - .trim(); - const detail = $(this).find(".novel-detail-body").text().trim(); - - novel[detailName] = detail; - }); - - novel.Alternative = novel["Alternative Names"]; - novel.novelSummary = novel.Description; - novel["Genre(s)"] = novel.Genre.replace(/[\t\n]/g, ", "); - novel.Release = novel.Year; - - delete novel["Alternative Names"]; - delete novel.Description; - delete novel.Genre; - delete novel.Year; - - let novelChapters = []; - - $(".panel").each(function (res) { - let volumeName = $(this).find("h4.panel-title").text(); - - $(this) - .find("ul.chapter-chs > li") - .each(function (result) { - chapterName = $(this) - .find("a") - .text() - .replace(/[\t\n]/g, "") - .trim(); - - releaseDate = null; - - chapterUrl = $(this).find("a").attr("href"); - - chapterUrl = chapterUrl.replace(`${baseUrl}/${novelUrl}/`, ""); - - if (volumeName.includes("Volume")) { - chapterName = volumeName + " " + chapterName; - } - - const chapter = { - chapterName, - releaseDate, - chapterUrl, - }; - - novelChapters.push(chapter); - }); - }); - - novel.novelChapters = novelChapters; - - res.json(novel); -}; - -const chapterScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - const volumeUrl = req.params.volumeUrl; - - let optionalUrl; - - if (volumeUrl) { - optionalUrl = volumeUrl; - } else { - optionalUrl = ""; - } - - const url = `${baseUrl}/${novelUrl}/${chapterUrl}/${optionalUrl}`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - $(".block-title > h1").find("a").remove(); - - const chapterName = $(".block-title > h1").text().replace(" - ", ""); - - $(".desc") - .find("hr") - .each(function (result) { - $(this).remove(); - }); - - $(".alert").remove(); - $(".hidden").remove(); - - let chapterText = $(".desc").html(); - - chapterText = parseHtml(chapterText).replace( - /\n\nSponsored Content\n\n|If audio player doesn't work, press Stop then Play button again/g, - "" - ); - - let nextChapter = null; - - if ($("a.next.next-link").length) { - nextChapter = $("a.next.next-link") - .attr("href") - .replace(`${baseUrl}/${novelUrl}/`, ""); - } - - let prevChapter = null; - - if ($("a.prev.prev-link").length) { - prevChapter = $("a.prev.prev-link") - .attr("href") - .replace(`${baseUrl}/${novelUrl}/`, ""); - } - - chapterUrl = chapterUrl + "/" + optionalUrl; - - const chapter = { - extensionId: 2, - novelUrl, - chapterUrl, - chapterName, - chapterText, - nextChapter, - prevChapter, - }; - - res.json(chapter); -}; - -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - - request.post( - { url: searchUrl, form: { keyword: searchTerm, search: 1 } }, - (err, response, body) => { - $ = cheerio.load(body); - - let novels = []; - - $(".top-novel-block").each(function (result) { - const novelName = $(this) - .find(".top-novel-header > h2 > a") - .text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this) - .find(".top-novel-header > h2 > a") - .attr("href") - .replace(`${baseUrl}/`, ""); - novelUrl = novelUrl + "/"; - - const novel = { - extensionId: 2, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); - } - ); -}; - -module.exports = readLightNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, -}; diff --git a/src/en/readlightnovel/icon.png b/src/en/readlightnovel/icon.png deleted file mode 100644 index 03aef20..0000000 Binary files a/src/en/readlightnovel/icon.png and /dev/null differ diff --git a/src/en/readlightnovelcc/ReadLightNovelCcScraper.js b/src/en/readlightnovelcc.js similarity index 58% rename from src/en/readlightnovelcc/ReadLightNovelCcScraper.js rename to src/en/readlightnovelcc.js index c2d3aa5..e926cb1 100644 --- a/src/en/readlightnovelcc/ReadLightNovelCcScraper.js +++ b/src/en/readlightnovelcc.js @@ -1,11 +1,9 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://m.readlightnovel.cc/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; const result = await fetch(baseUrl); const body = await result.text(); @@ -20,7 +18,7 @@ const novelsScraper = async (req, res) => { let novelUrl = $(this).find("a").attr("href").slice(1); const novel = { - extensionId: 19, + sourceId: 19, novelName, novelCover, novelUrl, @@ -29,11 +27,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}${novelUrl}/`; const result = await fetch(url); @@ -43,26 +40,26 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 19; + novel.sourceId = 19; novel.sourceName = "ReadLightNovel.cc"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".book-name").text(); novel.novelCover = $("div.book-img > img").attr("src"); - novel["Genre(s)"] = $("div.book-catalog > span.txt").text(); + novel.genre = $("div.book-catalog > span.txt").text(); - novel.Status = $("div.book-state > span.txt").text(); + novel.status = $("div.book-state > span.txt").text(); - novel["Author(s)"] = $("div.author > span.name").text(); + novel.author = $("div.author > span.name").text(); - novelSummary = $("div.content > p.desc").html(); - novel.novelSummary = htmlToText(novelSummary); + novelSummary = $("div.content > p.desc").text(); + novel.summary = novelSummary.trim(); let novelChapters = []; @@ -78,15 +75,12 @@ const novelScraper = async (req, res) => { }); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}${chapterUrl}`; const result = await fetch(url); @@ -96,39 +90,18 @@ const chapterScraper = async (req, res) => { const chapterName = $("h1.chapter-title").text(); let chapterText = $("div.chapter-entity").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($("a.next").length) { - nextChapter = $("a.next").attr("href").slice(1); - } - - let prevChapter = null; - - if ($("a.prev").length) { - prevChapter = $("a.prev").attr("href").slice(1); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - const chapter = { - extensionId: 19, + sourceId: 19, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}search/${searchTerm}/1`; const result = await fetch(url); @@ -147,7 +120,7 @@ const searchScraper = async (req, res) => { let novelUrl = $(this).find("a").attr("href").slice(1); const novel = { - extensionId: 19, + sourceId: 19, novelName, novelCover, novelUrl, @@ -156,12 +129,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = wuxiaWorldCoScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const readLightNovelCcScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default readLightNovelCcScraper; diff --git a/src/en/readlightnovelcc/ReadLightNovelCc.js b/src/en/readlightnovelcc/ReadLightNovelCc.js deleted file mode 100644 index 740ef18..0000000 --- a/src/en/readlightnovelcc/ReadLightNovelCc.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const ReadLightNovelCc = require("./ReadLightNovelCcScraper"); - -const router = express.Router(); - -router.get("/novels/", ReadLightNovelCc.novelsScraper); - -router.get("/novel/:novelUrl", ReadLightNovelCc.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", ReadLightNovelCc.chapterScraper); - -router.get("/search/", ReadLightNovelCc.searchScraper); - -module.exports = router; diff --git a/src/en/readlightnovelcc/icon.png b/src/en/readlightnovelcc/icon.png deleted file mode 100644 index b732fa7..0000000 Binary files a/src/en/readlightnovelcc/icon.png and /dev/null differ diff --git a/src/en/readnovelfull/ReadNovelFullScraper.js b/src/en/readnovelfull.js similarity index 65% rename from src/en/readnovelfull/ReadNovelFullScraper.js rename to src/en/readnovelfull.js index ef5ff7a..07c0343 100644 --- a/src/en/readnovelfull/ReadNovelFullScraper.js +++ b/src/en/readnovelfull.js @@ -1,11 +1,10 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://readnovelfull.com"; -const novelsScraper = async (req, res) => { - const url = `${baseUrl}/most-popular-novel`; +const popularNovels = async (page) => { + let totalPages = 61; + const url = `${baseUrl}/most-popular-novel?page=${page}`; const result = await fetch(url); const body = await result.text(); @@ -26,7 +25,7 @@ const novelsScraper = async (req, res) => { const novelCover = $(this).find("img").attr("src"); const novel = { - extensionId: 4, + sourceId: 4, novelUrl, novelName, novelCover, @@ -35,12 +34,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}/${novelUrl}.html`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}/${novelUrl.slice(0, -1)}.html`; const result = await fetch(url); const body = await result.text(); @@ -49,37 +47,37 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 4; + novel.sourceId = 4; novel.sourceName = "ReadNovelFull"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $("div.book > img").attr("alt"); novel.novelCover = $("div.book > img").attr("src"); - novel.novelSummary = $("div.desc-text").text(); + novel.summary = $("div.desc-text").text().trim(); - novel["Author(s)"] = $("li > h3") + novel.author = $("li > h3") .filter(function () { return $(this).text().trim() === "Author:"; }) .siblings() .text(); - novel["Genre(s)"] = $("li") + novel.genre = $("li") .filter(function () { return $(this).find("h3").text().trim() === "Genre:"; }) .text() .replace("Genre:", ""); - novel["Artist(s)"] = null; + novel.artist = null; - novel.Status = $("li > h3") + novel.status = $("li > h3") .filter(function () { return $(this).text().trim() === "Status:"; }) @@ -105,7 +103,7 @@ const novelScraper = async (req, res) => { let chapterName = $(this).find("a").attr("title"); let releaseDate = null; let chapterUrl = $(this).find("a").attr("href"); - chapterUrl = chapterUrl.replace(`/${novelUrl}/`, ""); + chapterUrl = chapterUrl.replace(`/${novelUrl}`, ""); novelChapters.push({ chapterName, @@ -116,16 +114,13 @@ const novelScraper = async (req, res) => { return novelChapters; }; - novel.novelChapters = await getChapters(novelId); + novel.chapters = await getChapters(novelId); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl.replace(".html/", ""); - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}/${novelUrl}${chapterUrl}`; const result = await fetch(url); const body = await result.text(); @@ -134,37 +129,18 @@ const chapterScraper = async (req, res) => { const chapterName = $(".chr-title").attr("title"); let chapterText = $("#chr-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - if ($("a#next_chap").attr("href")) { - nextChapter = $("a#next_chap") - .attr("href") - .replace(`/${novelUrl}/`, ""); - } - - let prevChapter = null; - if ($("a#prev_chap").attr("href")) { - prevChapter = $("a#prev_chap") - .attr("href") - .replace(`/${novelUrl}/`, ""); - } - const chapter = { - extensionId: 4, + sourceId: 4, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; +const searchNovels = async (searchTerm) => { const searchUrl = `https://readnovelfull.com/search?keyword=`; const url = `${searchUrl}${searchTerm}`; @@ -188,7 +164,7 @@ const searchScraper = async (req, res) => { const novelCover = $(this).find("img").attr("src"); const novel = { - extensionId: 4, + sourceId: 4, novelUrl, novelName, novelCover, @@ -197,12 +173,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = readNovelFullScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const readNovelFullScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default readNovelFullScraper; diff --git a/src/en/readnovelfull/ReadNovelFull.js b/src/en/readnovelfull/ReadNovelFull.js deleted file mode 100644 index 44e6dfa..0000000 --- a/src/en/readnovelfull/ReadNovelFull.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const readNovelFullScraper = require("./ReadNovelFullScraper"); - -const router = express.Router(); - -router.get("/novels/", readNovelFullScraper.novelsScraper); - -router.get("/novel/:novelUrl", readNovelFullScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", readNovelFullScraper.chapterScraper); - -router.get("/search/", readNovelFullScraper.searchScraper); - -module.exports = router; diff --git a/src/en/readnovelfull/icon.png b/src/en/readnovelfull/icon.png deleted file mode 100644 index 5fc2bd6..0000000 Binary files a/src/en/readnovelfull/icon.png and /dev/null differ diff --git a/src/en/readwn.js b/src/en/readwn.js new file mode 100644 index 0000000..2ea0f3f --- /dev/null +++ b/src/en/readwn.js @@ -0,0 +1,193 @@ +import QueryString from "qs"; +import cheerio from "react-native-cheerio"; + +const sourceId = 68; + +const sourceName = "Readwn.com"; + +const baseUrl = "https://www.readwn.com/"; + +const popularNovels = async (page) => { + let url = `${baseUrl}list/all/all-onclick-${page - 1}.html`; + const totalPages = 281; + + const headers = new Headers({ + "user-agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", + }); + + const result = await fetch(url, { method: "GET", headers }); + const body = await result.text(); + + console.log(body); + + $ = cheerio.load(body); + + let novels = []; + + $("li.novel-item").each(function () { + const novelName = $(this).find("h4").text(); + const novelUrl = $(this).find("a").attr("href").split("/").pop(); + + const novelCover = + baseUrl + $(this).find(".novel-cover > img").attr("data-src"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + return { novels, totalPages }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}novel/${novelUrl}`; + + const headers = new Headers({ + "user-agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", + }); + + const result = await fetch(url, { method: "GET", headers }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { + sourceId, + sourceName, + url, + novelUrl, + }; + + novel.novelUrl = novelUrl; + + novel.novelName = $("h1.novel-title").text(); + + novel.novelCover = baseUrl + $("figure.cover > img").attr("data-src"); + + novel.summary = $("p.description").text().trim(); + + novel.genre = ""; + + $("div.categories > ul > li").each(function () { + novel.genre += $(this).text().trim() + ","; + }); + + $("div.header-stats > span").each(function () { + if ($(this).find("small").text() === "Status") { + novel.status = $(this).find("strong").text(); + } + }); + + novel.genre = novel.genre.slice(0, -1); + + novel.author = $("span[itemprop=author]").text(); + + let novelChapters = []; + + const novelId = novelUrl.split("/").pop().replace(".html", ""); + + const latestChapterNumber = $(".header-stats") + .find("span > strong") + .first() + .text() + .trim(); + + for (let i = 1; i <= latestChapterNumber; i++) { + const chapterName = `Chapter ${i}`; + const chapterUrl = `${novelId}_${i}`; + const releaseDate = null; + + const chapter = { chapterName, releaseDate, chapterUrl }; + + novelChapters.push(chapter); + } + + novel.chapters = novelChapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}novel/${chapterUrl}.html`; + + const headers = new Headers({ + "user-agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", + }); + + const result = await fetch(url, { method: "GET", headers }); + const body = await result.text(); + + $ = cheerio.load(body); + + const chapterName = $(".titles > h2").text(); + let chapterText = $(".chapter-content").html(); + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const searchUrl = "https://www.readwn.com/e/search/index.php"; + + const result = await fetch(searchUrl, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Referer: "https://www.readwn.com/search.html", + Origin: "https://www.readwn.com", + "user-agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", + }, + method: "POST", + body: QueryString.stringify({ + show: "title", + tempid: 1, + tbname: "news", + keyboard: searchTerm, + }), + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $("li.novel-item").each(function () { + const novelName = $(this).find("h4").text(); + const novelCover = baseUrl + $(this).find("img").attr("src"); + const novelUrl = $(this).find("a").attr("href").split("/").pop(); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const ReadwnScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default ReadwnScraper; diff --git a/src/en/readwn/icon.png b/src/en/readwn/icon.png deleted file mode 100644 index 8673181..0000000 Binary files a/src/en/readwn/icon.png and /dev/null differ diff --git a/src/en/reaperscans.js b/src/en/reaperscans.js new file mode 100644 index 0000000..8fb495f --- /dev/null +++ b/src/en/reaperscans.js @@ -0,0 +1,160 @@ +import cheerio from "react-native-cheerio"; +import { Status } from "../helpers/constants"; + +const baseUrl = "https://reaperscans.com"; + +const sourceId = 67; +const sourceName = "ReaperScans"; + +const popularNovels = async (page) => { + let url = `${baseUrl}/all-series/novels/`; + let totalPages = 1; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".page-item-detail").each(function (result) { + const novelName = $(this).find(".item-summary h3").text().trim(); + const novelCover = $(this).find(".img-responsive").attr("data-src"); + + let novelUrl = $(this).find("div > a").attr("href"); + novelUrl = novelUrl.replace(`${baseUrl}/`, ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}/${novelUrl}`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, url, sourceName }; + + $(".post-title > h3 > span").remove(); + + novel.novelUrl = novelUrl; + + novel.novelName = $(".post-title > h1").text().trim(); + + novel.novelCover = $(".summary_image > a > img").attr("data-src"); + + $(".post-content_item").each(function () { + const detailName = $(this).find(".summary-heading > h5").text().trim(); + const detail = $(this).find(".summary-content").text().trim(); + + switch (detailName) { + case "Genre(s)": + novel.genre = detail.replace(/[\t\n]/g, ","); + break; + case "Author(s)": + novel.author = detail; + break; + case "Status": + novel.status = detail.includes("OnGoing") + ? Status.ONGOING + : Status.COMPLETED; + break; + } + }); + + $(".description-summary > div.summary__content").find("em").remove(); + $(".premium-block").remove(); + + novel.summary = $("div.summary__content").text().trim(); + + let novelChapters = []; + + $(".wp-manga-chapter").each(function (result) { + $("i").remove(); + + const chapterName = $(this).find("a").text().trim(); + const releaseDate = null; + + const chapterUrl = $(this).find("a").attr("href").replace(url, ""); + + const chapter = { chapterName, releaseDate, chapterUrl }; + + novelChapters.push(chapter); + }); + + novel.chapters = novelChapters.reverse(); + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + const chapterName = $("#chapter-heading").text(); + let chapterText = $(".reading-content").html(); + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".c-tabs-item__content").each(function (result) { + const novelName = $(this).find(".post-title > h3").text().trim(); + const novelCover = $(this).find("div > div > a > img").attr("data-src"); + + let novelUrl = $(this).find("div > div > a").attr("href"); + novelUrl = novelUrl.replace(`${baseUrl}/`, ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const ReaperScansScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default ReaperScansScraper; diff --git a/src/en/reaperscans/icon.png b/src/en/reaperscans/icon.png deleted file mode 100644 index 5d83212..0000000 Binary files a/src/en/reaperscans/icon.png and /dev/null differ diff --git a/src/en/royalroad/RoyalRoadScraper.js b/src/en/royalroad.js similarity index 50% rename from src/en/royalroad/RoyalRoadScraper.js rename to src/en/royalroad.js index 4fb7e38..9f52285 100644 --- a/src/en/royalroad/RoyalRoadScraper.js +++ b/src/en/royalroad.js @@ -1,12 +1,13 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; + +import { parseMadaraDate } from "../helpers/parseDate"; +import { Status } from "../helpers/constants"; const baseUrl = "https://www.royalroad.com/"; -const novelsScraper = async (req, res) => { - let url = baseUrl + "fictions/best-rated?page=1"; +const popularNovels = async (page) => { + const totalPages = 1846; + let url = baseUrl + "fictions/best-rated?page=" + page; const result = await fetch(url); const body = await result.text(); @@ -15,17 +16,21 @@ const novelsScraper = async (req, res) => { let novels = []; - $("div.fiction-list-item.row").each(function (result) { + $("div.fiction-list-item.row").each(function () { const novelName = $(this).find("h2.fiction-title").text().trim(); - const novelCover = $(this).find("img").attr("src"); + let novelCover = $(this).find("img").attr("src"); + if (novelCover === "/Content/Images/nocover-new-min.png") { + novelCover = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + } let novelUrl = $(this) .find("h2.fiction-title > a") .attr("href") .replace("/fiction/", ""); const novel = { - extensionId: 34, + sourceId: 34, novelName, novelCover, novelUrl, @@ -34,13 +39,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelId = req.params.novelId; - const novelUrl = req.params.novelUrl; - const url = baseUrl + "fiction/" + +novelId + "/" + novelUrl; +const parseNovelAndChapters = async (novelUrl) => { + const url = baseUrl + "fiction/" + novelUrl; const result = await fetch(url); const body = await result.text(); @@ -49,48 +52,56 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 34; + novel.sourceId = 34; novel.sourceName = "Royal Road"; - novel.sourceUrl = url; + novel.url = url; novel.novelUrl = novelUrl; novelName = $("h1").text(); novel.novelName = novelName; - novel["Author(s)"] = $("h1").next().text().replace("by ", "").trim(); - novel.novelCover = $("img.thumbnail").attr("src"); + let novelCover = $("img.thumbnail").attr("src"); - novel.novelSummary = $("div.description").text().trim(); + if (novelCover === "/Content/Images/nocover-new-min.png") { + novelCover = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + } - novel["Genre(s)"] = $("span.tags").text().trim().replace(/\n\s+/g, ","); - novel.Status = $( - "div.fiction-info > div.portlet > .col-md-8 > .margin-bottom-10 > span" - ) - .first() - .next() - .text() - .trim(); + novel.novelCover = novelCover; + + novel.summary = $("div.description").text().trim(); + + novel.author = $("h1").next().text().replace("by ", "").trim(); + novel.genre = $("span.tags").text().trim().replace(/\n\s+/g, ","); + novel.status = + $( + "div.fiction-info > div.portlet > .col-md-8 > .margin-bottom-10 > span" + ) + .first() + .next() + .text() + .trim() === "ONGOING" + ? Status.ONGOING + : Status.COMPLETED; let novelChapters = []; $("table#chapters > tbody") .find("tr") - .each(function (result) { + .each(function () { const chapterName = $(this).find("td").first().text().trim(); - const releaseDate = $(this).find("td").first().next().text().trim(); + let releaseDate = $(this).find("td").first().next().text().trim(); + releaseDate = releaseDate && parseMadaraDate(releaseDate); const chapterUrl = $(this) .find("td") .first() .find("a") .attr("href") - .replace( - "/fiction/" + +novelId + "/" + novelUrl + "/chapter/", - "" - ); + .replace("/fiction/" + novelUrl + "/chapter/", ""); novelChapters.push({ chapterName, @@ -99,18 +110,13 @@ const novelScraper = async (req, res) => { }); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}fiction/${req.params.novelId}/${novelUrl}/chapter/${req.params.chapterId}/${req.params.chapterUrl}`; - - console.log(url); +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}fiction/${novelUrl}/chapter/${chapterUrl}`; const result = await fetch(url); const body = await result.text(); @@ -120,31 +126,21 @@ const chapterScraper = async (req, res) => { let chapterName = $("div.chapter-content").find("strong").text(); let chapterText = $("div.chapter-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; const chapter = { - extensionId: 34, + sourceId: 34, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = baseUrl + "fictions/search?title=" + searchTerm; - console.log(url); - const result = await fetch(url); const body = await result.text(); @@ -152,7 +148,7 @@ const searchScraper = async (req, res) => { let novels = []; - $("div.fiction-list-item.row").each(function (result) { + $("div.fiction-list-item.row").each(function () { const novelName = $(this).find("h2.fiction-title").text().trim(); let novelCover = $(this).find("img").attr("src"); @@ -166,7 +162,7 @@ const searchScraper = async (req, res) => { .replace("/fiction/", ""); const novel = { - extensionId: 34, + sourceId: 34, novelName, novelCover, novelUrl, @@ -175,12 +171,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = RoyalRoadScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const RoyalRoadScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default RoyalRoadScraper; diff --git a/src/en/royalroad/RoyalRoad.js b/src/en/royalroad/RoyalRoad.js deleted file mode 100644 index f359466..0000000 --- a/src/en/royalroad/RoyalRoad.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const RoyalRoadScraper = require("./RoyalRoadScraper"); - -const router = express.Router(); - -router.get("/novels/", RoyalRoadScraper.novelsScraper); - -router.get("/novel/:novelId/:novelUrl", RoyalRoadScraper.novelScraper); - -router.get( - "/novel/:novelId/:novelUrl/:chapterId/:chapterUrl", - RoyalRoadScraper.chapterScraper -); - -router.get("/search/", RoyalRoadScraper.searchScraper); - -module.exports = router; diff --git a/src/en/royalroad/icon.png b/src/en/royalroad/icon.png deleted file mode 100644 index f7c5467..0000000 Binary files a/src/en/royalroad/icon.png and /dev/null differ diff --git a/src/en/scribblehub/ScribbleHubScraper.js b/src/en/scribblehub.js similarity index 69% rename from src/en/scribblehub/ScribbleHubScraper.js rename to src/en/scribblehub.js index 65e8cb9..ae7b0f8 100644 --- a/src/en/scribblehub/ScribbleHubScraper.js +++ b/src/en/scribblehub.js @@ -1,13 +1,10 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); -const FormData = require("form-data"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://www.scribblehub.com/"; -const novelsScraper = async (req, res) => { - let url = baseUrl + "series-ranking/?sort=1&order=4&pg=1"; +const popularNovels = async (page) => { + const totalPages = 326; + let url = baseUrl + "series-ranking/?sort=1&order=4&pg=" + page; const result = await fetch(url); const body = await result.text(); @@ -25,7 +22,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl[4] + "-" + novelUrl[5]; const novel = { - extensionId: 35, + sourceId: 35, novelName, novelCover, novelUrl, @@ -34,11 +31,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = baseUrl + "read/" + novelUrl; const result = await fetch(url); @@ -48,11 +44,11 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 35; + novel.sourceId = 35; novel.sourceName = "Scribble Hub"; - novel.sourceUrl = url; + novel.url = url; novel.novelUrl = novelUrl; @@ -60,19 +56,23 @@ const novelScraper = async (req, res) => { novel.novelCover = $("div.fic_image > img").attr("src"); - novel.novelSummary = $("div.wi_fic_desc").text(); + novel.summary = $("div.wi_fic_desc").text(); - novel["Genre(s)"] = ""; + novel.genre = ""; $("span.wi_fic_genre") .find("span") .each(function (res) { - novel["Genre(s)"] += $(this).text() + ","; + novel.genre += $(this).text() + ","; }); - if (novel["Genre(s)"]) { - novel["Genre(s)"] = novel["Genre(s)"].slice(0, -1); + if (novel.genre) { + novel.genre = novel.genre.slice(0, -1); } - novel["Author(s)"] = $("span.auth_name_fic").text(); + novel.status = $("span.rnd_stats").next().text().includes("Ongoing") + ? "Ongoing" + : "Completed"; + + novel.author = $("span.auth_name_fic").text(); let formData = new FormData(); formData.append("action", "wi_getreleases_pagination"); @@ -106,15 +106,12 @@ const novelScraper = async (req, res) => { }); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}read/${novelUrl}/chapter/${chapterUrl}`; const result = await fetch(url); @@ -125,27 +122,18 @@ const chapterScraper = async (req, res) => { let chapterName = $("div.chapter-title").text(); let chapterText = $("div.chp_raw").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - const chapter = { - extensionId: 35, + sourceId: 35, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = "https://www.scribblehub.com/?s=" + searchTerm + @@ -167,7 +155,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl[4] + "-" + novelUrl[5]; const novel = { - extensionId: 35, + sourceId: 35, novelName, novelCover, novelUrl, @@ -175,12 +163,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const ScribbleHubScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default ScribbleHubScraper; diff --git a/src/en/scribblehub/ScribbleHub.js b/src/en/scribblehub/ScribbleHub.js deleted file mode 100644 index c5023d6..0000000 --- a/src/en/scribblehub/ScribbleHub.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const ScribbleHubScraper = require("./ScribbleHubScraper"); - -const router = express.Router(); - -router.get("/novels/", ScribbleHubScraper.novelsScraper); - -router.get("/novel/:novelUrl", ScribbleHubScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", ScribbleHubScraper.chapterScraper); - -router.get("/search/", ScribbleHubScraper.searchScraper); - -module.exports = router; diff --git a/src/en/scribblehub/icon.png b/src/en/scribblehub/icon.png deleted file mode 100644 index c3a558b..0000000 Binary files a/src/en/scribblehub/icon.png and /dev/null differ diff --git a/src/en/tapread/TapReadScraper.js b/src/en/tapread.js similarity index 62% rename from src/en/tapread/TapReadScraper.js rename to src/en/tapread.js index 42b53db..1426b09 100644 --- a/src/en/tapread/TapReadScraper.js +++ b/src/en/tapread.js @@ -1,11 +1,15 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -var FormData = require("form-data"); -const { htmlToText } = require("html-to-text"); +import cheerio from "react-native-cheerio"; const baseUrl = "http://www.tapread.com"; -const novelsScraper = async (req, res) => { +let headers = new Headers({ + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", +}); + +const popularNovels = async (page) => { + let totalPages = 0; + const result = await fetch(baseUrl); const body = await result.text(); @@ -17,10 +21,9 @@ const novelsScraper = async (req, res) => { const novelName = $(this).find(".book-name").text(); const novelCover = "http:" + $(this).find("img").attr("src"); let novelUrl = $(this).attr("data-id"); - novelUrl += "/"; const novel = { - extensionId: 17, + sourceId: 17, novelName, novelCover, novelUrl, @@ -29,13 +32,14 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}/book/detail/${novelUrl}`; + console.log(url); + const result = await fetch(url); const body = await result.text(); @@ -43,26 +47,25 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 17; + novel.sourceId = 17; novel.sourceName = "TapRead"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".book-name").text(); novel.novelCover = "http:" + $("div.book-img > img").attr("src"); - novel["Genre(s)"] = $("div.book-catalog > span.txt").text(); + novel.genre = $("div.book-catalog > span.txt").text(); - novel.Status = $("div.book-state > span.txt").text(); + novel.status = $("div.book-state > span.txt").text(); - novel["Author(s)"] = $("div.author > span.name").text(); + novel.author = $("div.author > span.name").text(); - novelSummary = $("div.content > p.desc").html(); - novel.novelSummary = htmlToText(novelSummary); + novel.summary = $("div.content > p.desc").text(); const getChapters = async (novelId) => { const chapterListUrl = "http://www.tapread.com/ajax/book/contents"; @@ -98,15 +101,12 @@ const novelScraper = async (req, res) => { return novelChapters; }; - novel.novelChapters = await getChapters(novelUrl); + novel.chapters = await getChapters(novelUrl); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { let formData = new FormData(); formData.append("bookId", novelUrl); formData.append("chapterId", chapterUrl); @@ -120,30 +120,20 @@ const chapterScraper = async (req, res) => { const body = await result.json(); const chapterName = body.result.chapterName; - const chapterText = htmlToText(body.result.content); - const nextChapter = - body.result.nextChapterId === 0 - ? null - : body.result.nextChapterId.toString(); - const prevChapter = body.result.preChapterId.toString(); - novelUrl += "/"; + const chapterText = body.result.content; const chapter = { - extensionId: 17, + sourceId: 17, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { let formData = new FormData(); formData.append("storyType", 1); formData.append("pageNo", 1); @@ -162,21 +152,23 @@ const searchScraper = async (req, res) => { body.result.storyList.map((novel) => { const novelName = novel.storyName.replace( - 'Demon', + /|<\/font>/g, "" ); - const novelUrl = novel.storyId + "/"; + const novelUrl = novel.storyId; const novelCover = "http://static.tapread.com" + novel.coverUrl; - novels.push({ extensionId: 17, novelName, novelCover, novelUrl }); + novels.push({ sourceId: 17, novelName, novelCover, novelUrl }); }); - res.json(novels); + return novels; }; -module.exports = tapReadScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const tapReadScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default tapReadScraper; diff --git a/src/en/tapread/TapRead.js b/src/en/tapread/TapRead.js deleted file mode 100644 index c341292..0000000 --- a/src/en/tapread/TapRead.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const tapReadScraper = require("./TapReadScraper"); - -const router = express.Router(); - -router.get("/novels/", tapReadScraper.novelsScraper); - -router.get("/novel/:novelUrl", tapReadScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", tapReadScraper.chapterScraper); - -router.get("/search/", tapReadScraper.searchScraper); - -module.exports = router; diff --git a/src/en/tapread/icon.png b/src/en/tapread/icon.png deleted file mode 100644 index 1eb534e..0000000 Binary files a/src/en/tapread/icon.png and /dev/null differ diff --git a/src/en/travistranslations.js b/src/en/travistranslations.js new file mode 100644 index 0000000..4a9c2f6 --- /dev/null +++ b/src/en/travistranslations.js @@ -0,0 +1,156 @@ +import cheerio from "react-native-cheerio"; + +const sourceId = 76; + +const sourceName = "TravisTranslations"; + +const baseUrl = "https://travistranslations.com/"; + +const popularNovels = async (page) => { + let url = baseUrl + "all-series/"; + + if (page > 1) { + url += `page/${page}/`; + } + + const totalPages = 19; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".group").each(function () { + const novelName = $(this).find("h3").text(); + const novelCover = "https://" + $(this).find("img").attr("data-src"); + const novelUrl = $(this).find(" a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { novels, totalPages }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = novelUrl; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url, novelUrl }; + + novel.novelName = $("h1").text().trim(); + + novel.novelCover = "https://" + $("img.object-cover").attr("data-src"); + + novel.summary = $("div[property=description]").text().trim(); + + novel.genre = ""; + + $("div.flex.flex-col.mt-3.text-center > ul > li").each(function () { + novel.genre += + $(this) + .text() + .replace(/[\t\n]/g, "") + ","; + }); + $("div.header-stats > span").each(function () { + if ($(this).find("small").text() === "Status") { + novel.status = $(this).find("strong").text(); + } + }); + + novel.genre = novel.genre.slice(0, -1); + + novel.author = $(".author > a > span").text(); + let novelChapters = []; + + $(".tab_content > ul.grid") + .find("li") + .each(function () { + const chapterName = $(this).find("span").first().text().trim(); + const releaseDate = null; + const chapterUrl = $(this).find("a").attr("href"); + + novelChapters.push({ chapterName, releaseDate, chapterUrl }); + }); + + novel.chapters = novelChapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const result = await fetch(chapterUrl); + let body = await result.text(); + + $ = cheerio.load(body); + + const chapterId = $("input[name=id]").attr("value"); + + const url = + "https://travistranslations.com/wp-json/wp/v2/posts/" + chapterId; + + const data = await fetch(url); + body = await data.json(); + + let chapterName = body.title.rendered; + let chapterText = body.content.rendered; + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + let url = baseUrl + "all-series/?search=" + searchTerm; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".group").each(function () { + const novelName = $(this).find("h3").text(); + const novelCover = "https://" + $(this).find("img").attr("data-src"); + const novelUrl = $(this).find(" a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const TravisTranslationsScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default TravisTranslationsScraper; diff --git a/src/en/travistranslations/icon.png b/src/en/travistranslations/icon.png deleted file mode 100644 index a09d69e..0000000 Binary files a/src/en/travistranslations/icon.png and /dev/null differ diff --git a/src/en/vipnovel/VipNovelScraper.js b/src/en/vipnovel.js similarity index 62% rename from src/en/vipnovel/VipNovelScraper.js rename to src/en/vipnovel.js index 853a52b..a856dfe 100644 --- a/src/en/vipnovel/VipNovelScraper.js +++ b/src/en/vipnovel.js @@ -1,12 +1,10 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://vipnovel.com/"; -const novelsScraper = async (req, res) => { - let url = baseUrl; +const popularNovels = async (page) => { + let totalPages = 89; + let url = `${baseUrl}vipnovel/page/${page}/?m_orderby=rating`; const result = await fetch(url); const body = await result.text(); @@ -23,7 +21,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}vipnovel/`, ""); const novel = { - extensionId: 10, + sourceId: 10, novelName, novelCover, novelUrl, @@ -32,12 +30,13 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}vipnovel/${novelUrl}/`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}vipnovel/${novelUrl}`; + + console.log(url); const result = await fetch(url); const body = await result.text(); @@ -46,13 +45,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 10; + novel.sourceId = 10; novel.sourceName = "VipNovel"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".post-title > h3") .text() @@ -60,7 +59,6 @@ const novelScraper = async (req, res) => { .trim(); novel.novelCover = $(".summary_image > a > img").attr("src"); - $(".post-content_item").each(function (result) { detailName = $(this) .find(".summary-heading > h5") @@ -73,12 +71,26 @@ const novelScraper = async (req, res) => { .replace(/[\t\n]/g, "") .trim(); - novel[detailName] = detail; + switch (detailName) { + case "Genre(s)": + novel.genre = detail.trim().replace(/[\t\n]/g, ","); + break; + case "Author(s)": + novel.author = detail.trim(); + break; + case "Artist(s)": + novel.status = detail.trim(); + break; + } }); - let novelSummary = $(".description-summary > div.summary__content").html(); + $(".summary__content > p").first().remove(); - novel.novelSummary = htmlToText(novelSummary); + let novelSummary = $(".description-summary > div.summary__content") + .text() + .replace("Synopsis", ""); + + novel.summary = novelSummary.replace(/[\t\n]/g, ""); let novelChapters = []; @@ -99,19 +111,20 @@ const novelScraper = async (req, res) => { const chapter = { chapterName, releaseDate, chapterUrl }; + console.log(chapterUrl); + novelChapters.push(chapter); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = baseUrl + "vipnovel/" + novelUrl + chapterUrl; - const url = `${baseUrl}vipnovel/${novelUrl}/${chapterUrl}`; + console.log(url); const result = await fetch(url); const body = await result.text(); @@ -125,45 +138,18 @@ const chapterScraper = async (req, res) => { } let chapterText = $(".text-left").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($(".nav-next").length) { - nextChapter = $(".nav-next") - .find("a") - .attr("href") - .replace(baseUrl + "vipnovel/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($(".nav-previous").length) { - prevChapter = $(".nav-previous") - .find("a") - .attr("href") - .replace(baseUrl + "vipnovel/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - const chapter = { - extensionId: 10, + sourceId: 10, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga`; const result = await fetch(url); @@ -181,7 +167,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}vipnovel/`, ""); const novel = { - extensionId: 10, + sourceId: 10, novelName, novelCover, novelUrl, @@ -190,12 +176,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const vipNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default vipNovelScraper; diff --git a/src/en/vipnovel/VipNovel.js b/src/en/vipnovel/VipNovel.js deleted file mode 100644 index 85a27bb..0000000 --- a/src/en/vipnovel/VipNovel.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const vipNovelScraper = require("./VipNovelScraper"); - -const router = express.Router(); - -router.get("/novels/", vipNovelScraper.novelsScraper); - -router.get("/novel/:novelUrl", vipNovelScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", vipNovelScraper.chapterScraper); - -router.get("/search/", vipNovelScraper.searchScraper); - -module.exports = router; diff --git a/src/en/vipnovel/icon.png b/src/en/vipnovel/icon.png deleted file mode 100644 index 8b4629a..0000000 Binary files a/src/en/vipnovel/icon.png and /dev/null differ diff --git a/src/en/wlnupdates.js b/src/en/wlnupdates.js new file mode 100644 index 0000000..4b37e45 --- /dev/null +++ b/src/en/wlnupdates.js @@ -0,0 +1,177 @@ +import cheerio from "react-native-cheerio"; +import { Status } from "../helpers/constants"; +import NovelUpdatesScraper from "./novelupdates"; + +const sourceId = 62; + +const sourceName = "WLNUpdates"; + +const baseUrl = "https://www.wlnupdates.com/"; + +let headers = new Headers({ + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", +}); + +const popularNovels = async (page) => { + const totalPages = 100; + let url = `${baseUrl}highest-rated/` + page; + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); + + const $ = cheerio.load(body); + + const novels = []; + + $("table").find("tr").first().remove(); + + $("tr").each(function (res) { + const novelCover = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + + const novelName = $(this).find("td > a").text(); + const novelUrl = + "https://www.wlnupdates.com" + $(this).find("td > a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = novelUrl; + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { + sourceId, + sourceName, + url, + novelUrl, + }; + + novel.novelName = $("h2").text(); + + novel.novelCover = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + + novel.author = $(".multiitem#author") + .text() + .trim() + .replace(/[\t\n]/g, ""); + + novel.genre = $(".multiitem#genre") + .text() + .trim() + .replace(/[\t\n]/g, ""); + + novel.status = Status.UNKNOWN; + + let summary = $("#description").text().trim(); + + novel.summary = summary; + + let novelChapters = []; + + $("#release-entry").each(function () { + let chapterName; + + if ($(this).find("td.numeric").length > 1) { + chapterName = + "Volume " + + $(this).find(" td:nth-child(3)").text().trim() + + " Chapter " + + $(this).find(" td:nth-child(4)").text().trim(); + } else if ($(this).find("td.numeric").length > 0) { + chapterName = "Chapter " + $(this).find("td.numeric").text().trim(); + } else { + chapterName = $(this).find("td.postfix").text().trim(); + } + + const releaseDate = $(this).find(".release-entry-cell").text().trim(); + + const chapterUrl = $(this).find("td:nth-child(1) > a").attr("href"); + + novelChapters.push({ chapterName, releaseDate, chapterUrl }); + }); + + novel.chapters = novelChapters.reverse(); + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const chapterName = ""; + const chapterText = ( + await NovelUpdatesScraper.parseChapter(novelUrl, chapterUrl) + ).chapterText; + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = baseUrl + "search?title=" + searchTerm; + + const res = await fetch(url, { + method: "GET", + headers: headers, + }); + const body = await res.text(); + + $ = cheerio.load(body); + + let novels = []; + + $("tr").each(function (res) { + const novelCover = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + const novelName = $(this).find("td > a").text(); + const novelUrl = + "https://www.wlnupdates.com" + $(this).find("td > a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + return novels; +}; + +const WLNUpdatesScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default WLNUpdatesScraper; diff --git a/src/en/wlnupdates/icon.png b/src/en/wlnupdates/icon.png deleted file mode 100644 index c7c5456..0000000 Binary files a/src/en/wlnupdates/icon.png and /dev/null differ diff --git a/src/en/wnmtl/WNMTL.js b/src/en/wnmtl/WNMTL.js deleted file mode 100644 index f2764c3..0000000 --- a/src/en/wnmtl/WNMTL.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const WNMTLScraper = require("./WNMTLScraper"); - -const router = express.Router(); - -router.get("/novels/", WNMTLScraper.novelsScraper); - -router.get("/novel/:novelUrl", WNMTLScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", WNMTLScraper.chapterScraper); - -router.get("/search/", WNMTLScraper.searchScraper); - -module.exports = router; diff --git a/src/en/wnmtl/WNMTLScraper.js b/src/en/wnmtl/WNMTLScraper.js deleted file mode 100644 index 741c875..0000000 --- a/src/en/wnmtl/WNMTLScraper.js +++ /dev/null @@ -1,201 +0,0 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); - -const baseUrl = "https://www.wnmtl.org/"; - -const sourceName = "WNMTL"; -const sourceId = 77; - -const novelsScraper = async (req, res) => { - let url = baseUrl + "explore"; - - console.log(url); - - const result = await fetch( - "https://www.wnmtl.org/book/2308-warlock-apprentice" - ); - const body = await result.text(); - - // $ = cheerio.load(body); - - // let novels = []; - - // $(".card").each(function () { - // const novelName = $(this).find(".book-name").text(); - // const novelCover = $(this).find("img").attr("src"); - // const novelUrl = $(this).find("a").attr("href"); - - // const novel = { - // sourceId, - // novelName, - // novelCover, - // novelUrl, - // }; - - // novels.push(novel); - // }); - - res.json({ novels: body }); -}; - -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = novelUrl; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novel = { sourceId, sourceName, url, novelUrl }; - - novel.novelName = $("#thumbnail > img").text(); - - novel.novelCover = $("#thumbnail > img").attr("src"); - - let novelSummary = $("div#Description").html(); - novel.novelSummary = htmlToText(novelSummary); - - novel["Genre(s)"] = $("div#Genre") - .text() - .replace(/Genre:|\s/g, ""); - - novel.Status = $("div#Status") - .text() - .replace(/Status:|\s/g, ""); - - novel["Author(s)"] = $("div#Publisher") - .text() - .replace(/Publisher:|\s/g, ""); - - // novel['Genre(s)'] = $("div#Genre"). - - let novelChapters = []; - - let chapterUrlPrefix = $("tbody > tr") - .first() - .next() - .find("td > a") - .text() - .split(" "); - - chapterUrlPrefix.pop(); - - chapterUrlPrefix = chapterUrlPrefix.join("-").toLowerCase(); - - const latestChapter = $("tbody > tr") - .first() - .next() - .find("td > a") - .text() - .replace(/^\D+/g, ""); - - for (let i = 1; i <= latestChapter; i++) { - const chapterName = "Chapter " + i; - const releaseDate = null; - const chapterUrl = chapterUrlPrefix + "-" + i + "/"; - - const chapter = { - chapterName, - releaseDate, - chapterUrl, - }; - - novelChapters.push(chapter); - } - - novel.novelChapters = novelChapters; - - res.json(novel); -}; - -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}mtl/${novelUrl}/${chapterUrl}`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let chapterName = $(".entry-title").text(); - - let chapterText = $(".entry-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($(".nav-next").find("a").length) { - nextChapter = $(".nav-next") - .find("a") - .attr("href") - .replace(baseUrl + "mtl/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($(".nav-previous").find("a").length) { - prevChapter = $(".nav-previous") - .find("a") - .attr("href") - .replace(baseUrl + "mtl/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - - const chapter = { - sourceId, - novelUrl, - chapterUrl, - chapterName, - chapterText, - nextChapter, - prevChapter, - }; - - res.json(chapter); -}; - -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - - const url = baseUrl + "?s=" + searchTerm + "&post_type=novel"; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $("article.blog-article").each(function (result) { - const novelName = $(this).find("h5 > a").text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this).find("h5 > a").attr("href"); - novelUrl = novelUrl.replace(baseUrl + "novel/", ""); - - const novel = { - sourceId, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -module.exports = WNMTLScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, -}; diff --git a/src/en/woopread/WoopReadScraper.js b/src/en/woopread.js similarity index 59% rename from src/en/woopread/WoopReadScraper.js rename to src/en/woopread.js index f9c5cc7..4dfb0bd 100644 --- a/src/en/woopread/WoopReadScraper.js +++ b/src/en/woopread.js @@ -1,12 +1,10 @@ -const cheerio = require("cheerio"); -const FormData = require("form-data"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://woopread.com/"; -const novelsScraper = async (req, res) => { - let url = baseUrl + "novellist/?m_orderby=rating"; +const popularNovels = async (page) => { + let totalPages = 6; + let url = baseUrl + "novellist/page/" + page + "/?m_orderby=rating"; const result = await fetch(url); const body = await result.text(); @@ -17,13 +15,13 @@ const novelsScraper = async (req, res) => { $(".page-item-detail").each(function (result) { const novelName = $(this).find(".h5 > a").text(); - const novelCover = $(this).find("img").attr("src"); + const novelCover = $(this).find("img").attr("data-src"); let novelUrl = $(this).find(".h5 > a").attr("href"); novelUrl = novelUrl.replace(baseUrl + "series/", ""); const novel = { - extensionId: 21, + sourceId: 21, novelName, novelCover, novelUrl, @@ -32,12 +30,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}series/${novelUrl}/`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}series/${novelUrl}`; const result = await fetch(url); const body = await result.text(); @@ -46,13 +43,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 21; + novel.sourceId = 21; novel.sourceName = "WoopRead"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".post-title > h1") .text() @@ -73,14 +70,23 @@ const novelScraper = async (req, res) => { .replace(/[\t\n]/g, "") .trim(); - novel[detailName] = detail; + switch (detailName) { + case "Genre(s)": + novel.genre = detail.trim().replace(/[\t\n]/g, ","); + break; + case "Author(s)": + novel.author = detail.trim(); + break; + case "Artist(s)": + novel.status = detail.trim(); + break; + } }); - $(".description-summary > div.summary__content").find("em").remove(); - novel.novelSummary = $(".description-summary > div.summary__content") + novel.summary = $(".description-summary > div.summary__content") .text() - .replace(/[\t\n]/g, ""); + .trim(); let novelChapters = []; @@ -112,25 +118,13 @@ const novelScraper = async (req, res) => { novelChapters.push({ chapterName, releaseDate, chapterUrl }); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - let volumeUrl = req.params.volumeUrl; - - let optionalUrl; - - if (volumeUrl) { - optionalUrl = volumeUrl + "/"; - } else { - optionalUrl = ""; - } - - const url = `${baseUrl}series/${novelUrl}/${optionalUrl}${chapterUrl}/`; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}series/${novelUrl}/${chapterUrl}/`; const result = await fetch(url); const body = await result.text(); @@ -138,49 +132,22 @@ const chapterScraper = async (req, res) => { $ = cheerio.load(body); const chapterName = $("h1#chapter-heading").text(); + let chapterText = $(".reading-content").html(); - chapterText = parseHtml(chapterText).replace( - /\[(https:\/\/docs.google.com\/)(.*?)\]/g, - "" - ); - - let nextChapter = null; - - if ($(".nav-next").length) { - nextChapter = $(".nav-next") - .find("a") - .attr("href") - .replace(baseUrl + "series/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($(".nav-previous").length) { - prevChapter = $(".nav-previous") - .find("a") - .attr("href") - .replace(baseUrl + "series/" + novelUrl + "/", ""); - } - - novelUrl += "/"; - chapterUrl = optionalUrl + chapterUrl + "/"; + chapterText = chapterText.replace(/(?<=[[])[\n](?=[h])/g, ""); const chapter = { - extensionId: 21, + sourceId: 21, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga&op=&author=&artist=&release=&adult=`; const result = await fetch(url); @@ -198,7 +165,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}series/`, ""); const novel = { - extensionId: 21, + sourceId: 21, novelName, novelCover, novelUrl, @@ -207,12 +174,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = novelTrenchScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const WoopReadScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default WoopReadScraper; diff --git a/src/en/woopread/WoopRead.js b/src/en/woopread/WoopRead.js deleted file mode 100644 index 155f78e..0000000 --- a/src/en/woopread/WoopRead.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const WoopReadScraper = require("./WoopReadScraper"); - -const router = express.Router(); - -router.get("/novels/", WoopReadScraper.novelsScraper); - -router.get("/novel/:novelUrl", WoopReadScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:volumeUrl?/:chapterUrl", - WoopReadScraper.chapterScraper -); - -router.get("/search/", WoopReadScraper.searchScraper); - -module.exports = router; diff --git a/src/en/woopread/icon.png b/src/en/woopread/icon.png deleted file mode 100644 index 1171d22..0000000 Binary files a/src/en/woopread/icon.png and /dev/null differ diff --git a/src/en/wuxiablog/WuxiaBlogScraper.js b/src/en/wuxiablog.js similarity index 73% rename from src/en/wuxiablog/WuxiaBlogScraper.js rename to src/en/wuxiablog.js index 5e8567c..7ce5b0b 100644 --- a/src/en/wuxiablog/WuxiaBlogScraper.js +++ b/src/en/wuxiablog.js @@ -1,15 +1,14 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; -const extensionId = 48; +const sourceId = 48; const sourceName = "Wuxia.Blog"; const baseUrl = "https://www.wuxia.blog/"; -const novelsScraper = async (req, res) => { - let url = `${baseUrl}/listNovels`; +const popularNovels = async (page) => { + const totalPages = 0; + let url = `${baseUrl}listNovels`; const result = await fetch(url); const body = await result.text(); @@ -34,7 +33,7 @@ const novelsScraper = async (req, res) => { .replace("/novel/", ""); const novel = { - extensionId, + sourceId, novelName, novelCover, novelUrl, @@ -44,12 +43,10 @@ const novelsScraper = async (req, res) => { } }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}novel/${novelUrl}/`; const result = await fetch(url); @@ -58,9 +55,9 @@ const novelScraper = async (req, res) => { $ = cheerio.load(body); let novel = { - extensionId, + sourceId, sourceName, - sourceUrl: url, + url, novelUrl, }; @@ -70,18 +67,13 @@ const novelScraper = async (req, res) => { novel.novelCover = $('img[itemprop="image"]').attr("src"); - novel["Author(s)"] = $( + novel.author = $( "div.col-md-6 > div > div:nth-child(2) > div > div:nth-child(2) > a" ).text(); - novel["Genre(s)"] = $( - "div.row > div.col-md-6 > div > a:nth-child(4)" - ).text(); + novel.genre = $("div.row > div.col-md-6 > div > a:nth-child(4)").text(); - novel.novelSummary = $('div[itemprop="description"]') - .find("p") - .text() - .trim(); + novel.summary = $('div[itemprop="description"]').find("p").text().trim(); let novelChapters = []; @@ -133,17 +125,13 @@ const novelScraper = async (req, res) => { novelChapters.push(chapter); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterId = req.params.chapterId; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}novel/${novelUrl}/${chapterId}/${chapterUrl}`; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}novel/${novelUrl}/${chapterUrl}`; const result = await fetch(url); const body = await result.text(); @@ -158,27 +146,18 @@ const chapterScraper = async (req, res) => { .html() .replace(/(.*?)<\/span>/g, ""); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - const chapter = { - extensionId, + sourceId, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}?search=${searchTerm}`; const result = await fetch(url); @@ -202,7 +181,7 @@ const searchScraper = async (req, res) => { .replace(baseUrl + "novel/", ""); const novel = { - extensionId, + sourceId, novelName, novelCover, novelUrl, @@ -212,12 +191,14 @@ const searchScraper = async (req, res) => { } }); - res.json(novels); + return novels; }; -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const WuxiaBlogScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default WuxiaBlogScraper; diff --git a/src/en/wuxiablog/WuxiaBlog.js b/src/en/wuxiablog/WuxiaBlog.js deleted file mode 100644 index b1fd895..0000000 --- a/src/en/wuxiablog/WuxiaBlog.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const wuxiaBlogScraper = require("./WuxiaBlogScraper"); - -const router = express.Router(); - -router.get("/novels/", wuxiaBlogScraper.novelsScraper); - -router.get("/novel/:novelUrl", wuxiaBlogScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:chapterId/:chapterUrl", - wuxiaBlogScraper.chapterScraper -); - -router.get("/search/", wuxiaBlogScraper.searchScraper); - -module.exports = router; diff --git a/src/en/wuxiablog/icon.png b/src/en/wuxiablog/icon.png deleted file mode 100644 index 150cc43..0000000 Binary files a/src/en/wuxiablog/icon.png and /dev/null differ diff --git a/src/en/wuxiacity/WuxiaCityScraper.js b/src/en/wuxiacity.js similarity index 67% rename from src/en/wuxiacity/WuxiaCityScraper.js rename to src/en/wuxiacity.js index 0d7239f..a109d07 100644 --- a/src/en/wuxiacity/WuxiaCityScraper.js +++ b/src/en/wuxiacity.js @@ -1,15 +1,16 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; -const extensionId = 49; +import moment from "moment"; + +const sourceId = 49; const sourceName = "wuxia.city"; const baseUrl = "https://wuxia.city/"; -const novelsScraper = async (req, res) => { - let url = `${baseUrl}genre/all`; +const popularNovels = async (page) => { + const totalPages = 133; + let url = `${baseUrl}genre/all?page=` + page; const result = await fetch(url); const body = await result.text(); @@ -24,7 +25,7 @@ const novelsScraper = async (req, res) => { const novelUrl = $(this).find("a").attr("href").replace("/book/", ""); const novel = { - extensionId, + sourceId, novelName, novelCover, novelUrl, @@ -33,12 +34,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}book/${novelUrl}`; const result = await fetch(url); @@ -47,9 +46,9 @@ const novelScraper = async (req, res) => { let $ = cheerio.load(body); let novel = { - extensionId, + sourceId, sourceName, - sourceUrl: url, + url, novelUrl, }; @@ -57,14 +56,11 @@ const novelScraper = async (req, res) => { novel.novelCover = $("div.book-img > img").attr("src"); - novel["Author(s)"] = $("dl.author > dd").text(); + novel.author = $("dl.author > dd").text(); - novel["Genre(s)"] = $(".book-generes").text().trim(); + novel.genre = $(".book-generes").text().trim(); - novel.novelSummary = $(".book-synopsis") - .text() - .replace("Synopsis", "") - .trim(); + novel.summary = $(".book-synopsis").text().replace("Synopsis", "").trim(); const chapterListUrl = `${baseUrl}book/${novelUrl}/chapter-list`; @@ -84,9 +80,13 @@ const novelScraper = async (req, res) => { let releaseDate = new Date(); - let timeAgo = $(this).find(".chapter-time").text().match(/\d+/)[0]; + let timeAgo = $(this).find(".chapter-time").text(); - if (timeAgo.includes("months ago")) { + timeAgo = timeAgo.replace("one", 1); + + timeAgo = timeAgo.match(/\d+/)[0]; + + if (timeAgo.includes("months ago") || timeAgo.includes("month ago")) { releaseDate.setMonth(releaseDate.getMonth() - timeAgo); } @@ -94,6 +94,8 @@ const novelScraper = async (req, res) => { releaseDate.setDate(releaseDate.getDate() - timeAgo); } + releaseDate = moment(releaseDate).format("MM/DD/YY"); + const chapterUrl = $(this).find("a").attr("href").split("/").pop(); const chapter = { @@ -105,15 +107,12 @@ const novelScraper = async (req, res) => { novelChapters.push(chapter); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}book/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -124,27 +123,18 @@ const chapterScraper = async (req, res) => { let chapterName = $(".chapter-name").text(); let chapterText = $(".chapter-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - const chapter = { - extensionId, + sourceId, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}search?q=${searchTerm}`; const result = await fetch(url); @@ -161,7 +151,7 @@ const searchScraper = async (req, res) => { let novelUrl = $(this).find("a").attr("href").replace("/book/", ""); const novel = { - extensionId, + sourceId, novelName, novelCover, novelUrl, @@ -170,12 +160,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = wuxiCityScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const WuxiaCityScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default WuxiaCityScraper; diff --git a/src/en/wuxiacity/WuxiaCity.js b/src/en/wuxiacity/WuxiaCity.js deleted file mode 100644 index e031a15..0000000 --- a/src/en/wuxiacity/WuxiaCity.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const wuxiaCityScraper = require("./WuxiaCityScraper"); - -const router = express.Router(); - -router.get("/novels/", wuxiaCityScraper.novelsScraper); - -router.get("/novel/:novelUrl", wuxiaCityScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", wuxiaCityScraper.chapterScraper); - -router.get("/search/", wuxiaCityScraper.searchScraper); - -module.exports = router; diff --git a/src/en/wuxiacity/icon.png b/src/en/wuxiacity/icon.png deleted file mode 100644 index b732fa7..0000000 Binary files a/src/en/wuxiacity/icon.png and /dev/null differ diff --git a/src/en/wuxiaworld/WuxiaWorldScraper.js b/src/en/wuxiaworld.js similarity index 61% rename from src/en/wuxiaworld/WuxiaWorldScraper.js rename to src/en/wuxiaworld.js index d1a5b7b..749268d 100644 --- a/src/en/wuxiaworld/WuxiaWorldScraper.js +++ b/src/en/wuxiaworld.js @@ -1,10 +1,9 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://www.wuxiaworld.com/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; const url = `${baseUrl}api/novels`; const result = await fetch(url); @@ -16,21 +15,19 @@ const novelsScraper = async (req, res) => { let novelName = novel.name; let novelCover = novel.coverUrl; let novelUrl = novel.slug; - novelUrl = novelUrl + "/"; novels.push({ - extensionId: 7, + sourceId: 7, novelUrl, novelName, novelCover, }); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}novel/${novelUrl}/`; const result = await fetch(url); @@ -40,21 +37,27 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 7; + novel.sourceId = 7; novel.sourceName = "WuxiaWorld"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $("h2").text(); novel.novelCover = $("img.img-thumbnail").attr("src"); - novel.novelSummary = $(".fr-view > p").text(); + novel.summary = $("h3") + .filter(function () { + return $(this).text().trim() === "Synopsis"; + }) + .next() + .text() + .trim(); - novel["Author(s)"] = $("div > dt") + novel.author = $("div > dt") .filter(function () { return $(this).text().trim() === "Author:"; }) @@ -69,11 +72,15 @@ const novelScraper = async (req, res) => { genres.push($(this).find("a").text()); }); - novel["Genre(s)"] = genres.join(","); + novel.genre = genres.join(","); - novel["Artist(s)"] = null; + novel.artist = null; - novel.Status = null; + novel.status = null; + + novel.status = $("div.fr-view.pt-10").text().includes("Complete") + ? "Completed" + : "Ongoing"; let novelChapters = []; @@ -95,15 +102,12 @@ const novelScraper = async (req, res) => { novelChapters.push(chapter); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { let url = `${baseUrl}novel/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -111,45 +115,27 @@ const chapterScraper = async (req, res) => { $ = cheerio.load(body); + $(".chapter-nav").remove(); + let chapterName = $("#sidebar-toggler-container").next().text(); chapterName = chapterName.replace(/[\t\n]/g, ""); $("#chapter-content > script").remove(); let chapterText = $("#chapter-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($("li.next > a").attr("href") !== "#") { - nextChapter = $("li.next > a") - .attr("href") - .replace(`/novel/${novelUrl}`, ""); - } - - let prevChapter = null; - - if ($("li.prev > a").attr("href") !== "#") { - prevChapter = $("li.prev > a") - .attr("href") - .replace(`/novel/${novelUrl}`, ""); - } const chapter = { - extensionId: 7, + sourceId: 7, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; +const searchNovels = async (searchTerm) => { const searchUrl = `https://www.wuxiaworld.com/api/novels/search?query=`; const url = `${searchUrl}${searchTerm}`; @@ -163,22 +149,23 @@ const searchScraper = async (req, res) => { let novelName = novel.name; let novelCover = novel.coverUrl; let novelUrl = novel.slug; - novelUrl = novelUrl + "/"; novels.push({ - extensionId: 7, + sourceId: 7, novelUrl, novelName, novelCover, }); }); - res.json(novels); + return novels; }; -module.exports = wuxiaWorldScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const WuxiaWorldScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default WuxiaWorldScraper; diff --git a/src/en/wuxiaworld/WuxiaWorld.js b/src/en/wuxiaworld/WuxiaWorld.js deleted file mode 100644 index 746264b..0000000 --- a/src/en/wuxiaworld/WuxiaWorld.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const wuxiaWorldScraper = require("./WuxiaWorldScraper"); - -const router = express.Router(); - -router.get("/novels/", wuxiaWorldScraper.novelsScraper); - -router.get("/novel/:novelUrl", wuxiaWorldScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", wuxiaWorldScraper.chapterScraper); - -router.get("/search/", wuxiaWorldScraper.searchScraper); - -module.exports = router; diff --git a/src/en/wuxiaworld/icon.png b/src/en/wuxiaworld/icon.png deleted file mode 100644 index a260565..0000000 Binary files a/src/en/wuxiaworld/icon.png and /dev/null differ diff --git a/src/en/wuxiaworldcloud/WuxiaWorldCloudScraper.js b/src/en/wuxiaworldcloud.js similarity index 67% rename from src/en/wuxiaworldcloud/WuxiaWorldCloudScraper.js rename to src/en/wuxiaworldcloud.js index dfdd57d..4deeeb5 100644 --- a/src/en/wuxiaworldcloud/WuxiaWorldCloudScraper.js +++ b/src/en/wuxiaworldcloud.js @@ -1,11 +1,10 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "http://wuxiaworld.cloud/"; -const novelsScraper = async (req, res) => { - const url = `${baseUrl}/popular-novel`; +const popularNovels = async (page) => { + let totalPages = 41; + const url = `${baseUrl}/popular-novel?page=${page}`; const result = await fetch(url); const body = await result.text(); @@ -25,7 +24,7 @@ const novelsScraper = async (req, res) => { const novelCover = $(this).find("img").attr("src"); const novel = { - extensionId: 20, + sourceId: 20, novelUrl, novelName, novelCover, @@ -34,11 +33,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}novel/${novelUrl}`; const result = await fetch(url); @@ -48,28 +46,28 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 20; + novel.sourceId = 20; novel.sourceName = "WuxiaWorld.cloud"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $("div.book > img").attr("alt"); novel.novelCover = $("div.book > img").attr("src"); - novel.novelSummary = $("div.desc-text").text(); + novel.summary = $("div.desc-text").text().trim(); - novel["Author(s)"] = $("div.info > div > h3") + novel.author = $("div.info > div > h3") .filter(function () { return $(this).text().trim() === "Author:"; }) .siblings() .text(); - novel["Genre(s)"] = $("div.info > div") + novel.genre = $("div.info > div") .filter(function () { return $(this).find("h3").text().trim() === "Genre:"; }) @@ -77,14 +75,15 @@ const novelScraper = async (req, res) => { .replace("Genre:", "") .replace(/\s/g, ""); - novel["Artist(s)"] = null; + novel.artist = null; - novel.Status = $("li > h3") + novel.status = $("div.info > div") .filter(function () { - return $(this).text().trim() === "Status:"; + return $(this).find("h3").text().trim() === "Status:"; }) - .siblings() - .text(); + .text() + .replace("Status:", "") + .replace(/\s/g, ""); const novelId = $("#rating").attr("data-novel-id"); @@ -114,15 +113,12 @@ const novelScraper = async (req, res) => { return novelChapters; }; - novel.novelChapters = await getChapters(novelId); + novel.chapters = await getChapters(novelId); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { chapterUrl = encodeURI(chapterUrl); const url = `${baseUrl}${chapterUrl}`; @@ -134,33 +130,18 @@ const chapterScraper = async (req, res) => { const chapterName = $(".chr-title").attr("title"); let chapterText = $("#chr-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - if ($("a#next_chap").attr("href")) { - nextChapter = $("a#next_chap").attr("href").replace(baseUrl, ""); - } - - let prevChapter = null; - if ($("a#prev_chap").attr("href")) { - prevChapter = $("a#prev_chap").attr("href").replace(baseUrl, ""); - } - const chapter = { - extensionId: 20, + sourceId: 20, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; +const searchNovels = async (searchTerm) => { const searchUrl = `http://wuxiaworld.cloud/search?keyword=`; const url = `${searchUrl}${searchTerm}`; @@ -183,7 +164,7 @@ const searchScraper = async (req, res) => { const novelCover = $(this).find("img").attr("src"); const novel = { - extensionId: 20, + sourceId: 20, novelUrl, novelName, novelCover, @@ -192,12 +173,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = readNovelFullScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const WuxiaWorldCloudScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default WuxiaWorldCloudScraper; diff --git a/src/en/wuxiaworldcloud/WuxiaWorldCloud.js b/src/en/wuxiaworldcloud/WuxiaWorldCloud.js deleted file mode 100644 index 81da6b2..0000000 --- a/src/en/wuxiaworldcloud/WuxiaWorldCloud.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const WuxiaWorldCloudScraper = require("./WuxiaWorldCloudScraper"); - -const router = express.Router(); - -router.get("/novels/", WuxiaWorldCloudScraper.novelsScraper); - -router.get("/novel/:novelUrl", WuxiaWorldCloudScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:chapterUrl", - WuxiaWorldCloudScraper.chapterScraper -); - -router.get("/search/", WuxiaWorldCloudScraper.searchScraper); - -module.exports = router; diff --git a/src/en/wuxiaworldcloud/icon.png b/src/en/wuxiaworldcloud/icon.png deleted file mode 100644 index 5fc2bd6..0000000 Binary files a/src/en/wuxiaworldcloud/icon.png and /dev/null differ diff --git a/src/en/wuxiaworldco/WuxiaWorldCoScraper.js b/src/en/wuxiaworldco.js similarity index 57% rename from src/en/wuxiaworldco/WuxiaWorldCoScraper.js rename to src/en/wuxiaworldco.js index 30ec45d..1bfd7eb 100644 --- a/src/en/wuxiaworldco/WuxiaWorldCoScraper.js +++ b/src/en/wuxiaworldco.js @@ -1,11 +1,9 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://www.wuxiaworld.co/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; const result = await fetch(baseUrl); const body = await result.text(); @@ -20,7 +18,7 @@ const novelsScraper = async (req, res) => { let novelUrl = $(this).find("a").attr("href").slice(1); const novel = { - extensionId: 16, + sourceId: 16, novelName, novelCover, novelUrl, @@ -29,11 +27,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}${novelUrl}/`; const result = await fetch(url); @@ -43,26 +40,26 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 16; + novel.sourceId = 16; novel.sourceName = "WuxiaWorldCo"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".book-name").text(); novel.novelCover = $("div.book-img > img").attr("src"); - novel["Genre(s)"] = $("div.book-catalog > span.txt").text(); + novel.genre = $("div.book-catalog > span.txt").text(); - novel.Status = $("div.book-state > span.txt").text(); + novel.status = $("div.book-state > span.txt").text(); - novel["Author(s)"] = $("div.author > span.name").text(); + novel.author = $("div.author > span.name").text(); novelSummary = $("div.content > p.desc").html(); - novel.novelSummary = htmlToText(novelSummary); + novel.summary = novelSummary.trim(); let novelChapters = []; @@ -78,15 +75,12 @@ const novelScraper = async (req, res) => { }); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -96,43 +90,18 @@ const chapterScraper = async (req, res) => { const chapterName = $("h1.chapter-title").text(); let chapterText = $("div.chapter-entity").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($("a.next").length) { - nextChapter = $("a.next") - .attr("href") - .replace("/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($("a.prev").length) { - prevChapter = $("a.prev") - .attr("href") - .replace("/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - const chapter = { - extensionId: 16, + sourceId: 16, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}search/${searchTerm}/1`; const result = await fetch(url); @@ -151,7 +120,7 @@ const searchScraper = async (req, res) => { let novelUrl = $(this).find("a").attr("href").slice(1); const novel = { - extensionId: 16, + sourceId: 16, novelName, novelCover, novelUrl, @@ -160,12 +129,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = wuxiaWorldCoScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const WuxiaWorldCoScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default WuxiaWorldCoScraper; diff --git a/src/en/wuxiaworldco/WuxiaWorldCo.js b/src/en/wuxiaworldco/WuxiaWorldCo.js deleted file mode 100644 index 5273eaf..0000000 --- a/src/en/wuxiaworldco/WuxiaWorldCo.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const wuxiaWorldCoScraper = require("./WuxiaWorldCoScraper"); - -const router = express.Router(); - -router.get("/novels/", wuxiaWorldCoScraper.novelsScraper); - -router.get("/novel/:novelUrl", wuxiaWorldCoScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", wuxiaWorldCoScraper.chapterScraper); - -router.get("/search/", wuxiaWorldCoScraper.searchScraper); - -module.exports = router; diff --git a/src/en/wuxiaworldco/icon.png b/src/en/wuxiaworldco/icon.png deleted file mode 100644 index b732fa7..0000000 Binary files a/src/en/wuxiaworldco/icon.png and /dev/null differ diff --git a/src/en/wuxiaworldsite/WuxiaWorldSiteScraper.js b/src/en/wuxiaworldsite.js similarity index 57% rename from src/en/wuxiaworldsite/WuxiaWorldSiteScraper.js rename to src/en/wuxiaworldsite.js index 4530fce..3d10c09 100644 --- a/src/en/wuxiaworldsite/WuxiaWorldSiteScraper.js +++ b/src/en/wuxiaworldsite.js @@ -1,13 +1,22 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://wuxiaworldsite.co/"; const searchUrl = "https://wuxiaworldsite.co/search/"; -const novelsScraper = async (req, res) => { - const url = `${baseUrl}/power-ranking`; +const popularNovels = async (page) => { + let totalPages = 222; + const url = `https://wuxiaworldsite.co/ajax-story-power.ajax`; - const result = await fetch(url); + const formData = new FormData(); + formData.append("page", page); + formData.append("keyword", ""); + formData.append("count", 18); + formData.append("genres_include", ""); + formData.append("limit", 18); + formData.append("order_type", "DESC"); + formData.append("order_by", "views"); + + const result = await fetch(url, { method: "POST", body: formData }); const body = await result.text(); $ = cheerio.load(body); @@ -23,7 +32,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelId[2] + "/"; const novel = { - extensionId: 12, + sourceId: 12, novelName, novelCover, novelUrl, @@ -32,11 +41,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}${novelUrl}`; const result = await fetch(url); @@ -48,36 +56,36 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 12; + novel.sourceId = 12; novel.sourceName = "WuxiaWorldSite"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $(".content-reading > h1").text().trim(); novel.novelCover = baseUrl + $(".img-read> img").attr("src"); - novel.novelSummary = $(".story-introduction-content").text(); + novel.summary = $(".story-introduction-content").text(); - novel["Author(s)"] = $(".content-reading > p").text(); + novel.author = $(".content-reading > p").text().trim(); - novel["Artist(s)"] = null; + novel.artist = null; - novel["Genre(s)"] = ""; + novel.genre = ""; $(".a_tag_item").each((i, el) => { - novel["Genre(s)"] += $(el).text() + ","; + novel.genre += $(el).text() + ","; }); - novel["Genre(s)"] = novel["Genre(s)"].split(","); - novel["Genre(s)"].pop(); + novel.genre = novel.genre.split(","); + novel.genre.pop(); - novel.Status = novel["Genre(s)"].pop(); + novel.status = novel.genre.pop(); - novel["Genre(s)"] = novel["Genre(s)"].join(","); + novel.genre = novel.genre.join(","); const novelID = $(".show-more-list").attr("data-id"); @@ -108,15 +116,12 @@ const novelScraper = async (req, res) => { return novelChapters; }; - novel.novelChapters = await getChapters(novelID); + novel.chapters = await getChapters(novelID); - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -124,50 +129,24 @@ const chapterScraper = async (req, res) => { $ = cheerio.load(body); - let chapterText = $("#content").html() || ""; - let chapterName = ""; - if (chapterText) { - chapterText = chapterText.split("
"); - chapterName = chapterText.shift(); - } else { - $("p").each((i, el) => { - if ($(el).css("display") !== "none") { - chapterText += $(el).text() + "\n"; - } - }); - chapterText = chapterText.split("\n"); - chapterName = chapterText.shift(); - } - - chapterText = chapterText.join("\n"); - - const prevChapter = $(".pre").attr("href") - ? $(".pre").attr("href").split("/").pop() - : null; - - const nextChapter = $(".next_sesction > a").attr("href") - ? $(".next_sesction > a").attr("href").split("/").pop() - : null; + let chapterName = $("h1.text-center.heading_read").text(); + let chapterText = $(".content-story").html(); novelUrl += "/"; chapterUrl += "/"; const chapter = { - extensionId: 12, + sourceId: 12, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${searchUrl}${searchTerm}`; const result = await fetch(url); @@ -185,7 +164,7 @@ const searchScraper = async (req, res) => { novelUrl = novelId[2] + "/"; const novel = { - extensionId: 12, + sourceId: 12, novelName, novelCover, novelUrl, @@ -194,12 +173,14 @@ const searchScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = WuxiaWorldSiteScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const WuxiaWorldSiteScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default WuxiaWorldSiteScraper; diff --git a/src/en/wuxiaworldsite/WuxiaWorldSite.js b/src/en/wuxiaworldsite/WuxiaWorldSite.js deleted file mode 100644 index 0cf16e8..0000000 --- a/src/en/wuxiaworldsite/WuxiaWorldSite.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const WuxiaWorldScraper = require("./WuxiaWorldSiteScraper"); - -const router = express.Router(); - -router.get("/novels/", WuxiaWorldSiteScraper.novelsScraper); - -router.get("/novel/:novelUrl", WuxiaWorldSiteScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", WuxiaWorldSiteScraper.chapterScraper); - -router.get("/search/", WuxiaWorldSiteScraper.searchScraper); - -module.exports = router; diff --git a/src/en/wuxiaworldsite/icon.png b/src/en/wuxiaworldsite/icon.png deleted file mode 100644 index f94b242..0000000 Binary files a/src/en/wuxiaworldsite/icon.png and /dev/null differ diff --git a/src/es/einherjarproject/EinherjarProjectScraper.js b/src/es/einherjarproject.js similarity index 74% rename from src/es/einherjarproject/EinherjarProjectScraper.js rename to src/es/einherjarproject.js index 73dff33..b363fdb 100644 --- a/src/es/einherjarproject/EinherjarProjectScraper.js +++ b/src/es/einherjarproject.js @@ -1,11 +1,9 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://einherjarproject.net/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; let url = baseUrl + "proyectos-activos/"; const result = await fetch(url); @@ -26,7 +24,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace(baseUrl, ""); const novel = { - extensionId: 25, + sourceId: 25, novelName, novelCover, novelUrl, @@ -35,11 +33,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = baseUrl + novelUrl; const result = await fetch(url); @@ -49,13 +46,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 25; + novel.sourceId = 25; novel.sourceName = "Einherjar Project"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $("h1").text(); @@ -65,33 +62,33 @@ const novelScraper = async (req, res) => { .find("li") .each(function (result) { if ($(this).text().includes("Autor:")) { - novel["Author(s)"] = $(this) + novel.author = $(this) .text() .replace("Autor:", "") .slice(0, -1); } if ($(this).text().includes("Ilustrador: ")) { - novel["Artist(s)"] = $(this) + novel.artist = $(this) .text() .replace("Ilustrador: ", "") .slice(0, -1); } if ($(this).text().includes("Estado: ")) { - novel.Status = $(this) + novel.status = $(this) .text() .replace("Estado: ", "") .slice(0, -1); } }); - novel["Genre(s)"] = $(".post-content > h6") + novel.genre = $(".post-content > h6") .text() .replace(/GÉNEROS: /, "") .replace(/,\s/g, ","); let novelSummary = $(".post-content > .has-text-align-center").html(); - novel.novelSummary = htmlToText(novelSummary); + novel.summary = novelSummary.trim(); let novelChapters = []; @@ -114,17 +111,14 @@ const novelScraper = async (req, res) => { } }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = baseUrl + chapterUrl; - console.log(url); + const result = await fetch(url); const body = await result.text(); @@ -133,30 +127,21 @@ const chapterScraper = async (req, res) => { let chapterName = $("h1.post-title").text(); let chapterText = $(".post-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - novelUrl = novelUrl + "/"; chapterUrl = chapterUrl + "/"; const chapter = { - extensionId: 25, + sourceId: 25, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = baseUrl + "?s=" + searchTerm; const result = await fetch(url); @@ -186,7 +171,7 @@ const searchScraper = async (req, res) => { .replace(baseUrl, ""); const novel = { - extensionId: 25, + sourceId: 25, novelName, novelCover, novelUrl, @@ -196,12 +181,14 @@ const searchScraper = async (req, res) => { } }); - res.json(novels); + return novels; }; -module.exports = einharjarProjectScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const EinharjarProjectScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default EinharjarProjectScraper; diff --git a/src/es/einherjarproject/EinherjarProject.js b/src/es/einherjarproject/EinherjarProject.js deleted file mode 100644 index f273888..0000000 --- a/src/es/einherjarproject/EinherjarProject.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const einherjarProjectScraper = require("./EinherjarProjectScraper"); - -const router = express.Router(); - -router.get("/novels/", einherjarProjectScraper.novelsScraper); - -router.get("/novel/:novelUrl", einherjarProjectScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:chapterUrl", - einherjarProjectScraper.chapterScraper -); - -router.get("/search/", einherjarProjectScraper.searchScraper); - -module.exports = router; diff --git a/src/es/einherjarproject/icon.png b/src/es/einherjarproject/icon.png deleted file mode 100644 index b9ef6dc..0000000 Binary files a/src/es/einherjarproject/icon.png and /dev/null differ diff --git a/src/es/hasutl/HasutlScraper.js b/src/es/hasutl.js similarity index 65% rename from src/es/hasutl/HasutlScraper.js rename to src/es/hasutl.js index 904f549..5889d41 100644 --- a/src/es/hasutl/HasutlScraper.js +++ b/src/es/hasutl.js @@ -1,11 +1,9 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://hasutl.wordpress.com/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; let url = baseUrl + "light-novels-activas/"; const result = await fetch(url); @@ -23,7 +21,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace(baseUrl, ""); const novel = { - extensionId: 29, + sourceId: 29, novelName, novelCover, novelUrl, @@ -32,11 +30,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}/${novelUrl}/`; const result = await fetch(url); @@ -46,13 +43,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 29; + novel.sourceId = 29; novel.sourceName = "Hasu Translations"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $(".post-header").text(); @@ -64,22 +61,22 @@ const novelScraper = async (req, res) => { details = details.match(/<\/strong>(.|\n)*?
/g); details = details.map((detail) => detail.replace(/<\/strong>|
/g, "")); - novel["Genre(s)"] = ""; + novel.genre = ""; detailName.map((detail, index) => { if (detail.includes("Autor")) { - novel["Author(s)"] = details[index]; + novel.author = details[index]; } if (detail.includes("Géneros")) { - novel["Genre(s)"] = details[index].replace(/\s/g, ""); + novel.genre = details[index].replace(/\s/g, ""); } if (detail.includes("Artista")) { - novel["Artist(s)"] = details[index]; + novel.artist = details[index]; } }); let novelSummary = $(".post-content").find("p").html(); - novel.novelSummary = htmlToText(novelSummary); + novel.summary = novelSummary; let novelChapters = []; @@ -98,15 +95,12 @@ const novelScraper = async (req, res) => { novelChapters.push(chapter); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -117,45 +111,21 @@ const chapterScraper = async (req, res) => { let chapterName = $(".post-title").text(); let chapterText = $(".post-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - // if ($(".nav-next").length) { - // nextChapter = $(".nav-next") - // .find("a") - // .attr("href") - // .replace(baseUrl + "vipnovel/" + novelUrl + "/", ""); - // } - - let prevChapter = null; - - // if ($(".nav-previous").length) { - // prevChapter = $(".nav-previous") - // .find("a") - // .attr("href") - // .replace(baseUrl + "vipnovel/" + novelUrl + "/", ""); - // } - novelUrl = novelUrl + "/"; chapterUrl = chapterUrl; const chapter = { - extensionId: 29, + sourceId: 29, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga`; const result = await fetch(url); @@ -178,7 +148,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl.replace(baseUrl, ""); const novel = { - extensionId: 29, + sourceId: 29, novelName, novelCover, novelUrl, @@ -188,12 +158,13 @@ const searchScraper = async (req, res) => { } }); - res.json(novels); + return novels; +}; +const HasuTlScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; -module.exports = HasutlScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, -}; +export default HasuTlScraper; diff --git a/src/es/hasutl/Hasutl.js b/src/es/hasutl/Hasutl.js deleted file mode 100644 index 53112b7..0000000 --- a/src/es/hasutl/Hasutl.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const HasutlScraper = require("./HasutlScraper"); - -const router = express.Router(); - -router.get("/novels/", HasutlScraper.novelsScraper); - -router.get("/novel/:novelUrl", HasutlScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", HasutlScraper.chapterScraper); - -router.get("/search/", HasutlScraper.searchScraper); - -module.exports = router; diff --git a/src/es/hasutl/icon.png b/src/es/hasutl/icon.png deleted file mode 100644 index 9ca3582..0000000 Binary files a/src/es/hasutl/icon.png and /dev/null differ diff --git a/src/es/novelasligera/NovelasLigeraScraper.js b/src/es/novelasligera.js similarity index 60% rename from src/es/novelasligera/NovelasLigeraScraper.js rename to src/es/novelasligera.js index efba1c5..5e9c0fb 100644 --- a/src/es/novelasligera/NovelasLigeraScraper.js +++ b/src/es/novelasligera.js @@ -1,11 +1,10 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; +import { htmlToText } from "../helpers/htmlToText"; const baseUrl = "https://novelasligera.com/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; let url = baseUrl; const result = await fetch(url); @@ -27,7 +26,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace("novela/", ""); const novel = { - extensionId: 26, + sourceId: 26, novelName, novelCover, novelUrl, @@ -37,13 +36,14 @@ const novelsScraper = async (req, res) => { } }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = baseUrl + "novela/" + novelUrl; + console.log(url); + const result = await fetch(url); const body = await result.text(); @@ -51,13 +51,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 26; + novel.sourceId = 26; novel.sourceName = "Novelas Ligera"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $("h1").text(); @@ -67,25 +67,21 @@ const novelScraper = async (req, res) => { .find("p") .each(function (result) { if ($(this).text().includes("Autor:")) { - novel["Author(s)"] = $(this) - .text() - .replace("Autor:", "") - .trim(); + novel.author = $(this).text().replace("Autor:", "").trim(); } if ($(this).text().includes("Estado:")) { - novel.Status = $(this).text().replace("Estado: ", "").trim(); + novel.status = $(this).text().replace("Estado: ", "").trim(); } if ($(this).text().includes("Género:")) { $(this).find("span").remove(); - novel["Genre(s)"] = $(this).text().replace(/,\s/g, ","); + novel.genre = $(this).text().replace(/,\s/g, ","); } }); - novel["Artist(s)"] = null; + novel.artist = null; - let novelSummary = $(".elementor-text-editor.elementor-clearfix").html(); - novel.novelSummary = htmlToText(novelSummary); + novel.novelSummary = $(".elementor-text-editor.elementor-clearfix").text(); let novelChapters = []; @@ -106,25 +102,13 @@ const novelScraper = async (req, res) => { novelChapters.push(chapter); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - const volumeUrl = req.params.volumeUrl; - - let optionalUrl; - - if (volumeUrl) { - optionalUrl = volumeUrl + "/"; - } else { - optionalUrl = novelUrl + "/"; - } - - const url = baseUrl + "novela/" + optionalUrl + chapterUrl; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = baseUrl + "novela/" + chapterUrl; console.log(url); const result = await fetch(url); @@ -134,22 +118,6 @@ const chapterScraper = async (req, res) => { let chapterName = $("h1.entry-title").text(); - let nextChapter = null; - - // if ($(".wp-post-navigation-next").length > 0) { - // nextChapter = - // $(".wp-post-navigation-next").find("a").attr("href") || null; - // nextChapter && nextChapter.replace(baseUrl + "novela/", ""); - // } - - let prevChapter = null; - - // if ($(".wp-post-navigation-pre").length > 0) { - // prevChapter = - // $(".wp-post-navigation-pre").find("a").attr("href") || null; - // prevChapter && prevChapter.replace(baseUrl + "novela/", ""); - // } - $(".osny-nightmode.osny-nightmode--left").remove(); $(".code-block.code-block-1").remove(); $(".adsb30").remove(); @@ -159,25 +127,18 @@ const chapterScraper = async (req, res) => { let chapterText = $(".entry-content").html(); chapterText = htmlToText(chapterText, { preserveNewlines: true }); - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; - const chapter = { - extensionId: 26, + sourceId: 26, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = baseUrl + "?s=" + searchTerm + "&post_type=wp-manga"; console.log(url); @@ -203,7 +164,7 @@ const searchScraper = async (req, res) => { novelUrl += "/"; const novel = { - extensionId: 26, + sourceId: 26, novelName, novelCover, novelUrl, @@ -214,12 +175,14 @@ const searchScraper = async (req, res) => { novels = [{ ...novels[1] }]; - res.json(novels); + return novels; }; -module.exports = einharjarProjectScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const NovelasLigeraScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default NovelasLigeraScraper; diff --git a/src/es/novelasligera/NovelasLigera.js b/src/es/novelasligera/NovelasLigera.js deleted file mode 100644 index e92631e..0000000 --- a/src/es/novelasligera/NovelasLigera.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const NovelasLigeraScraper = require("./NovelasLigeraScraper"); - -const router = express.Router(); - -router.get("/novels/", NovelasLigeraScraper.novelsScraper); - -router.get("/novel/:novelUrl", NovelasLigeraScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:volumeUrl?/:chapterUrl", - NovelasLigeraScraper.chapterScraper -); - -router.get("/search/", NovelasLigeraScraper.searchScraper); - -module.exports = router; diff --git a/src/es/novelasligera/icon.png b/src/es/novelasligera/icon.png deleted file mode 100644 index 09e1fdc..0000000 Binary files a/src/es/novelasligera/icon.png and /dev/null differ diff --git a/src/es/novelawuxia/NovelaWuxiaScraper.js b/src/es/novelawuxia.js similarity index 72% rename from src/es/novelawuxia/NovelaWuxiaScraper.js rename to src/es/novelawuxia.js index 5944530..9c12e96 100644 --- a/src/es/novelawuxia/NovelaWuxiaScraper.js +++ b/src/es/novelawuxia.js @@ -1,7 +1,4 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "http://www.novelawuxia.com/"; @@ -9,10 +6,19 @@ function getNovelName(y) { return y.replace(/-/g, " ").replace(/(?:^|\s)\S/g, (a) => a.toUpperCase()); } -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; let url = baseUrl + "p/todas-las-novelas.html"; - const result = await fetch(url); + let headers = new Headers({ + "User-Agent": + "'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", + }); + + const result = await fetch(url, { + method: "GET", + headers: headers, + }); const body = await result.text(); $ = cheerio.load(body); @@ -34,7 +40,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl.replace(`${baseUrl}p/`, "") + "/"; const novel = { - extensionId: 31, + sourceId: 31, novelName, novelCover, novelUrl, @@ -43,12 +49,11 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}p/${novelUrl}`; +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}p/${novelUrl.replace("/", "")}`; const result = await fetch(url); const body = await result.text(); @@ -57,20 +62,20 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 31; + novel.sourceId = 31; novel.sourceName = "Novela Wuxia"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = `${novelUrl}/`; + novel.novelUrl = novelUrl; novel.novelName = $("h1.post-title").text().trim(); novel.novelCover = $("div.separator").find("a").attr("href"); - novel["Artist(s)"] = ""; - novel.Status = ""; + novel.artist = ""; + novel.status = ""; $("div > b").each(function (result) { const detailName = $(this).text(); @@ -80,14 +85,14 @@ const novelScraper = async (req, res) => { detail = detail.nodeValue; if (detailName.includes("Autor")) { - novel["Author(s)"] = detail.replace("Autor:", ""); + novel.author = detail.replace("Autor:", ""); } if (detailName.includes("Estatus")) { - novel.Status = detail.replace("Estatus: ", ""); + novel.status = detail.replace("Estatus: ", ""); } if (detailName.includes("Géneros:")) { - novel["Genre(s)"] = detail + novel.genre = detail .replace("Géneros: ", "") .replace(/,\s/g, ","); } @@ -98,13 +103,16 @@ const novelScraper = async (req, res) => { $("div").each(function (result) { const detailName = $(this).text(); - if (detailName.includes("Sinopsis")) { - novel.novelSummary = $(this) - .next() - .text() - .replace("Sinopsis", "") - .trim(); + novel.summary = + $(this).next().text() !== "" + ? $(this).next().text().replace("Sinopsis", "").trim() + : $(this) + .next() + .next() + .text() + .replace("Sinopsis", "") + .trim(); } if (detailName.includes("Lista de Capítulos")) { @@ -137,21 +145,14 @@ const novelScraper = async (req, res) => { } }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let year = req.params.year; - let month = req.params.month; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}${year}/${month}/${chapterUrl}`; - console.log(url); - const result = await fetch(url); const body = await result.text(); @@ -160,30 +161,21 @@ const chapterScraper = async (req, res) => { let chapterName = $("h1.post-title").text().trim(); let chapterText = $(".post-body.entry-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - novelUrl = novelUrl + "/"; chapterUrl = chapterUrl + "/"; const chapter = { - extensionId: 31, + sourceId: 31, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { const url = `${baseUrl}search?q=${searchTerm}`; const result = await fetch(url); @@ -210,7 +202,7 @@ const searchScraper = async (req, res) => { if (!exists) { const novelCover = null; const novel = { - extensionId: 31, + sourceId: 31, novelName, novelCover, novelUrl, @@ -220,12 +212,14 @@ const searchScraper = async (req, res) => { } }); - res.json(novels); + return novels; }; -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const NovelaWuxiaScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default NovelaWuxiaScraper; diff --git a/src/es/novelawuxia/NovelaWuxia.js b/src/es/novelawuxia/NovelaWuxia.js deleted file mode 100644 index 511b9b1..0000000 --- a/src/es/novelawuxia/NovelaWuxia.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const NovelaWuxiaScraper = require("./NovelaWuxiaScraper"); - -const router = express.Router(); - -router.get("/novels/", NovelaWuxiaScraper.novelsScraper); - -router.get("/novel/:novelUrl", NovelaWuxiaScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:year/:month/:chapterUrl", - NovelaWuxiaScraper.chapterScraper -); - -router.get("/search/", NovelaWuxiaScraper.searchScraper); - -module.exports = router; diff --git a/src/es/novelawuxia/icon.png b/src/es/novelawuxia/icon.png deleted file mode 100644 index 623c577..0000000 Binary files a/src/es/novelawuxia/icon.png and /dev/null differ diff --git a/src/es/oasistranslations/OasisTranslationsScraper.js b/src/es/oasistranslations.js similarity index 77% rename from src/es/oasistranslations/OasisTranslationsScraper.js rename to src/es/oasistranslations.js index 4dc7514..432925a 100644 --- a/src/es/oasistranslations/OasisTranslationsScraper.js +++ b/src/es/oasistranslations.js @@ -1,13 +1,12 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://oasistranslations.wordpress.com/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { let url = baseUrl; + let totalPages = 1; + const result = await fetch(url); const body = await result.text(); @@ -27,7 +26,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl[novelUrl.length - 2] + "/"; const novel = { - extensionId: 30, + sourceId: 30, novelName, novelCover, novelUrl, @@ -37,11 +36,10 @@ const novelsScraper = async (req, res) => { } }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = baseUrl + novelUrl + "/"; const result = await fetch(url); @@ -51,13 +49,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 30; + novel.sourceId = 30; novel.sourceName = "Oasis Translations"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $("h1.entry-title") .text() @@ -74,16 +72,16 @@ const novelScraper = async (req, res) => { detail.replace(/|<\/strong>|
|:\s/g, "") ); - novel["Genre(s)"] = ""; + novel.genre = ""; - novel["Author(s)"] = details[2]; - novel["Genre(s)"] = details[4].replace(/\s| /g, ""); - novel["Artist(s)"] = details[3]; + novel.author = details[2]; + novel.genre = details[4].replace(/\s| /g, ""); + novel.artist = details[3]; } }); // let novelSummary = $(this).next().html(); - novel.novelSummary = ""; + novel.summary = ""; let novelChapters = []; @@ -125,15 +123,12 @@ const novelScraper = async (req, res) => { // }); // } - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -146,29 +141,21 @@ const chapterScraper = async (req, res) => { $("div#jp-post-flair").remove(); let chapterText = $(".entry-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - novelUrl = novelUrl + "/"; chapterUrl = chapterUrl + "/"; const chapter = { - extensionId: 30, + sourceId: 30, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - let searchTerm = req.query.s; +const searchNovels = async (searchTerm) => { searchTerm = searchTerm.toLowerCase(); let url = baseUrl; @@ -191,7 +178,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl[novelUrl.length - 2] + "/"; const novel = { - extensionId: 30, + sourceId: 30, novelName, novelCover, novelUrl, @@ -205,12 +192,14 @@ const searchScraper = async (req, res) => { novel.novelName.toLowerCase().includes(searchTerm) ); - res.json(novels); + return novels; }; -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const OasisTranslationsScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default OasisTranslationsScraper; diff --git a/src/es/oasistranslations/OasisTranslations.js b/src/es/oasistranslations/OasisTranslations.js deleted file mode 100644 index a4568a4..0000000 --- a/src/es/oasistranslations/OasisTranslations.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const OasisTranslationsScraper = require("./OasisTranslationsScraper"); - -const router = express.Router(); - -router.get("/novels/", OasisTranslationsScraper.novelsScraper); - -router.get("/novel/:novelUrl", OasisTranslationsScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:chapterUrl", - OasisTranslationsScraper.chapterScraper -); - -router.get("/search/", OasisTranslationsScraper.searchScraper); - -module.exports = router; diff --git a/src/es/oasistranslations/icon.png b/src/es/oasistranslations/icon.png deleted file mode 100644 index b529d0d..0000000 Binary files a/src/es/oasistranslations/icon.png and /dev/null differ diff --git a/src/es/skynovels/SkynovelsScraper.js b/src/es/skynovels.js similarity index 62% rename from src/es/skynovels/SkynovelsScraper.js rename to src/es/skynovels.js index 64f00ac..232f70f 100644 --- a/src/es/skynovels/SkynovelsScraper.js +++ b/src/es/skynovels.js @@ -1,11 +1,7 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); - const baseUrl = "https://www.skynovels.net/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; const url = "https://api.skynovels.net/api/novels?&q"; const result = await fetch(url); @@ -21,17 +17,17 @@ const novelsScraper = async (req, res) => { "/novels/false"; const novelUrl = res.id + "/" + res.nvl_name + "/"; - const novel = { extensionId: 24, novelName, novelUrl, novelCover }; + const novel = { sourceId: 24, novelName, novelUrl, novelCover }; novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelId = req.params.novelId; - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novUrl) => { + let novelId = novUrl.split("/")[0]; + let novelUrl = novUrl[1]; const url = "https://api.skynovels.net/api/novel/" + novelId + "/reading?&q"; @@ -42,13 +38,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 24; + novel.sourceId = 24; novel.sourceName = "SkyNovels"; - novel.sourceUrl = baseUrl + "novelas/" + novelId + "/" + novelUrl; + novel.url = baseUrl + "novelas/" + novelId + "/" + novelUrl; - novel.novelUrl = novelId + "/" + novelUrl + "/"; + novel.novelUrl = novUrl; novel.novelName = item.nvl_title; @@ -59,10 +55,10 @@ const novelScraper = async (req, res) => { let genres = []; item.genres.map((genre) => genres.push(genre.genre_name)); - novel["Genre(s)"] = genres.join(","); - novel["Author(s)"] = item.nvl_writer; - novel.novelSummary = item.nvl_content; - novel.Status = item.nvl_status; + novel.genre = genres.join(","); + novel.author = item.nvl_writer; + novel.summary = item.nvl_content; + novel.status = item.nvl_status; let novelChapters = []; @@ -78,21 +74,18 @@ const novelScraper = async (req, res) => { }); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelId = req.params.novelId; - let novelUrl = req.params.novelUrl; - let chapterId = req.params.chapterId; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novUrl, chapUrl) => { + let novelId = novUrl.split("/")[0]; + let novelUrl = novUrl[1]; + let chapterId = chapUrl.split("/")[0]; + let chapterUrl = chapUrl[1]; const url = `https://api.skynovels.net/api/novel-chapter/${chapterId}`; - console.log(url); - const result = await fetch(url); const body = await result.json(); @@ -100,29 +93,23 @@ const chapterScraper = async (req, res) => { let chapterName = item.chp_index_title; - let chapterText = parseHtml(item.chp_content); - - let nextChapter = null; - let prevChapter = null; + let chapterText = item.chp_content; novelUrl = novelId + "/" + novelUrl + "/"; chapterUrl = item.id + "/" + item.chp_name; const chapter = { - extensionId: 24, + sourceId: 24, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - let searchTerm = req.query.s; +const searchNovels = async (searchTerm) => { searchTerm = searchTerm.toLowerCase(); const url = "https://api.skynovels.net/api/novels?&q"; @@ -143,17 +130,19 @@ const searchScraper = async (req, res) => { "/novels/false"; const novelUrl = res.id + "/" + res.nvl_name + "/"; - const novel = { extensionId: 24, novelName, novelUrl, novelCover }; + const novel = { sourceId: 24, novelName, novelUrl, novelCover }; novels.push(novel); }); - res.json(novels); + return novels; }; -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const SkyNovelsScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default SkyNovelsScraper; diff --git a/src/es/skynovels/Skynovels.js b/src/es/skynovels/Skynovels.js deleted file mode 100644 index 8187fe6..0000000 --- a/src/es/skynovels/Skynovels.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const skynovelsScraper = require("./SkynovelsScraper"); - -const router = express.Router(); - -router.get("/novels/", skynovelsScraper.novelsScraper); - -router.get("/novel/:novelId/:novelUrl/", skynovelsScraper.novelScraper); - -router.get( - "/novel/:novelId/:novelUrl/:chapterId/:chapterUrl", - skynovelsScraper.chapterScraper -); - -router.get("/search/", skynovelsScraper.searchScraper); - -module.exports = router; diff --git a/src/es/skynovels/icon.png b/src/es/skynovels/icon.png deleted file mode 100644 index e708d43..0000000 Binary files a/src/es/skynovels/icon.png and /dev/null differ diff --git a/src/es/tunovelaligera.js b/src/es/tunovelaligera.js new file mode 100644 index 0000000..282bddd --- /dev/null +++ b/src/es/tunovelaligera.js @@ -0,0 +1,190 @@ +import cheerio from "react-native-cheerio"; +import { defaultCoverUri, Status } from "../helpers/constants"; + +const sourceId = 23; +const sourceName = "TuNovelaLigera"; + +const baseUrl = "https://tunovelaligera.com/"; + +const popularNovels = async (page) => { + let totalPages = 62; + let url = baseUrl + "novelas/page/" + page + "/?m_orderby=rating"; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".page-item-detail").each(function () { + const novelName = $(this).find(".h5 > a").text(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this).find(".h5 > a").attr("href").split("/")[4]; + novelUrl += "/"; + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = `${baseUrl}novelas/${novelUrl}`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url }; + + novel.novelUrl = novelUrl; + + $(".manga-title-badges").remove(); + + novel.novelName = $(".post-title > h1").text().trim(); + + novelCover = $(".summary_image > a > img"); + + novel.novelCover = + novelCover.attr("data-src") || + novelCover.attr("src") || + defaultCoverUri; + + $(".post-content_item").each(function () { + const detailName = $(this).find(".summary-heading > h5").text().trim(); + const detail = $(this).find(".summary-content").text().trim(); + + switch (detailName) { + case "Generos": + novel.genre = detail.replace(/, /g, ","); + break; + case "Autores": + novel.author = detail; + break; + case "Estado": + novel.status = + detail.includes("OnGoing") || detail.includes("Updating") + ? Status.ONGOING + : Status.COMPLETED; + break; + } + }); + + novel.summary = $("div.summary__content > p").text().trim(); + + let novelChapters = []; + + const novelId = + $(".rating-post-id").attr("value") || + $("#manga-chapters-holder").attr("data-id"); + + let formData = new FormData(); + formData.append("action", "manga_get_chapters"); + formData.append("manga", novelId); + + const data = await fetch( + "https://tunovelaligera.com/wp-admin/admin-ajax.php", + { + method: "POST", + body: formData, + } + ); + const text = await data.text(); + + $ = cheerio.load(text); + + $(".wp-manga-chapter").each(function (result) { + chapterName = $(this) + .find("a") + .text() + .replace(/[\t\n]/g, "") + .trim(); + + releaseDate = $(this).find("span").text().trim(); + + chapterUrl = $(this).find("a").attr("href").split("/"); + + chapterUrl[6] + ? (chapterUrl = chapterUrl[5] + "/" + chapterUrl[6]) + : (chapterUrl = chapterUrl[5]); + + novelChapters.push({ chapterName, releaseDate, chapterUrl }); + }); + + novel.chapters = novelChapters.reverse(); + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = `${baseUrl}novelas/${novelUrl}/${chapterUrl}`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = $("h1#chapter-heading").text(); + + let chapterText = $(".text-left").html(); + novelUrl = novelUrl + "/"; + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga`; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".c-tabs-item__content").each(function (result) { + const novelName = $(this).find(".h4 > a").text(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this).find(".h4 > a").attr("href"); + novelUrl = novelUrl.replace(`${baseUrl}novelas/`, ""); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const TuNovelaLigeraScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default TuNovelaLigeraScraper; diff --git a/src/es/tunovelaligera/Tunovelaligera.js b/src/es/tunovelaligera/Tunovelaligera.js deleted file mode 100644 index 24d41db..0000000 --- a/src/es/tunovelaligera/Tunovelaligera.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const tunovelaligeraScraper = require("./TunovelaligeraScraper"); - -const router = express.Router(); - -router.get("/novels/", tunovelaligeraScraper.novelsScraper); - -router.get("/novel/:novelUrl", tunovelaligeraScraper.novelScraper); - -router.get( - "/novel/:novelUrl/:volumeUrl?/:chapterUrl", - tunovelaligeraScraper.chapterScraper -); - -router.get("/search/", tunovelaligeraScraper.searchScraper); - -module.exports = router; diff --git a/src/es/tunovelaligera/TunovelaligeraScraper.js b/src/es/tunovelaligera/TunovelaligeraScraper.js deleted file mode 100644 index 0577bb3..0000000 --- a/src/es/tunovelaligera/TunovelaligeraScraper.js +++ /dev/null @@ -1,240 +0,0 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); -const FormData = require("form-data"); - -const baseUrl = "https://tunovelaligera.com/"; - -const novelsScraper = async (req, res) => { - let url = baseUrl + "mejores-novelas-originales/"; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $(".page-item-detail").each(function (result) { - const novelName = $(this).find(".h5 > a").text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this).find(".h5 > a").attr("href").split("/")[4]; - novelUrl += "/"; - - const novel = { - extensionId: 23, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const url = `${baseUrl}novelas/${novelUrl}/`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novel = {}; - - novel.extensionId = 23; - - novel.sourceName = "Tunovelaligera"; - - novel.sourceUrl = url; - - novel.novelUrl = novelUrl + "/"; - - novel.novelName = $("h1.titulo-novela") - .text() - .replace(/[\t\n]/g, "") - .trim(); - - novel.novelCover = $(".summary_image > a > img").attr("src"); - - $(".post-content_item").each(function (result) { - detailName = $(this) - .find(".summary-heading > h5") - .text() - .replace(/[\t\n]/g, "") - .trim(); - detail = $(this) - .find(".summary-content") - .text() - .replace(/[\t\n]/g, "") - .trim(); - - novel[detailName] = detail; - }); - - novel["Genre(s)"] = novel.Generos.replace(/, /g, ","); - novel["Author(s)"] = novel.Autores; - novel.Status = novel.Estado; - novel.Status = null; - - novel["Artist(s)"] = novel["Artista(s)"]; - - delete novel.Genre; - delete novel["Artista(s)"]; - delete novel.Autores; - delete novel.Estado; - - let novelSummary = ""; - - $(".summary__content") - .find("p") - .each(function () { - novelSummary += $(this).html(); - }); - - novel.novelSummary = htmlToText(novelSummary); - - let novelChapters = []; - - const novelId = $(".wp-manga-action-button").attr("data-post"); - - let formData = new FormData(); - formData.append("action", "manga_get_chapters"); - formData.append("manga", novelId); - - const data = await fetch( - "https://tunovelaligera.com/wp-admin/admin-ajax.php", - { - method: "POST", - body: formData, - } - ); - const text = await data.text(); - - $ = cheerio.load(text); - - $(".wp-manga-chapter").each(function (result) { - chapterName = $(this) - .find("a") - .text() - .replace(/[\t\n]/g, "") - .trim(); - - releaseDate = $(this).find("span").text().trim(); - - chapterUrl = $(this).find("a").attr("href").split("/"); - - chapterUrl[6] - ? (chapterUrl = chapterUrl[5] + "/" + chapterUrl[6]) - : (chapterUrl = chapterUrl[5]); - - novelChapters.push({ chapterName, releaseDate, chapterUrl }); - }); - - novel.novelChapters = novelChapters.reverse(); - - res.json(novel); -}; - -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - let volumeUrl = req.params.volumeUrl; - - let optionalUrl; - - if (volumeUrl) { - optionalUrl = volumeUrl + "/"; - } else { - optionalUrl = ""; - } - - const url = `${baseUrl}novelas/${novelUrl}/${optionalUrl}${chapterUrl}/`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let chapterName = $("h1#chapter-heading").text(); - - let chapterText = $(".text-left").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - if ($(".nav-next").length) { - nextChapter = $(".nav-next") - .find("a") - .attr("href") - .replace(baseUrl + "novelas/" + novelUrl + "/", ""); - } - - let prevChapter = null; - - if ($(".nav-previous").length) { - prevChapter = $(".nav-previous") - .find("a") - .attr("href") - .replace(baseUrl + "novelas/" + novelUrl + "/", ""); - } - - novelUrl = novelUrl + "/"; - chapterUrl = optionalUrl + chapterUrl; - - const chapter = { - extensionId: 23, - novelUrl, - chapterUrl, - chapterName, - chapterText, - nextChapter, - prevChapter, - }; - - res.json(chapter); -}; - -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - - const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga`; - - const result = await fetch(url); - const body = await result.text(); - - $ = cheerio.load(body); - - let novels = []; - - $(".c-tabs-item__content").each(function (result) { - const novelName = $(this).find(".h4 > a").text(); - const novelCover = $(this).find("img").attr("src"); - - let novelUrl = $(this).find(".h4 > a").attr("href"); - novelUrl = novelUrl.replace(`${baseUrl}novelas/`, ""); - - const novel = { - extensionId: 23, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - res.json(novels); -}; - -module.exports = tunovelaligeraScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, -}; diff --git a/src/es/tunovelaligera/icon.png b/src/es/tunovelaligera/icon.png deleted file mode 100644 index e42f5e0..0000000 Binary files a/src/es/tunovelaligera/icon.png and /dev/null differ diff --git a/src/es/yuukitls/YuukiTlsScraper.js b/src/es/yuukitls.js similarity index 76% rename from src/es/yuukitls/YuukiTlsScraper.js rename to src/es/yuukitls.js index dd9e179..1db606b 100644 --- a/src/es/yuukitls/YuukiTlsScraper.js +++ b/src/es/yuukitls.js @@ -1,11 +1,9 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; const baseUrl = "https://yuukitls.com/"; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + let totalPages = 1; let url = baseUrl; const result = await fetch(url); @@ -26,7 +24,7 @@ const novelsScraper = async (req, res) => { novelUrl = novelUrl[novelUrl.length - 2] + "/"; const novel = { - extensionId: 28, + sourceId: 28, novelName, novelCover, novelUrl, @@ -35,11 +33,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = `${baseUrl}${novelUrl}/`; const result = await fetch(url); @@ -49,13 +46,13 @@ const novelScraper = async (req, res) => { let novel = {}; - novel.extensionId = 28; + novel.sourceId = 28; novel.sourceName = "YuukiTls"; - novel.sourceUrl = url; + novel.url = url; - novel.novelUrl = novelUrl + "/"; + novel.novelUrl = novelUrl; novel.novelName = $("h1.entry-title") .text() @@ -68,26 +65,23 @@ const novelScraper = async (req, res) => { .find("div") .each(function (result) { if ($(this).text().includes("Escritor:")) { - novel["Author(s)"] = $(this) - .text() - .replace("Escritor: ", "") - .trim(); + novel.author = $(this).text().replace("Escritor: ", "").trim(); } if ($(this).text().includes("Ilustrador:")) { - novel["Artist(s)"] = $(this) + novel.artist = $(this) .text() .replace("Ilustrador: ", "") .trim(); } if ($(this).text().includes("Género:")) { - novel["Genre(s)"] = $(this) + novel.genre = $(this) .text() .replace(/Género: |\s/g, ""); } if ($(this).text().includes("Sinopsis:")) { - novel.novelSummary = $(this).next().text(); + novel.summary = $(this).next().text(); } }); @@ -131,15 +125,12 @@ const novelScraper = async (req, res) => { }); } - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - +const parseChapter = async (novelUrl, chapterUrl) => { const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; const result = await fetch(url); @@ -150,29 +141,21 @@ const chapterScraper = async (req, res) => { let chapterName = $("h3").text(); let chapterText = $(".entry-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; - novelUrl = novelUrl + "/"; chapterUrl = chapterUrl + "/"; const chapter = { - extensionId: 28, + sourceId: 28, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - let searchTerm = req.query.s; +const searchNovels = async (searchTerm) => { searchTerm = searchTerm.toLowerCase(); let url = baseUrl; @@ -195,7 +178,7 @@ const searchScraper = async (req, res) => { novelUrl = novelUrl[novelUrl.length - 2] + "/"; const novel = { - extensionId: 28, + sourceId: 28, novelName, novelCover, novelUrl, @@ -208,12 +191,14 @@ const searchScraper = async (req, res) => { novel.novelName.toLowerCase().includes(searchTerm) ); - res.json(novels); + return novels; }; -module.exports = vipNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const YuukiTlsScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default YuukiTlsScraper; diff --git a/src/es/yuukitls/YuukiTls.js b/src/es/yuukitls/YuukiTls.js deleted file mode 100644 index c70745b..0000000 --- a/src/es/yuukitls/YuukiTls.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const YuukiTlsScraper = require("./YuukiTlsScraper"); - -const router = express.Router(); - -router.get("/novels/", YuukiTlsScraper.novelsScraper); - -router.get("/novel/:novelUrl", YuukiTlsScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", YuukiTlsScraper.chapterScraper); - -router.get("/search/", YuukiTlsScraper.searchScraper); - -module.exports = router; diff --git a/src/es/yuukitls/icon.png b/src/es/yuukitls/icon.png deleted file mode 100644 index 301be42..0000000 Binary files a/src/es/yuukitls/icon.png and /dev/null differ diff --git a/src/fr/noveldeglace/NovelDeGlaceScraper.js b/src/fr/noveldeglace.js similarity index 63% rename from src/fr/noveldeglace/NovelDeGlaceScraper.js rename to src/fr/noveldeglace.js index a025eb1..a733fb2 100644 --- a/src/fr/noveldeglace/NovelDeGlaceScraper.js +++ b/src/fr/noveldeglace.js @@ -1,14 +1,14 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { htmlToText } = require("html-to-text"); -const { parseHtml } = require("../../helper"); +import cheerio from "react-native-cheerio"; + +const sourceId = 77; + +const sourceName = "NovelDeGlace"; const baseUrl = "https://noveldeglace.com/"; -const sourceName = "Novel De Glace"; -const sourceId = 76; +const popularNovels = async (page) => { + const totalPages = 1; -const novelsScraper = async (req, res) => { let url = baseUrl + "roman"; const result = await fetch(url); @@ -33,11 +33,10 @@ const novelsScraper = async (req, res) => { novels.push(novel); }); - res.json(novels); + return { novels, totalPages }; }; -const novelScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; +const parseNovelAndChapters = async (novelUrl) => { const url = baseUrl + "roman/" + novelUrl; const result = await fetch(url); @@ -51,12 +50,15 @@ const novelScraper = async (req, res) => { "div.entry-content > div > strong" )[0].nextSibling.nodeValue.trim(); - novel.novelCover = $("#thumbnail > img").attr("src"); + novel.novelCover = $(".su-row > div > div > img").attr("src"); - novel.novelSummary = $("div[data-title=Synopsis]").text(); - novel.author = $( + novel.summary = $("div[data-title=Synopsis]").text(); + + author = $( "div.romans > div.project-large > div.su-row > div.su-column.su-column-size-3-4 > div > div:nth-child(3) > strong" - )[0].nextSibling.nodeValue.trim(); + )[0]; + + novel.author = author ? author.nextSibling.nodeValue.trim() : null; novel.genre = $(".genre") .text() @@ -68,7 +70,7 @@ const novelScraper = async (req, res) => { $(".chpt").each(function () { const chapterName = $(this).find("a").text().trim(); const releaseDate = null; - const chapterUrl = $(this).find("a").attr("href").split("/")[4]; + const chapterUrl = $(this).find("a").attr("href"); const novel = { sourceId, @@ -80,16 +82,13 @@ const novelScraper = async (req, res) => { novelChapters.push(novel); }); - novel.novelChapters = novelChapters; + novel.chapters = novelChapters; - res.json(novel); + return novel; }; -const chapterScraper = async (req, res) => { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - const url = `${baseUrl}chapitre/${chapterUrl}`; +const parseChapter = async (novelUrl, chapterUrl) => { + const url = chapterUrl; const result = await fetch(url); const body = await result.text(); @@ -97,16 +96,7 @@ const chapterScraper = async (req, res) => { $ = cheerio.load(body); let chapterName = $("h2.western").text(); - let chapterText = $(".chapter-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - - let prevChapter = null; - - novelUrl = novelUrl + "/"; - chapterUrl = chapterUrl + "/"; const chapter = { sourceId, @@ -114,16 +104,12 @@ const chapterScraper = async (req, res) => { chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; }; -const searchScraper = async (req, res) => { - const searchTerm = req.query.s; - +const searchNovels = async (searchTerm) => { let url = baseUrl + "roman"; const result = await fetch(url); @@ -149,15 +135,17 @@ const searchScraper = async (req, res) => { }); novels = novels.filter((novel) => - novel.novelName.toLowerCase().includes(searchTerm) + novel.novelName.toLowerCase().includes(searchTerm.toLowerCase()) ); - res.json(novels); + return novels; }; -module.exports = NovelDeGlaceScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const NovelDeGlaceScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default NovelDeGlaceScraper; diff --git a/src/fr/noveldeglace/NovelDeGlace.js b/src/fr/noveldeglace/NovelDeGlace.js deleted file mode 100644 index de5a2f3..0000000 --- a/src/fr/noveldeglace/NovelDeGlace.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const NovelDeGlaceScraper = require("./NovelDeGlaceScraper"); - -const router = express.Router(); - -router.get("/novels/", NovelDeGlaceScraper.novelsScraper); - -router.get("/novel/:novelUrl", NovelDeGlaceScraper.novelScraper); - -router.get("/novel/:novelUrl/:chapterUrl", NovelDeGlaceScraper.chapterScraper); - -router.get("/search/", NovelDeGlaceScraper.searchScraper); - -module.exports = router; diff --git a/src/fr/noveldeglace/icon.png b/src/fr/noveldeglace/icon.png deleted file mode 100644 index e69241a..0000000 Binary files a/src/fr/noveldeglace/icon.png and /dev/null differ diff --git a/src/helper.js b/src/helper.js deleted file mode 100644 index ae4e2e7..0000000 --- a/src/helper.js +++ /dev/null @@ -1,35 +0,0 @@ -const chromium = require("chrome-aws-lambda"); -const { htmlToText } = require("html-to-text"); -const puppeteer = require("puppeteer"); -const UserAgent = require("user-agents"); - -const userAgent = new UserAgent(); - -const scraper = async (url) => { - const browser = await puppeteer.launch({ - args: [ - ...chromium.args, - "--hide-scrollbars", - "--disable-web-security", - "--user-agent=" + userAgent + "", - ], - defaultViewport: chromium.defaultViewport, - executablePath: await chromium.executablePath, - headless: true, - ignoreHTTPSErrors: true, - }); - - const page = await browser.newPage(); - await page.goto(url); - const body = await page.content(); - - return body; -}; - -const parseHtml = (html) => { - let text = htmlToText(html); - text = text.replace(/\n{2,}/g, "\n\n"); - return text; -}; - -module.exports = { scraper, parseHtml }; diff --git a/src/helpers/cleanHtml.js b/src/helpers/cleanHtml.js new file mode 100644 index 0000000..84735e0 --- /dev/null +++ b/src/helpers/cleanHtml.js @@ -0,0 +1,36 @@ +export const cleanHtml = (html) => { + let text = html; + + if (!text) { + return "Chapter is empty.\n\nReport if it's available in webview."; + } + + text = text + /** + * Remove inline styles + */ + .replace( + /style="((?:font-family|line-height|font-size|text-align).*?)"/gi, + "" + ) + .replace(/class=".*?"/gi, "") + .replace(/id=".*?"/gi, "") + + /** + * Remove comments + */ + .replace(//gim, "") + .replace(/<\s*script[^>]*>[\s\S]*?<\/script>/gim, "") + .replace(/<\s*noscript[^>]*>[\s\S]*?<\/noscript>/gim, "") + .replace(/<\s*form[^>]*>[\s\S]*?<\/form>/gim, "") + /** + * Remove relative links + */ + .replace( + /<\s*a[^>]*href=['"](\/.*?)['"][^>]*>([\s\S]*?)<\/\s*a\s*>/gi, + "$2" + ) + .trim(); + + return text; +}; diff --git a/src/helpers/constants.js b/src/helpers/constants.js new file mode 100644 index 0000000..7397d40 --- /dev/null +++ b/src/helpers/constants.js @@ -0,0 +1,8 @@ +export const defaultCoverUri = + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; + +export const Status = { + ONGOING: "Ongoing", + COMPLETED: "Completed", + UNKNOWN: "Unknown", +}; diff --git a/src/helpers/htmlToText.js b/src/helpers/htmlToText.js new file mode 100644 index 0000000..ef1b56b --- /dev/null +++ b/src/helpers/htmlToText.js @@ -0,0 +1,263 @@ +const htmlToText = (html, options = {}) => { + const { removeLineBreaks = true } = options; + + if (!html) { + return "Chapter is empty.\n\nReport if it's available in webview."; + } + + html = html.trim(); + + text = removeLineBreaks && html.replace(/(?:\n|\r\n|\r)/gi, ""); + + text = html + /** + *
-> \n + */ + + .replace(/<\s*br[^>]*>/gi, "\n") + + /** + *
-> [hr]() + */ + + .replace(/<\s*hr[^>]*>/gi, "") + + /** + *
  • -> \n + */ + .replace(/<\s*\/li[^>]*>/gi, "\n") + + /** + *

    -> \n\n + */ + + .replace(/<\s*p[^>]*>/gi, "\n\n") + + /** + * Remove script and noscript tags + */ + .replace(/<\s*script[^>]*>[\s\S]*?<\/script>/gim, "") + .replace(/<\s*noscript[^>]*>[\s\S]*?<\/noscript>/gim, "") + + /** + * Remove style tags + */ + + .replace(/<\s*style[^>]*>[\s\S]*?<\/style>/gim, "") + + /** + * Remove comments + */ + + .replace(//gim, "") + + /** + * Text -> [Text](link) + */ + + .replace( + /<\s*a[^>]*href=['"](http.*?)['"][^>]*>([\s\S]*?)<\/\s*a\s*>/gi, + "$2" + ) + // .replace( + // /<\s*a[^>]*href=['"](http.*?)['"][^>]*>([\s\S]*?)<\/\s*a\s*>/gi, + // "[$2]($1)" + // ) + /** + * -> [img](src) + */ + + // .replace(/<\s*img[^>]*src=['"](.*?)['"][^>]*>/gi, "[img]($1)") + + /** + * Remove remaining tags + */ + + .replace(/(<([^>]+)>)/gi, "") + + /** + * Remove tabs. + */ + + .replace(/\t/g, "") + + /** + * Remove newlines at the beginning of the text. + */ + + .replace(/^\n+/m, "") + + /** + * Replace multiple spaces with a single space. + */ + + .replace(/ {2,}/g, " ") + + /** + * Make sure there are never more than two consecutive linebreaks. + */ + + .replace(/\n{2,}/g, "\n\n") + + /** + * Decode HTML entities. + */ + + .replace(/&([^;]+);/g, decodeHtmlEntity) + + .trim(); + + return text; +}; + +const decodeHtmlEntity = (m, n) => { + let code; + + if (n.substr(0, 1) == "#") { + if (n.substr(1, 1) == "x") { + code = parseInt(n.substr(2), 16); + } else { + code = parseInt(n.substr(1), 10); + } + } else { + code = ENTITIES_MAP[n]; + } + + return code === undefined || code === NaN + ? "&" + n + ";" + : String.fromCharCode(code); +}; + +let ENTITIES_MAP = { + nbsp: 160, + iexcl: 161, + cent: 162, + pound: 163, + curren: 164, + yen: 165, + brvbar: 166, + sect: 167, + uml: 168, + copy: 169, + ordf: 170, + laquo: 171, + not: 172, + shy: 173, + reg: 174, + macr: 175, + deg: 176, + plusmn: 177, + sup2: 178, + sup3: 179, + acute: 180, + micro: 181, + para: 182, + middot: 183, + cedil: 184, + sup1: 185, + ordm: 186, + raquo: 187, + frac14: 188, + frac12: 189, + frac34: 190, + iquest: 191, + Agrave: 192, + Aacute: 193, + Acirc: 194, + Atilde: 195, + Auml: 196, + Aring: 197, + AElig: 198, + Ccedil: 199, + Egrave: 200, + Eacute: 201, + Ecirc: 202, + Euml: 203, + Igrave: 204, + Iacute: 205, + Icirc: 206, + Iuml: 207, + ETH: 208, + Ntilde: 209, + Ograve: 210, + Oacute: 211, + Ocirc: 212, + Otilde: 213, + Ouml: 214, + times: 215, + Oslash: 216, + Ugrave: 217, + Uacute: 218, + Ucirc: 219, + Uuml: 220, + Yacute: 221, + THORN: 222, + szlig: 223, + agrave: 224, + aacute: 225, + acirc: 226, + atilde: 227, + auml: 228, + aring: 229, + aelig: 230, + ccedil: 231, + egrave: 232, + eacute: 233, + ecirc: 234, + euml: 235, + igrave: 236, + iacute: 237, + icirc: 238, + iuml: 239, + eth: 240, + ntilde: 241, + ograve: 242, + oacute: 243, + ocirc: 244, + otilde: 245, + ouml: 246, + divide: 247, + oslash: 248, + ugrave: 249, + uacute: 250, + ucirc: 251, + uuml: 252, + yacute: 253, + thorn: 254, + yuml: 255, + quot: 34, + amp: 38, + lt: 60, + gt: 62, + OElig: 338, + oelig: 339, + Scaron: 352, + scaron: 353, + Yuml: 376, + circ: 710, + tilde: 732, + ensp: 8194, + emsp: 8195, + thinsp: 8201, + zwnj: 8204, + zwj: 8205, + lrm: 8206, + rlm: 8207, + ndash: 8211, + mdash: 8212, + lsquo: 8216, + rsquo: 8217, + sbquo: 8218, + ldquo: 8220, + rdquo: 8221, + bdquo: 8222, + dagger: 8224, + Dagger: 8225, + permil: 8240, + lsaquo: 8249, + rsaquo: 8250, + euro: 8364, + apos: 39, +}; + +export { htmlToText }; diff --git a/src/helpers/parseDate.js b/src/helpers/parseDate.js new file mode 100644 index 0000000..b8bf19a --- /dev/null +++ b/src/helpers/parseDate.js @@ -0,0 +1,28 @@ +import moment from "moment"; + +export const parseMadaraDate = (date) => { + let releaseDate = date; + + if (date.includes("ago")) { + let timeAgo = date; + releaseDate = new Date(); + + timeAgo = timeAgo.match(/\d+/)[0]; + + if (timeAgo.includes("hours ago") || timeAgo.includes("hour ago")) { + releaseDate.setHours(releaseDate.getHours() - timeAgo); + } + + if (timeAgo.includes("days ago") || timeAgo.includes("day ago")) { + releaseDate.setDate(releaseDate.getDate() - timeAgo); + } + + if (timeAgo.includes("months ago") || timeAgo.includes("month ago")) { + releaseDate.setMonth(releaseDate.getMonth() - timeAgo); + } + + releaseDate = moment(releaseDate).format("MMMM DD, YYYY"); + } + + return releaseDate; +}; diff --git a/src/jp/syosetu/SyosetuScrapper.js b/src/jp/syosetu.js similarity index 63% rename from src/jp/syosetu/SyosetuScrapper.js rename to src/jp/syosetu.js index 5d5aef9..f2b5fc1 100644 --- a/src/jp/syosetu/SyosetuScrapper.js +++ b/src/jp/syosetu.js @@ -1,6 +1,6 @@ -// scraper and cheerio for scrapping from site -const { scraper } = require("../../helper"); -const cheerio = require("cheerio"); +import cheerio from "react-native-cheerio"; + +import moment from "moment"; const baseUrl = "https://syosetu.com"; // base url for syosetu.com @@ -25,10 +25,11 @@ const getChapterUrl = (id, chn) => `https://ncode.syosetu.com/${id}${chn === "oneshot" ? "" : `/${chn}`}`; // Because syosetu does not have any covers, I did a replacement for them -const novelCover = "https://raw.githubusercontent.com/skillgg/lnreader-sources/main/src/jp/syosetu/not_found.png"; +const novelCover = + "https://raw.githubusercontent.com/skillgg/lnreader-sources/main/src/jp/syosetu/not_found.png"; // ID of this extension -const extensionId = 36; +const sourceId = 36; // Name of this extension const sourceName = "Syosetu"; @@ -38,20 +39,18 @@ const sourceName = "Syosetu"; // TOOD: in-app dynamic loading on scroll const maxPageLoad = 3; -const novelsScraper = async (req, res) => { +const popularNovels = async (page) => { + const totalPages = 100; // array of all the novels let novels = []; // returns list of novels from given page let getNovelsFromPage = async (pagenumber) => { // load page - console.log(searchUrl(pagenumber)); - const body = await scraper(searchUrl(pagenumber || null)); - console.log("Loaded"); + const result = await fetch(searchUrl(pagenumber || null)); + const body = await result.text(); // Cheerio it! const cheerioQuery = cheerio.load(body, { decodeEntities: false }); - console.log("Parsed"); - let pageNovels = []; // find class=searchkekka_box cheerioQuery(".searchkekka_box").each(function (i, e) { @@ -63,58 +62,49 @@ const novelsScraper = async (req, res) => { pageNovels.push({ novelName: novelDIV.text(), // get the name novelUrl: getLastPartOfUrl(novelA.attribs.href), // get last part of the link - extensionId, + sourceId, novelCover, // TODO: IDK what to do about covers... On Syo they don't have them }); }); - console.log("Read"); // return all novels from this page return pageNovels; }; - // counter of loaded pages - let pagesLoaded = 0; - do { - // always load first one - novels.push(...(await getNovelsFromPage(pagesLoaded + 1))); - pagesLoaded++; - console.log("Added"); - } while (pagesLoaded < maxPageLoad); // check if we should load more - + novels = await getNovelsFromPage(page); /** Use * novels.push(...(await getNovelsFromPage(pageNumber))) * if you want to load more */ // respond with novels! - res.json(novels); - console.log("Finished!"); + return { totalPages, novels }; }; -const novelScraper = async (req, res) => { - const novelUrl = getNovelUrl(req.params.novelUrl); +const parseNovelAndChapters = async (novelUrl) => { + const url = getNovelUrl(novelUrl); let chapters = []; - const body = await scraper(novelUrl); + const result = await fetch(url); + const body = await result.text(); const cheerioQuery = cheerio.load(body, { decodeEntities: false }); // create novel object let novel = { - extensionId, + sourceId, sourceName, - sourceUrl: novelUrl, - novelUrl: req.params.novelUrl, + url: url, + novelUrl, novelName: cheerioQuery(".novel_title").text(), + author: cheerioQuery(".novel_writername").text().replace("作者:", ""), novelCover, }; // Get all the chapters const cqGetChapters = cheerioQuery(".novel_sublist2"); - console.log(novelUrl, cqGetChapters.length); if (cqGetChapters.length !== 0) { // has more than 1 chapter - novel.novelSummary = cheerioQuery("#novel_ex") + novel.summary = cheerioQuery("#novel_ex") .text() .replace(/<\s*br.*?>/g, "\n"); cqGetChapters.each(function (i, e) { @@ -138,53 +128,51 @@ const novelScraper = async (req, res) => { * but every oneshot is set as "there are no chapters" and all contents are thrown into the description!! */ // get summary for oneshot chapter - novel.novelSummary = cheerio // because there is no summary anywhere on the novel page, we have to take it from search page for a one-shot manga - .load(await scraper(searchUrl() + `&word=${novel.novelName}`), { - decodeEntities: false, - })(".searchkekka_box") // find the manga in search.php + + const result = await fetch(searchUrl() + `&word=${novel.novelName}`); + const body = await result.text(); + const summaryQuery = cheerio.load(body, { decodeEntities: false }); + const foundText = summaryQuery(".searchkekka_box") .first() .find(".ex") .text() - .replace(/\s{2,}/g, "\n"); // get the description as text + .replace(/\s{2,}/g, "\n"); + novel.summary = foundText; + // add single chapter chapters.push({ - chapterName: novel.novelName, + chapterName: "Oneshot", releaseDate: cheerioQuery("head") .find("meta[name='WWWC']") .attr("content"), // get date from metadata chapterUrl: "oneshot", // set chapterUrl to oneshot so that chapterScraper knows it's a one-shot }); } + novel.chapters = chapters; - novel.novelChapters = chapters; - - res.json(novel); - console.log("Finished!"); + return novel; }; -let chapterScraper = async (req, res) => { - const novelUrl = req.params.novelUrl; - const chapterUrl = req.params.chapterUrl; - +let parseChapter = async (novelUrl, chapterUrl) => { const url = getChapterUrl(novelUrl, chapterUrl); // get Url + const result = await fetch(url); + const body = await result.text(); + // create cheerioQuery - const cheerioQuery = cheerio.load(await scraper(url), { + const cheerioQuery = cheerio.load(body, { decodeEntities: false, }); + let chapterText = cheerioQuery("#novel_honbun") // get chapter text + .html(); // create chapter data structure let chapter = { - extensionId: 1, + sourceId: 36, novelUrl, chapterUrl, chapterName: "", - chapterText: cheerioQuery("#novel_honbun") // get chapter text - .text() - .replace(/<\s*br.*?>/g, "") - .replace(/\n{2,}/g, "\n"), // remove double breaklines - nextChapter: null, - prevChapter: null, + chapterText, }; if (chapterUrl === "oneshot") @@ -200,26 +188,13 @@ let chapterScraper = async (req, res) => { const chapterButtons = cheerioQuery( "#novel_contents .novel_bn" ).first(); - if (chapterButtons.length === 1) { - const button = chapterButtons.find("a"); - if (button.text().match(/次/)) - chapter.nextChapter = getLastPartOfUrl(button.attr("href")); - else chapter.prevChapter = getLastPartOfUrl(button.attr("href")); - } else { - const firstButton = chapterButtons.find("a").first(); - const lastButton = chapterButtons.find("a").last(); - chapter.prevChapter = getLastPartOfUrl(firstButton.attribs.href); - chapter.nextChapter = getLastPartOfUrl(lastButton.attribs.href); - } } - res.json(chapter); - console.log("Finished!"); + return chapter; }; -let searchScraper = async (req, res) => { - const searchTerm = req.query.s; - const orderBy = req.query.o; +let searchNovels = async (searchTerm) => { + // const orderBy = req.query.o; // array of all the novels let novels = []; @@ -229,10 +204,10 @@ let searchScraper = async (req, res) => { // returns list of novels from given page let getNovelsFromPage = async (pagenumber) => { // load page - const body = await scraper( - searchUrl(pagenumber || null, req.query.o || null) + - `&word=${searchTerm}` + const result = await fetch( + searchUrl(pagenumber || null, null) + `&word=${searchTerm}` ); + const body = await result.text(); // Cheerio it! const cheerioQuery = cheerio.load(body, { decodeEntities: false }); @@ -249,7 +224,7 @@ let searchScraper = async (req, res) => { pageNovels.push({ novelName: novelDIV.text(), // get the name novelUrl: getLastPartOfUrl(novelA.attribs.href), // get last part of the link - extensionId, + sourceId, novelCover, // TODO: IDK what to do about covers... On Syo they don't have them }); }); @@ -258,12 +233,14 @@ let searchScraper = async (req, res) => { }; // counter of loaded pages - let pagesLoaded = 0; - do { - // always load first one - novels.push(...(await getNovelsFromPage(pagesLoaded + 1))); - pagesLoaded++; - } while (pagesLoaded < maxPageLoad && isNext); // check if we should load more + // let pagesLoaded = 0; + // do { + // // always load first one + // novels.push(...(await getNovelsFromPage(pagesLoaded + 1))); + // pagesLoaded++; + // } while (pagesLoaded < maxPageLoad && isNext); // check if we should load more + + novels = await getNovelsFromPage(1); /** Use * novels.push(...(await getNovelsFromPage(pageNumber))) @@ -271,13 +248,14 @@ let searchScraper = async (req, res) => { */ // respond with novels! - res.json(novels); - console.log("Finished!"); + return novels; }; -module.exports = mtlNovelScraper = { - novelsScraper, - novelScraper, - chapterScraper, - searchScraper, +const SyosetuScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, }; + +export default SyosetuScraper; diff --git a/src/jp/syosetu/Syosetu.js b/src/jp/syosetu/Syosetu.js deleted file mode 100644 index b970217..0000000 --- a/src/jp/syosetu/Syosetu.js +++ /dev/null @@ -1,11 +0,0 @@ -const express = require("express"); -const syosetuNovelScrapper = require("./SyosetuScrapper"); - -const router = express.Router(); - -router.get("/novels/", syosetuNovelScrapper.novelsScraper); -router.get("/novel/:novelUrl", syosetuNovelScrapper.novelScraper); -router.get("/novel/:novelUrl/:chapterUrl", syosetuNovelScrapper.chapterScraper); -router.get("/search/", syosetuNovelScrapper.searchScraper); - -module.exports = router; diff --git a/src/jp/syosetu/icon.png b/src/jp/syosetu/icon.png deleted file mode 100644 index a516abf..0000000 Binary files a/src/jp/syosetu/icon.png and /dev/null differ diff --git a/src/jp/syosetu/not_found.png b/src/jp/syosetu/not_found.png deleted file mode 100644 index fd89a4a..0000000 Binary files a/src/jp/syosetu/not_found.png and /dev/null differ diff --git a/src/multisrc/madara/MadaraGenerator.js b/src/multisrc/madara/MadaraGenerator.js new file mode 100644 index 0000000..0aa7825 --- /dev/null +++ b/src/multisrc/madara/MadaraGenerator.js @@ -0,0 +1,188 @@ +import MadaraScraper from "./MadaraScraper"; + +const getPath = (extensionId) => { + const path = { + 38: { novels: "manga", novel: "manga", chapter: "manga" }, + 39: { novels: "series", novel: "series", chapter: "series" }, + 40: { novels: "novels", novel: "novel", chapter: "novel" }, + 41: { novels: "manga", novel: "manga", chapter: "manga" }, + 42: { novels: "all-novels", novel: "manga", chapter: "manga" }, + 43: { novels: "series", novel: "series", chapter: "series" }, + 44: { novels: "series", novel: "series", chapter: "series" }, + 45: { novels: "novel", novel: "novel", chapter: "novel" }, + 46: { novels: "novel", novel: "novel", chapter: "novel" }, + 47: { novels: "novel-list", novel: "novel", chapter: "novel" }, + 55: { novels: "manga", novel: "manga", chapter: "manga" }, + 56: { novels: "novel", novel: "novel", chapter: "novel" }, + 57: { novels: "series", novel: "series", chapter: "series" }, + 58: { novels: "novel", novel: "novel", chapter: "novel" }, + 59: { novels: "novel", novel: "novels", chapter: "novel" }, + 60: { novels: "novel", novel: "novel", chapter: "novel" }, + 61: { novels: "novel", novel: "novel", chapter: "novel" }, + 62: { novels: "novel", novel: "novel", chapter: "novel" }, + 63: { novels: "novel", novel: "novel", chapter: "novel" }, + 75: { novels: "series", novel: "series", chapter: "series" }, + }; + + return ( + path[extensionId] ?? { + novels: "novel", + novel: "novel", + chapter: "novel", + } + ); +}; + +export const SkyNovelScraper = new MadaraScraper( + 38, + "https://skynovel.org/", + "SkyNovel", + getPath(38) +); + +export const NovelCakeScraper = new MadaraScraper( + 39, + "https://novelcake.com/", + "NovelCake", + getPath(39) +); + +export const NovelsRockScraper = new MadaraScraper( + 40, + "https://novelsrock.com/", + "NovelsRock", + getPath(40) +); + +export const ZinnNovelScraper = new MadaraScraper( + 41, + "https://zinnovel.com/", + "ZinnNovel", + getPath(41) +); + +export const NovelTranslateScraper = new MadaraScraper( + 42, + "https://noveltranslate.com/", + "NovelTranslate", + getPath(42) +); + +export const LunarLettersScraper = new MadaraScraper( + 43, + "https://www.lunarletters.com/", + "LunarLetters", + getPath(43) +); + +export const SleepyTranslationsScraper = new MadaraScraper( + 44, + "https://sleepytranslations.com/", + "SleepyTranslations", + getPath(44) +); + +export const FreeNovelScraper = new MadaraScraper( + 45, + "https://freenovel.me/", + "FreeNovelMe", + getPath(45) +); + +export const FirstKissNovelScraper = new MadaraScraper( + 46, + "https://1stkissnovel.love/", + "1stKissNovel", + getPath(46) +); + +export const DaoNovelScraper = new MadaraScraper( + 47, + "https://daonovel.com/", + "DaoNovel", + getPath(47) +); + +export const MostNovelScraper = new MadaraScraper( + 55, + "https://mostnovel.com/", + "MostNovel", + getPath(55) +); + +export const NovelMultiverseScraper = new MadaraScraper( + 56, + "https://www.novelmultiverse.com/", + "NovelMultiverse", + getPath(56) +); + +export const LightNovelHeavenScraper = new MadaraScraper( + 57, + "https://lightnovelheaven.com/", + "LightNovelHeaven", + getPath(57) +); + +export const LightNovelsHubScraper = new MadaraScraper( + 58, + "https://lightnovelshub.com/", + "LightNovelsHub", + getPath(58) +); + +export const MeionNovelScraper = new MadaraScraper( + 60, + "https://meionovel.id/", + "MeionNovel", + getPath(60) +); + +export const WebNovelLoverScraper = new MadaraScraper( + 61, + "https://www.webnovelover.com/", + "WebNovelLover", + getPath(61) +); + +export const BoxNovelOnlineScraper = new MadaraScraper( + 63, + "https://boxnovel.online/", + "BoxNovel.online", + getPath(63) +); + +export const ClickNovelScraper = new MadaraScraper( + 64, + "https://clicknovel.net/", + "ClickNovel", + getPath(64) +); + +export const ReadWebNovelsScraper = new MadaraScraper( + 65, + "https://readwebnovels.net/", + "ReadWebNovels", + getPath(65) +); + +export const WBNovelScraper = new MadaraScraper( + 66, + "https://wbnovel.com/", + "WBNovel", + getPath(66) +); + +export const WuxiaWorldDotSiteScraper = new MadaraScraper( + 74, + "https://wuxiaworld.site/", + "WuxiaWorld.Site", + getPath(74) +); + +export const MysticalSeriesScraper = new MadaraScraper( + 75, + "https://mysticalmerries.com/", + "MysticalSeries", + getPath(75) +); diff --git a/multisrc/madara/MadaraScraper.js b/src/multisrc/madara/MadaraScraper.js similarity index 64% rename from multisrc/madara/MadaraScraper.js rename to src/multisrc/madara/MadaraScraper.js index 5bf8dac..6d9a67d 100644 --- a/multisrc/madara/MadaraScraper.js +++ b/src/multisrc/madara/MadaraScraper.js @@ -1,24 +1,25 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const FormData = require("form-data"); -const { parseHtml } = require("../../src/helper"); +import moment from "moment"; +import cheerio from "react-native-cheerio"; +import { Status } from "../../helpers/constants"; +import { parseMadaraDate } from "../../helpers/parseDate"; class MadaraScraper { - constructor(extensionId, baseUrl, sourceName, path) { - this.extensionId = extensionId; + constructor(sourceId, baseUrl, sourceName, path) { + this.sourceId = sourceId; this.baseUrl = baseUrl; this.sourceName = sourceName; this.path = path; - - this.novelsScraper = this.novelsScraper.bind(this); - this.novelScraper = this.novelScraper.bind(this); - this.chapterScraper = this.chapterScraper.bind(this); - this.searchScraper = this.searchScraper.bind(this); } - async novelsScraper(req, res) { - let url = this.baseUrl + this.path.novels + "/?m_orderby=rating"; - let extensionId = this.extensionId; + async popularNovels(page) { + const totalPages = 100; + let url = + this.baseUrl + + this.path.novels + + "/page/" + + page + + "/?m_orderby=rating"; + let sourceId = this.sourceId; const result = await fetch(url); const body = await result.text(); @@ -29,7 +30,7 @@ class MadaraScraper { $(".manga-title-badges").remove(); - $(".page-item-detail").each(function (result) { + $(".page-item-detail").each(function () { const novelName = $(this).find(".post-title").text().trim(); let image = $(this).find("img"); const novelCover = image.attr("data-src") || image.attr("src"); @@ -41,7 +42,7 @@ class MadaraScraper { .split("/")[4]; const novel = { - extensionId, + sourceId, novelName, novelCover, novelUrl, @@ -50,11 +51,10 @@ class MadaraScraper { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; } - async novelScraper(req, res) { - const novelUrl = req.params.novelUrl; + async parseNovelAndChapters(novelUrl) { const url = `${this.baseUrl}${this.path.novel}/${novelUrl}/`; const result = await fetch(url); @@ -64,11 +64,11 @@ class MadaraScraper { let novel = {}; - novel.extensionId = this.extensionId; + novel.sourceId = this.sourceId; novel.sourceName = this.sourceName; - novel.sourceUrl = url; + novel.url = url; novel.novelUrl = novelUrl; @@ -78,9 +78,10 @@ class MadaraScraper { novel.novelCover = $(".summary_image > a > img").attr("data-src") || - $(".summary_image > a > img").attr("src"); + $(".summary_image > a > img").attr("src") || + "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"; - $(".post-content_item").each(function (result) { + $(".post-content_item").each(function () { const detailName = $(this) .find(".summary-heading > h5") .text() @@ -89,18 +90,20 @@ class MadaraScraper { switch (detailName) { case "Genre(s)": - novel["Genre(s)"] = detail.replace(/[\t\n]/g, ","); + novel.genre = detail.replace(/[\t\n]/g, ","); break; case "Author(s)": - novel["Author(s)"] = detail; + novel.author = detail; break; case "Status": - novel.Status = detail; + novel.status = detail.includes("OnGoing") + ? Status.ONGOING + : Status.COMPLETED; break; } }); - novel.novelSummary = $("div.summary__content").text().trim(); + novel.summary = $("div.summary__content").text().trim(); let novelChapters = []; @@ -118,36 +121,45 @@ class MadaraScraper { }); const text = await data.text(); - $ = cheerio.load(text); + if (text !== "0") { + $ = cheerio.load(text); + } - $(".wp-manga-chapter").each(function (result) { + $(".wp-manga-chapter").each(function () { const chapterName = $(this) .find("a") .text() .replace(/[\t\n]/g, "") .trim(); - const releaseDate = $(this) - .find("span") + let releaseDate = null; + releaseDate = $(this) + .find("span.chapter-release-date") .text() - .replace("Free", "") .trim(); + if (releaseDate) { + releaseDate = parseMadaraDate(releaseDate); + } else { + /** + * Insert current date + */ + + releaseDate = moment().format("MMMM DD, YYYY"); + } + const chapterUrl = $(this).find("a").attr("href").split("/")[5]; novelChapters.push({ chapterName, releaseDate, chapterUrl }); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; } - async chapterScraper(req, res) { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - let extensionId = this.extensionId; + async parseChapter(novelUrl, chapterUrl) { + let sourceId = this.sourceId; const url = `${this.baseUrl}${this.path.chapter}/${novelUrl}/${chapterUrl}`; @@ -160,27 +172,19 @@ class MadaraScraper { $(".text-center").text() || $("#chapter-heading").text(); let chapterText = $(".text-left").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; const chapter = { - extensionId, + sourceId, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; } - async searchScraper(req, res) { - const searchTerm = req.query.s; - + async searchNovels(searchTerm) { const url = `${this.baseUrl}?s=${searchTerm}&post_type=wp-manga`; const result = await fetch(url); @@ -189,9 +193,9 @@ class MadaraScraper { const $ = cheerio.load(body); let novels = []; - let extensionId = this.extensionId; + let sourceId = this.sourceId; - $(".c-tabs-item__content").each(function (result) { + $(".c-tabs-item__content").each(function () { const novelName = $(this).find(".post-title").text().trim(); let image = $(this).find("img"); @@ -204,7 +208,7 @@ class MadaraScraper { .split("/")[4]; const novel = { - extensionId, + sourceId, novelName, novelCover, novelUrl, @@ -213,8 +217,8 @@ class MadaraScraper { novels.push(novel); }); - res.json(novels); + return novels; } } -module.exports = MadaraScraper; +export default MadaraScraper; diff --git a/src/multisrc/wpmangastream/WPMangaStreamGenerator.js b/src/multisrc/wpmangastream/WPMangaStreamGenerator.js new file mode 100644 index 0000000..8139632 --- /dev/null +++ b/src/multisrc/wpmangastream/WPMangaStreamGenerator.js @@ -0,0 +1,13 @@ +import WPMangaStreamScraper from "./WPMangaStreamScraper"; + +export const KolNovelScraper = new WPMangaStreamScraper( + 53, + "https://kolnovel.com/", + "KolNovel" +); + +export const RewayatArScraper = new WPMangaStreamScraper( + 54, + "https://rewayat-ar.site/", + "RewayatAr" +); diff --git a/multisrc/wpmangastream/WPMangaStreamScraper.js b/src/multisrc/wpmangastream/WPMangaStreamScraper.js similarity index 57% rename from multisrc/wpmangastream/WPMangaStreamScraper.js rename to src/multisrc/wpmangastream/WPMangaStreamScraper.js index 96baf61..7a67b55 100644 --- a/multisrc/wpmangastream/WPMangaStreamScraper.js +++ b/src/multisrc/wpmangastream/WPMangaStreamScraper.js @@ -1,22 +1,17 @@ -const cheerio = require("cheerio"); -const fetch = require("node-fetch"); -const { parseHtml } = require("../../src/helper"); +import cheerio from "react-native-cheerio"; -class MadaraScraper { - constructor(extensionId, baseUrl, sourceName) { - this.extensionId = extensionId; +class WPMangaStreamScraper { + constructor(sourceId, baseUrl, sourceName) { + this.sourceId = sourceId; this.baseUrl = baseUrl; this.sourceName = sourceName; - - this.novelsScraper = this.novelsScraper.bind(this); - this.novelScraper = this.novelScraper.bind(this); - this.chapterScraper = this.chapterScraper.bind(this); - this.searchScraper = this.searchScraper.bind(this); } - async novelsScraper(req, res) { - let url = this.baseUrl + "series/?m_orderby=popular"; - let extensionId = this.extensionId; + async popularNovels(page) { + let totalPages = 100; + let url = + this.baseUrl + "series/?page=" + page + "&status=&order=popular"; + let sourceId = this.sourceId; const result = await fetch(url); const body = await result.text(); @@ -25,7 +20,7 @@ class MadaraScraper { let novels = []; - $("article.bs").each(function (result) { + $("article.bs").each(function () { const novelName = $(this).find(".ntitle").text().trim(); let image = $(this).find("img"); const novelCover = image.attr("src"); @@ -33,7 +28,7 @@ class MadaraScraper { let novelUrl = $(this).find("a").attr("href").split("/")[4]; const novel = { - extensionId, + sourceId, novelName, novelCover, novelUrl, @@ -42,11 +37,10 @@ class MadaraScraper { novels.push(novel); }); - res.json(novels); + return { totalPages, novels }; } - async novelScraper(req, res) { - const novelUrl = req.params.novelUrl; + async parseNovelAndChapters(novelUrl) { const url = `${this.baseUrl}series/${novelUrl}/`; const result = await fetch(url); @@ -56,42 +50,42 @@ class MadaraScraper { let novel = {}; - novel.extensionId = this.extensionId; + novel.sourceId = this.sourceId; novel.sourceName = this.sourceName; - novel.sourceUrl = url; + novel.url = url; novel.novelUrl = novelUrl; novel.novelName = $(".entry-title").text(); - novel.novelCover = $("img.thumb").attr("src"); + novel.novelCover = $("img.wp-post-image").attr("src"); - $("div.spe > span").each(function (result) { + $("div.spe > span").each(function () { const detailName = $(this).find("b").text().trim(); const detail = $(this).find("b").next().text().trim(); if ($(this).text().includes("الحالة:")) { - novel.Status = $(this).text().replace("الحالة: ", ""); + novel.status = $(this).text().replace("الحالة: ", ""); } switch (detailName) { case "المؤلف:": - novel["Author(s)"] = detail; + novel.auhtor = detail; break; } }); - novel["Genre(s)"] = $(".genxed").text().replace(/\s/g, ","); + novel.genre = $(".genxed").text().replace(/\s/g, ","); - novel.novelSummary = $('div[itemprop="description"]').text().trim(); + novel.summary = $('div[itemprop="description"]').text().trim(); let novelChapters = []; $(".eplister") .find("li") - .each(function (result) { + .each(function () { const chapterName = $(this).find(".epl-num").text() + " - " + @@ -104,16 +98,13 @@ class MadaraScraper { novelChapters.push({ chapterName, releaseDate, chapterUrl }); }); - novel.novelChapters = novelChapters.reverse(); + novel.chapters = novelChapters.reverse(); - res.json(novel); + return novel; } - async chapterScraper(req, res) { - let novelUrl = req.params.novelUrl; - let chapterUrl = req.params.chapterUrl; - - let extensionId = this.extensionId; + async parseChapter(novelUrl, chapterUrl) { + let sourceId = this.sourceId; const url = `${this.baseUrl}${chapterUrl}`; @@ -123,29 +114,20 @@ class MadaraScraper { const $ = cheerio.load(body); let chapterName = $(".entry-title").text(); - - let chapterText = $(".entry-content").html(); - chapterText = parseHtml(chapterText); - - let nextChapter = null; - let prevChapter = null; + let chapterText = $("div.epcontent").html(); const chapter = { - extensionId, + sourceId, novelUrl, chapterUrl, chapterName, chapterText, - nextChapter, - prevChapter, }; - res.json(chapter); + return chapter; } - async searchScraper(req, res) { - const searchTerm = req.query.s; - + async searchNovels(searchTerm) { const url = `${this.baseUrl}?s=${searchTerm}`; const result = await fetch(url); @@ -154,9 +136,9 @@ class MadaraScraper { const $ = cheerio.load(body); let novels = []; - let extensionId = this.extensionId; + let sourceId = this.sourceId; - $("article.bs").each(function (result) { + $("article.bs").each(function () { const novelName = $(this).find(".ntitle").text().trim(); let image = $(this).find("img"); const novelCover = image.attr("src"); @@ -164,7 +146,7 @@ class MadaraScraper { let novelUrl = $(this).find("a").attr("href").split("/")[4]; const novel = { - extensionId, + sourceId, novelName, novelCover, novelUrl, @@ -173,8 +155,8 @@ class MadaraScraper { novels.push(novel); }); - res.json(novels); + return novels; } } -module.exports = MadaraScraper; +module.exports = WPMangaStreamScraper; diff --git a/src/ru/ranobehub.js b/src/ru/ranobehub.js new file mode 100644 index 0000000..da98731 --- /dev/null +++ b/src/ru/ranobehub.js @@ -0,0 +1,152 @@ +import cheerio from "react-native-cheerio"; +import { Status } from "../helpers/constants"; + +const sourceId = 69; + +const sourceName = "RanobeHub"; + +const baseUrl = "https://ranobehub.org/"; + +const popularNovels = async (page) => { + let url = + baseUrl + "api/search?page=" + page + "&sort=computed_rating&take=40"; + const totalPages = 69; + + const result = await fetch(url); + const body = await result.json(); + + let novels = []; + + body.resource.forEach((novel) => { + const novelName = novel.names.rus; + const novelCover = novel.poster.medium; + const novelUrl = novel.url.split("/").pop(); + + novels.push({ + sourceId, + novelName, + novelCover, + novelUrl, + }); + }); + + return { novels, totalPages }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = baseUrl + "ranobe/" + novelUrl; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url, novelUrl }; + + novel.novelName = $("h1").text(); + + novel.novelCover = $("img.__posterbox ").attr("data-src"); + + novel.summary = $(".book-description__text").text().trim(); + + novel.genre = ""; + + novel.status = Status.UNKNOWN; + + novel.author = $(".book-author").text().trim(); + + let novelChapters = []; + + const novelId = novelUrl.split("-")[0]; + const fetchChaptersUrl = `https://ranobehub.org/api/ranobe/${novelId}/contents`; + + const chaptersRaw = await fetch(fetchChaptersUrl); + const chaptersJSON = await chaptersRaw.json(); + + chaptersJSON.volumes.map((volume) => + volume.chapters.map((chapter) => + novelChapters.push({ + chapterName: chapter.name, + chapterUrl: chapter.url, + releaseDate: null, + }) + ) + ); + + novel.chapters = novelChapters; + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = chapterUrl; + const chapterId = chapterUrl.split("/")[4]; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = $( + "body > div.pusher.container_main > div:nth-child(5) > div:nth-child(1) > div.title-wrapper > h1" + ).text(); + let chapterText = $( + "body > div.pusher.container_main > div:nth-child(5) > div:nth-child(1)" + ).html(); + + if (chapterText) { + /** + * Convert relative url to absolute + */ + + chapterText = chapterText + .replace(/<\s*script[^>]*>[\s\S]*?<\/script>/gim, "") + .replace( + /data-src="(.*)"/g, + `src="https://ranobehub.org/img/ranobe/content/${chapterId}/$1.jpg"` + ); + } + + console.log(chapterText); + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = "https://ranobehub.org/api/fulltext/ranobe?query=" + searchTerm; + + const result = await fetch(url); + const data = await result.json(); + + let novels = []; + + data.data.map((novel) => + novels.push({ + sourceId, + novelName: novel.names.rus, + novelUrl: novel.url.match( + /https:\/\/ranobehub\.org\/ranobe\/(.*?)\?utm_source=search_name&utm_medium=search&utm_campaign=search_using/ + )[1], + novelCover: novel.image, + }) + ); + + return novels; +}; + +const RanobeHubScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default RanobeHubScraper; diff --git a/src/ru/ranobehub/icon.png b/src/ru/ranobehub/icon.png deleted file mode 100644 index 0591f7b..0000000 Binary files a/src/ru/ranobehub/icon.png and /dev/null differ diff --git a/src/sources.js b/src/sources.js new file mode 100644 index 0000000..1988c83 --- /dev/null +++ b/src/sources.js @@ -0,0 +1,165 @@ +import ComradeMaoScraper from "./en/comrademao"; +import ReadLightNovelScraper from "./en/readlightnovel"; +import BoxNovelScraper from "./en/boxnovel"; +import fastNovelScraper from "./en/fastnovel"; +import readNovelFullScraper from "./en/readnovelfull"; +import mtlNovelScraper from "./en/mtlnovel"; +import novelhallScraper from "./en/novelhall"; +import WuxiaWorldScraper from "./en/wuxiaworld"; +import novelFullScraper from "./en/novelfull"; +import novelTrenchScraper from "./en/noveltrench"; +import vipNovelScraper from "./en/vipnovel"; +import kissLightNovelScraper from "./en/kisslightnovels"; +import WuxiaWorldSiteScraper from "./en/wuxiaworldsite"; +import FreeWebNovelScraper from "./en/freewebnovel"; +import JPMTLScraper from "./en/jpmtl"; +import lightNovelPubScraper from "./en/lightnovelpub"; +import WuxiaWorldCoScraper from "./en/wuxiaworldco"; +import novelUpdatesCcScraper from "./en/novelupdatescc"; +import readLightNovelCcScraper from "./en/readlightnovelcc"; +import tapReadScraper from "./en/tapread"; +import WuxiaWorldCloudScraper from "./en/wuxiaworldcloud"; +import WoopReadScraper from "./en/woopread"; +import FoxaHolicScraper from "./en/foxaholic"; +import EinharjarProjectScraper from "./es/einherjarproject"; +import TuNovelaLigeraScraper from "./es/tunovelaligera"; +import SkyNovelsScraper from "./es/skynovels"; +import NovelasLigeraScraper from "./es/novelasligera"; +import YuukiTlsScraper from "./es/yuukitls"; +import NovelaWuxiaScraper from "./es/novelawuxia"; +import OasisTranslationsScraper from "./es/oasistranslations"; +import HasuTlScraper from "./es/hasutl"; +import NovelPassionScraper from "./en/novelpassion"; +import RoyalRoadScraper from "./en/royalroad"; +import ScribbleHubScraper from "./en/scribblehub"; +import SyosetuScraper from "./jp/syosetu"; +import LNMTLScraper from "./en/lnmtl"; +import { + BoxNovelOnlineScraper, + ClickNovelScraper, + DaoNovelScraper, + FirstKissNovelScraper, + FreeNovelScraper, + LightNovelHeavenScraper, + LightNovelsHubScraper, + LunarLettersScraper, + MeionNovelScraper, + MostNovelScraper, + MysticalSeriesScraper, + NovelCakeScraper, + NovelMultiverseScraper, + NovelsRockScraper, + NovelTranslateScraper, + ReadWebNovelsScraper, + SkyNovelScraper, + SleepyTranslationsScraper, + WBNovelScraper, + WebNovelLoverScraper, + WuxiaWorldDotSiteScraper, + ZinnNovelScraper, +} from "./multisrc/madara/MadaraGenerator"; +import WuxiaBlogScraper from "./en/wuxiablog"; +import WuxiaCityScraper from "./en/wuxiacity"; +import NovelUpdatesScraper from "./en/novelupdates"; +import RanobesScraper from "./en/ranobes"; +import YushuboScraper from "./ch/yushubo"; +import { + KolNovelScraper, + RewayatArScraper, +} from "./multisrc/wpmangastream/WPMangaStreamGenerator"; +import WLNUpdatesScraper from "./en/wlnupdates"; +import ReaperScansScraper from "./en/reaperscans"; +import ReadwnScraper from "./en/readwn"; +import RanobeHubScraper from "./ru/ranobehub"; +import ArNovelScraper from "./ar/arnovel"; +import DivineDaoLibraryScraper from "./en/divinedaolibrary"; +import NovelOnlineFullScraper from "./en/novelonlinefull"; +import LightNovelUpdatesScraper from "./en/lightnovelupdates"; +import EpikNovelScraper from "./tr/epiknovel"; +import TravisTranslationsScraper from "./en/travistranslations"; +import NovelDeGlaceScraper from "./fr/noveldeglace"; +import RainOfSnowScraper from "./en/rainofsnow"; + +export const getSource = (sourceId) => { + const scrapers = { + 1: BoxNovelScraper, + 2: ReadLightNovelScraper, + 3: fastNovelScraper, + 4: readNovelFullScraper, + 5: mtlNovelScraper, + 6: novelhallScraper, + 7: WuxiaWorldScraper, + 8: novelFullScraper, + 9: novelTrenchScraper, + 10: vipNovelScraper, + 11: kissLightNovelScraper, + 12: WuxiaWorldSiteScraper, + 13: FreeWebNovelScraper, + 14: JPMTLScraper, + 15: lightNovelPubScraper, + 16: WuxiaWorldCoScraper, + 17: tapReadScraper, + 18: novelUpdatesCcScraper, + 19: readLightNovelCcScraper, + 20: WuxiaWorldCloudScraper, + 21: WoopReadScraper, + 22: FoxaHolicScraper, + 23: TuNovelaLigeraScraper, + 24: SkyNovelsScraper, + 25: EinharjarProjectScraper, + 26: NovelasLigeraScraper, + 27: ComradeMaoScraper, + 28: YuukiTlsScraper, + 29: HasuTlScraper, + 30: OasisTranslationsScraper, + 31: NovelaWuxiaScraper, + 33: NovelPassionScraper, + 34: RoyalRoadScraper, + 35: ScribbleHubScraper, + 36: SyosetuScraper, + 37: LNMTLScraper, + 38: SkyNovelScraper, + 39: NovelCakeScraper, + 40: NovelsRockScraper, + 41: ZinnNovelScraper, + 42: NovelTranslateScraper, + 43: LunarLettersScraper, + 44: SleepyTranslationsScraper, + 45: FreeNovelScraper, + 46: FirstKissNovelScraper, + 47: DaoNovelScraper, + 48: WuxiaBlogScraper, + 49: WuxiaCityScraper, + 50: NovelUpdatesScraper, + 51: RanobesScraper, + 52: YushuboScraper, + 53: KolNovelScraper, + 54: RewayatArScraper, + 55: MostNovelScraper, + 56: NovelMultiverseScraper, + 57: LightNovelHeavenScraper, + 58: LightNovelsHubScraper, + 59: ArNovelScraper, + 60: MeionNovelScraper, + 61: WebNovelLoverScraper, + 62: WLNUpdatesScraper, + 63: BoxNovelOnlineScraper, + 64: ClickNovelScraper, + 65: ReadWebNovelsScraper, + 66: WBNovelScraper, + 67: ReaperScansScraper, + 68: ReadwnScraper, + 69: RanobeHubScraper, + 70: DivineDaoLibraryScraper, + 71: NovelOnlineFullScraper, + 72: LightNovelUpdatesScraper, + 73: EpikNovelScraper, + 74: WuxiaWorldDotSiteScraper, + 75: MysticalSeriesScraper, + 76: TravisTranslationsScraper, + 77: NovelDeGlaceScraper, + 78: RainOfSnowScraper, + }; + + return scrapers[sourceId]; +}; diff --git a/src/sources.json b/src/sources.json new file mode 100644 index 0000000..5fa85fa --- /dev/null +++ b/src/sources.json @@ -0,0 +1,541 @@ +[ + { + "sourceId": 1, + "url": "https://boxnovel.com/", + "sourceName": "BoxNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/boxnovel/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 2, + "sourceName": "ReadLightNovel", + "url": "https://www.readlightnovel.org", + "lang": "English", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readlightnovel/icon.png?raw=true" + }, + { + "sourceId": 3, + "url": "https://fastnovel.net/", + "sourceName": "FastNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/fastnovel/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 4, + "url": "https://readnovelfull.com/", + "sourceName": "ReadNovelFull", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readnovelfull/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 5, + "url": "https://www.mtlnovel.com/", + "sourceName": "MTLNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/mtlnovel/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 6, + "url": "https://www.novelhall.com/", + "sourceName": "NovelHall", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelhall/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 7, + "url": "https://www.wuxiaworld.com/", + "sourceName": "WuxiaWorld", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworld/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 8, + "url": "https://novelfull.com/", + "sourceName": "NovelFull", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelfull/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 9, + "url": "https://noveltrench.com/", + "sourceName": "NovelTrench", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/noveltrench/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 10, + "url": "https://vipnovel.com/", + "sourceName": "VipNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/vipnovel/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 11, + "url": "https://kisslightnovels.info/", + "sourceName": "KissLightNovels", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/kisslightnovels/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 12, + "url": "https://wuxiaworldsite.co/", + "sourceName": "WuxiaWorldSite", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldsite/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 13, + "url": "https://freewebnovel.com/", + "sourceName": "FreeWebNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/freewebnovel/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 14, + "url": "https://jpmtl.com/", + "sourceName": "JPMTL", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/jpmtl/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 15, + "url": "https://www.lightnovelpub.com/", + "sourceName": "LightNovelPub", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/lightnovelpub/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 16, + "url": "https://www.wuxiaworld.co/", + "sourceName": "WuxiaWorld.co", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldco/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 17, + "url": "http://www.tapread.com/", + "sourceName": "TapRead", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/tapread/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 18, + "url": "https://www.novelupdates.cc/", + "sourceName": "NovelUpdates.cc", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelupdatescc/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 19, + "url": "https://m.readlightnovel.cc/", + "sourceName": "ReadLightNovel.cc", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readlightnovelcc/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 20, + "url": "http://wuxiaworld.cloud/", + "sourceName": "WuxiaWorld.cloud", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldcloud/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 21, + "url": "https://woopread.com/", + "sourceName": "WoopRead", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/woopread/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 22, + "url": "https://foxaholic.com/", + "sourceName": "Foxaholic", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/foxaholic/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 23, + "url": "https://tunovelaligera.com/", + "sourceName": "TuNovelaLigera", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/tunovelaligera/icon.png?raw=true", + "lang": "Spanish" + }, + { + "sourceId": 24, + "url": "https://www.skynovels.net/", + "sourceName": "SkyNovels", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/skynovels/icon.png?raw=true", + "lang": "Spanish" + }, + { + "sourceId": 25, + "url": "https://einherjarproject.net/", + "sourceName": "EinherjarProject", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/einherjarproject/icon.png?raw=true", + "lang": "Spanish" + }, + { + "sourceId": 26, + "url": "https://novelasligera.com/", + "sourceName": "NovelasLigera", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/novelasligera/icon.png?raw=true", + "lang": "Spanish" + }, + { + "sourceId": 27, + "url": "https://comrademao.com/", + "sourceName": "ComradeMao", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/comrademao/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 28, + "url": "https://yuukitls.com/", + "sourceName": "YuukiTls", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/yuukitls/icon.png?raw=true", + "lang": "Spanish" + }, + { + "sourceId": 29, + "url": "https://hasutl.wordpress.com/", + "sourceName": "Hasu Translations", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/hasutl/icon.png?raw=true", + "lang": "Spanish" + }, + { + "sourceId": 30, + "url": "https://oasistranslations.wordpress.com/", + "sourceName": "Oasis Translations", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/oasistranslations/icon.png?raw=true", + "lang": "Spanish" + }, + { + "sourceId": 31, + "url": "http://www.novelawuxia.com/", + "sourceName": "Novela Wuxia", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/novelawuxia/icon.png?raw=true", + "lang": "Spanish" + }, + { + "sourceId": 33, + "url": "https://www.novelpassion.com/", + "sourceName": "NovelPassion", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelpassion/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 34, + "url": "https://www.royalroad.com/", + "sourceName": "Royal Road", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/royalroad/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 35, + "url": "https://www.scribblehub.com/", + "sourceName": "Scribble Hub", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/scribblehub/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 36, + "url": "https://syosetu.com", + "sourceName": "Syosetu", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/jp/syosetu/icon.png?raw=true", + "lang": "Japanese" + }, + { + "sourceId": 37, + "url": "https://lnmtl.com/", + "sourceName": "LNMTL", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/lnmtl/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 38, + "url": "https://skynovel.org/", + "sourceName": "SkyNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/skynovel.png?raw=true", + "lang": "English" + }, + { + "sourceId": 39, + "url": "https://novelcake.com/", + "sourceName": "NovelCake", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/novelcake.png?raw=true", + "lang": "English" + }, + { + "sourceId": 40, + "url": "https://novelsrock.com/", + "sourceName": "NovelsRock", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/novelsrock.png?raw=true", + "lang": "English" + }, + { + "sourceId": 41, + "url": "https://zinnovel.com/", + "sourceName": "ZinnNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/zinnovel.png?raw=true", + "lang": "English" + }, + { + "sourceId": 42, + "url": "https://noveltranslate.com/", + "sourceName": "NovelTranslate", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/noveltranslate.png?raw=true", + "lang": "English" + }, + { + "sourceId": 43, + "url": "https://www.lunarletters.com/", + "sourceName": "LunarLetters", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/lunarletters.png?raw=true", + "lang": "English" + }, + { + "sourceId": 44, + "url": "https://sleepytranslations.com/", + "sourceName": "SleepyTranslations", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/sleepytranslations.png?raw=true", + "lang": "English" + }, + { + "sourceId": 45, + "url": "https://freenovel.me/", + "sourceName": "FreeNovelMe", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/freenovel.png?raw=true", + "lang": "English" + }, + { + "sourceId": 46, + "url": "https://1stkissnovel.love/", + "sourceName": "1stKissNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/1stkissnovel.png?raw=true", + "lang": "English" + }, + { + "sourceId": 47, + "url": "https://daonovel.com/", + "sourceName": "DaoNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/daonovel.png?raw=true", + "lang": "English" + }, + { + "sourceId": 48, + "url": "https://www.wuxia.blog/", + "sourceName": "Wuxia.Blog", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiablog/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 49, + "url": "https://wuxia.city/", + "sourceName": "Wuxia.City", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiacity/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 50, + "url": "https://www.novelupdates.com/", + "sourceName": "Novel Updates", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelupdates/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 51, + "url": "https://ranobes.net/", + "sourceName": "Ranobes", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/ranobes/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 52, + "url": "https://m.yushubo.com", + "sourceName": "Yushubo", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/ch/yushubo/icon.png?raw=true", + "lang": "Chinese" + }, + { + "sourceId": 53, + "url": "https://kolnovel.com/", + "sourceName": "KolNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/wpmangastream/icons/kolnovel.png?raw=true", + "lang": "Arabic" + }, + { + "sourceId": 54, + "url": "https://rewayat-ar.site/", + "sourceName": "RewayatAr", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/wpmangastream/icons/rewayatar.png?raw=true", + "lang": "Arabic" + }, + { + "sourceId": 55, + "url": "https://mostnovel.com/", + "sourceName": "MostNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/mostnovel.png?raw=true", + "lang": "English" + }, + { + "sourceId": 56, + "url": "https://www.novelmultiverse.com/", + "sourceName": "NovelMultiverse", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/novelmultiverse.png?raw=true", + "lang": "English" + }, + { + "sourceId": 57, + "url": "https://lightnovelheaven.com/", + "sourceName": "LightNovelHeaven", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/lightnovelheaven.png?raw=true", + "lang": "English" + }, + { + "sourceId": 58, + "url": "https://lightnovelshub.com/", + "sourceName": "LightNovelsHub", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/lightnovelshub.png?raw=true", + "lang": "English" + }, + { + "sourceId": 59, + "url": "https://arnovel.me/", + "sourceName": "ArNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/arnovel.png?raw=true", + "lang": "Arabic" + }, + { + "sourceId": 60, + "url": "https://meionovel.id/", + "sourceName": "MeioNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/meionovel.png?raw=true", + "lang": "Indonesian" + }, + { + "sourceId": 61, + "url": "https://www.webnovelover.com/", + "sourceName": "WebNovelLover", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/webnovellover.png?raw=true", + "lang": "English" + }, + { + "sourceId": 62, + "url": "https://www.wlnupdates.com/", + "sourceName": "WLNUpdates", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wlnupdates/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 63, + "url": "https://boxnovel.online/", + "sourceName": "BoxNovel.online", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/boxnovel.online.png?raw=true", + "lang": "English" + }, + { + "sourceId": 64, + "url": "https://clicknovel.net/", + "sourceName": "ClickNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/clicknovel.png?raw=true", + "lang": "English" + }, + { + "sourceId": 65, + "url": "https://readwebnovels.net/", + "sourceName": "ReadWebNovels", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/readwebnovels.png?raw=true", + "lang": "English" + }, + { + "sourceId": 66, + "url": "https://wbnovel.com/", + "sourceName": "WBNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/wbnovel.png?raw=true", + "lang": "English" + }, + { + "sourceId": 67, + "url": "https://reaperscans.com/all-series/novels/", + "sourceName": "ReaperScans", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/reaperscans/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 68, + "url": "https://www.readwn.com/", + "sourceName": "Readwn.com", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readwn/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 69, + "url": "https://ranobehub.org/", + "sourceName": "Ranobehub", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/ru/ranobehub/icon.png?raw=true", + "lang": "Russian" + }, + { + "sourceId": 70, + "url": "https://divinedaolibrary.com/", + "sourceName": "Divine Dao Library", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/divinedaolibrary/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 71, + "url": "https://novelonlinefull.com/", + "sourceName": "NovelOnlineFull.com", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelonlinefull/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 72, + "url": "https://www.lightnovelupdates.com/", + "sourceName": "LightNovelUpdates", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/lightnovelupdates/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 73, + "url": "https://www.epiknovel.com/", + "sourceName": "EpikNovel", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/tr/epiknovel/icon.png?raw=true", + "lang": "Turkish" + }, + { + "sourceId": 74, + "url": "https://wuxiaworld.site/", + "sourceName": "WuxiaWorld.Site", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/wuxiaworldsite.png?raw=true", + "lang": "English" + }, + { + "sourceId": 75, + "url": "https://mysticalmerries.com/", + "sourceName": "MysticalSeries", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/mysticalseries.png?raw=true", + "lang": "English" + }, + { + "sourceId": 76, + "url": "https://travistranslations.com/", + "sourceName": "TravisTranslations", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/travistranslations/icon.png?raw=true", + "lang": "English" + }, + { + "sourceId": 77, + "url": "https://noveldeglace.com/", + "sourceName": "NovelDeGlace", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/fr/noveldeglace/icon.png?raw=true", + "lang": "French" + }, + { + "sourceId": 78, + "url": "https://rainofsnow.com/", + "sourceName": "RainOfSnow", + "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/rainofsnow/icon.png?raw=true", + "lang": "French" + } +] diff --git a/src/tr/epiknovel.js b/src/tr/epiknovel.js new file mode 100644 index 0000000..2c22126 --- /dev/null +++ b/src/tr/epiknovel.js @@ -0,0 +1,170 @@ +import cheerio from "react-native-cheerio"; + +const sourceId = 73; + +const sourceName = "EpikNovel"; + +const baseUrl = "https://www.epiknovel.com/"; + +const popularNovels = async (page) => { + let url = baseUrl + "seri-listesi?Sayfa=" + page; + const totalPages = 5; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $("div.col-lg-12.col-md-12").each(function () { + const novelName = $(this).find("h3").text(); + const novelCover = $(this).find("img").attr("data-src"); + const novelUrl = $(this).find("h3 > a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + console.log(novels); + + return { novels, totalPages }; +}; + +const parseNovelAndChapters = async (novelUrl) => { + const url = novelUrl; + console.log(url); + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novel = { sourceId, sourceName, url, novelUrl }; + + novel.novelName = $("h1#tables").text().trim(); + + novel.novelCover = $("img.manga-cover").attr("src"); + + novel.summary = $( + "#wrapper > div.row > div.col-md-9 > div:nth-child(6) > p:nth-child(3)" + ) + .text() + .trim(); + + novel.status = $( + "#wrapper > div.row > div.col-md-9 > div.row > div.col-md-9 > h4:nth-child(3) > a" + ) + .text() + .trim(); + + novel.author = $("#NovelInfo > p:nth-child(4)") + .text() + .replace(/Publisher:|\s/g, "") + .trim(); + + let novelChapters = []; + + $("table").find("tr").first().remove(); + + $("table") + .find("tr") + .each(function () { + const releaseDate = $(this).find("td:nth-child(3)").text(); + + let chapterName = $(this).find("td:nth-child(1) > a").text(); + + if ($(this).find("td:nth-child(1) > span").length > 0) { + chapterName = "🔒 " + chapterName; + } + + const chapterUrl = $(this) + .find(" td:nth-child(1) > a") + .attr("href"); + + novelChapters.push({ + chapterName, + chapterUrl, + releaseDate, + }); + }); + + novel.chapters = novelChapters; + console.log(novel); + + return novel; +}; + +const parseChapter = async (novelUrl, chapterUrl) => { + const url = chapterUrl; + + console.log(url); + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName, chapterText; + + if (result.url == "https://www.epiknovel.com/login") { + chapterName = ""; + chapterText = "Premium Chapter"; + } else { + chapterName = $("#icerik > center > h4 > b").text(); + chapterText = $("div#icerik").html(); + } + + const chapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm) => { + const url = baseUrl + "seri-listesi?q=" + searchTerm; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $("div.col-lg-12.col-md-12").each(function () { + const novelName = $(this).find("h3").text(); + const novelCover = $(this).find("img").attr("data-src"); + const novelUrl = $(this).find("h3 > a").attr("href"); + + const novel = { + sourceId, + novelName, + novelCover, + novelUrl, + }; + + novels.push(novel); + }); + + return novels; +}; + +const EpikNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default EpikNovelScraper; diff --git a/vercel.json b/vercel.json deleted file mode 100644 index df47e78..0000000 --- a/vercel.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": 2, - "builds": [ - { - "src": "./index.js", - "use": "@vercel/node" - } - ], - "routes": [ - { - "src": "/(.*)", - "dest": "/" - } - ] -}