diff --git a/README.md b/README.md index 86195ba..b05e604 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ Sources for [LNReader](https://github.com/rajarsheechatterjee/lnreader) | https://www.novelpassion.com/ | :heavy_check_mark: | | https://www.royalroad.com/ | :heavy_check_mark: | | https://www.scribblehub.com/ | :heavy_check_mark: | +| https://lnmtl.com/ | :heavy_check_mark: | +| https://syosetu.com | :heavy_check_mark: | | https://tunovelaligera.com/ | :heavy_check_mark: | | https://www.skynovels.net/ | :heavy_check_mark: | | https://einherjarproject.net/ | :heavy_check_mark: | diff --git a/index.js b/index.js index 6bb63d9..0c34833 100755 --- a/index.js +++ b/index.js @@ -45,6 +45,7 @@ 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")); // Spanish app.use("/api/23/", require("./src/es/tunovelaligera/Tunovelaligera")); diff --git a/sources.js b/sources.js index a307a20..33e5196 100755 --- a/sources.js +++ b/sources.js @@ -237,6 +237,15 @@ extensions = [ 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, + }, /** * Spanish @@ -322,9 +331,10 @@ extensions = [ sourceId: 36, sourceUrl: "https://syosetsu.com", sourceName: "Syosetsu", - sourceCover: "https://github.com/skillgg/lnreader-sources/blob/main/src/jp/syosetu/icon.png?raw=true", + sourceCover: + "https://github.com/skillgg/lnreader-sources/blob/main/src/jp/syosetu/icon.png?raw=true", sourceLanguage: "Japanese", - status: 0 + status: 1, }, ]; diff --git a/src/en/lnmtl/LNMTL.js b/src/en/lnmtl/LNMTL.js new file mode 100644 index 0000000..513db6a --- /dev/null +++ b/src/en/lnmtl/LNMTL.js @@ -0,0 +1,11 @@ +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/LNMTLScraper.js b/src/en/lnmtl/LNMTLScraper.js new file mode 100644 index 0000000..5c18c80 --- /dev/null +++ b/src/en/lnmtl/LNMTLScraper.js @@ -0,0 +1,193 @@ +const cheerio = require("cheerio"); +const fetch = require("node-fetch"); +const { parseHtml } = require("../../helper"); + +const baseUrl = "https://lnmtl.com/"; + +const novelsScraper = async (req, res) => { + let url = baseUrl + "novel?page=1"; // + page; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = []; + + $(".media").each(function (result) { + const novelName = $(this).find("h4").text(); + const novelCover = $(this).find("img").attr("src"); + + let novelUrl = $(this).find("h4 > a").attr("href"); + novelUrl = novelUrl.replace(baseUrl + "novel/", ""); + + const novel = { + extensionId: 37, + 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 = 37; + + novel.sourceName = "LNMTL"; + + novel.sourceUrl = url; + + novel.novelUrl = novelUrl; + + novel.novelName = $(".novel-name").text(); + + novel.novelCover = $("div.novel").find("img").attr("src"); + + novel.novelSummary = $("div.description").text().trim(); + + novel["Author(s)"] = $( + "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 = $( + "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)"] = $( + "main > div.container > div > div.col-lg-3.col-md-4 > div:nth-child(4) > div.panel-body > ul" + ) + .text() + .trim() + .replace(/\s\s/g, ","); + + let volumes = JSON.parse( + $("main") + .next() + .html() + .match(/lnmtl.volumes = \[(.*?)\]/)[0] + .replace("lnmtl.volumes = ", "") + ); + + let chapters = []; + + volumes = volumes.map((volume) => volume.id); + + for (const volume of volumes) { + let volumeData = await fetch( + `https://lnmtl.com/chapter?page=1&volumeId=${volume}` + ); + volumeData = await volumeData.json(); + + // volumeData = volumeData.data.map((volume) => volume.slug); + + for (let i = 1; i <= volumeData.last_page; i++) { + let chapterData = await fetch( + `https://lnmtl.com/chapter?page=${i}&volumeId=${volume}` + ); + chapterData = await chapterData.json(); + + chapterData = chapterData.data.map((chapter) => ({ + chapterName: `#${chapter.number} ${chapter.title}`, + chapterUrl: chapter.slug, + releaseDate: chapter.created_at, + })); + + chapters.push(...chapterData); + } + } + + novel.novelChapters = chapters; + + res.json(novel); +}; + +const chapterScraper = async (req, res) => { + let novelUrl = req.params.novelUrl; + let chapterUrl = req.params.chapterUrl; + + const url = `${baseUrl}chapter/${chapterUrl}`; + console.log(url); + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let chapterName = $("h3 > span.chapter-title").text().trim(); + + $(".original").remove(); + + let chapterText = $(".chapter-body ").html(); + chapterText = chapterName + "\n\n" + parseHtml(chapterText); + + let nextChapter = null; + let prevChapter = null; + + const chapter = { + extensionId: 37, + novelUrl, + chapterUrl, + chapterName, + chapterText, + nextChapter, + prevChapter, + }; + + res.json(chapter); +}; + +const searchScraper = async (req, res) => { + const searchTerm = req.query.s; + + const url = "https://lnmtl.com/term"; + + const result = await fetch(url); + const body = await result.text(); + + $ = cheerio.load(body); + + let novels = $("footer") + .next() + .next() + .html() + .match(/local: \[(.*?)\]/)[0] + .replace("local: ", ""); + + novels = JSON.parse(novels); + + novels = novels.filter((novel) => + novel.name.toLowerCase().includes(searchTerm) + ); + + novels = novels.map((novel) => ({ + extensionId: 37, + novelName: novel.name, + novelUrl: novel.slug, + novelCover: novel.image, + })); + + res.json(novels); +}; + +module.exports = lnmtlScraper = { + novelsScraper, + novelScraper, + chapterScraper, + searchScraper, +}; diff --git a/src/en/lnmtl/icon.png b/src/en/lnmtl/icon.png new file mode 100644 index 0000000..162b1b3 Binary files /dev/null and b/src/en/lnmtl/icon.png differ