diff --git a/package-lock.json b/package-lock.json index 585a765d0..7f97e3b64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5464,13 +5464,19 @@ "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, "node_modules/caniuse-lite": { - "version": "1.0.30001281", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001281.tgz", - "integrity": "sha512-1ktfDV2+4LYxOGeAZygMyvhiPBJKV1gsqPmfGGwRbwYOzDWbsQHgBFo+2lpRTMO2gWjPWJrCMLWa2fnSVnxn0A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "version": "1.0.30001452", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz", + "integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/capture-exit": { "version": "2.0.0", @@ -20037,9 +20043,9 @@ "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, "caniuse-lite": { - "version": "1.0.30001281", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001281.tgz", - "integrity": "sha512-1ktfDV2+4LYxOGeAZygMyvhiPBJKV1gsqPmfGGwRbwYOzDWbsQHgBFo+2lpRTMO2gWjPWJrCMLWa2fnSVnxn0A==" + "version": "1.0.30001452", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz", + "integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==" }, "capture-exit": { "version": "2.0.0", diff --git a/src/sources/en/LightNovelReader.ts b/src/sources/en/LightNovelReader.ts index b4911d38d..cf934b389 100644 --- a/src/sources/en/LightNovelReader.ts +++ b/src/sources/en/LightNovelReader.ts @@ -85,13 +85,13 @@ const parseNovelAndChapters = async (novelUrl: string) => { : undefined; novel.summary = loadedCheerio( - 'body > section:nth-child(4) > div > div > div.col-12.col-xl-9 > div > div:nth-child(5)', + 'div.container > div > div.col-12.col-xl-9 > div > div:nth-child(5) > div', ) .text() .trim(); novel.author = loadedCheerio( - 'body > section:nth-child(4) > div > div > div.col-12.col-xl-9 > div > div:nth-child(2) > div > div.novels-detail-right > ul > li:nth-child(6) > div.novels-detail-right-in-right', + 'div.novels-detail-right > ul > li:nth-child(6) > .novels-detail-right-in-right > a', ) .text() .trim(); @@ -104,7 +104,7 @@ const parseNovelAndChapters = async (novelUrl: string) => { .replace(/[\t\n ]+/g, ','); novel.status = loadedCheerio( - 'body > section:nth-child(4) > div > div > div.col-12.col-xl-9 > div > div:nth-child(2) > div > div.novels-detail-right > ul > li:nth-child(2) > div.novels-detail-right-in-right', + 'div.novels-detail-right > ul > li:nth-child(2) > .novels-detail-right-in-right', ) .text() .trim(); @@ -155,41 +155,35 @@ const parseChapter = async (novelUrl: string, chapterUrl: string) => { return chapter; }; -const searchNovels = async (searchTerm: string) => { - const url = `${baseUrl}/detailed-search-lnr`; - - const formData = new FormData(); - formData.append('keyword', searchTerm); +interface searchData { + overview: string; + original_title: string; + link: string; + image: string; +} - const result = await fetch(url, { method: 'POST', body: formData }); - const body = await result.text(); +const searchNovels = async (searchTerm: string) => { + const url = `${baseUrl}/search/autocomplete?dataType=json&query=${searchTerm}`; - const loadedCheerio = cheerio.load(body); + const result = await fetch(url, { method: 'POST' }); + const body = await result.json(); + const data: searchData[] = body.results; const novels: SourceNovelItem[] = []; - loadedCheerio('.category-items.cm-list li').each(function () { - let novelUrl = loadedCheerio(this).find('.category-name > a').attr('href'); - - if (novelUrl && !isUrlAbsolute(novelUrl)) { - novelUrl = baseUrl + novelUrl; - } - - if (novelUrl) { - const novelName = loadedCheerio(this).find('.category-name').text(); - let novelCover = loadedCheerio(this).find('img').attr('src'); - - if (novelCover && !isUrlAbsolute(novelCover)) { - novelCover = baseUrl + novelCover; - } - - novels.push({ - sourceId, - novelUrl, - novelName, - novelCover, - }); - } + data.forEach(item => { + let novelUrl = item.link; + let novelName = item.original_title + .replace(/“|”/g, '"') + .replace(/&#[0-9]*;/g, ' '); + let novelCover = item.image; + + novels.push({ + sourceId, + novelUrl, + novelName, + novelCover, + }); }); return novels; diff --git a/src/sources/en/NovelsCafe.ts b/src/sources/en/NovelsCafe.ts deleted file mode 100644 index 87b5419c4..000000000 --- a/src/sources/en/NovelsCafe.ts +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Template file for NovelsCafe - */ - -import * as cheerio from 'cheerio'; -import { isUrlAbsolute } from '../../utils/isAbsoluteUrl'; -import { - SourceChapter, - SourceChapterItem, - SourceNovel, - SourceNovelItem, -} from '../types'; - -const sourceId = 113; -const sourceName = 'NovelsCafe'; -const baseUrl = 'https://novelscafe.com'; - -const popularNovels = async (page: number) => { - const totalPages = 14; - const url = `${baseUrl}/completed-novel/page/${page}/`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - const novels: SourceNovelItem[] = []; - - loadedCheerio('.posts.row .post-column').each(function () { - let novelUrl = loadedCheerio(this).find('a').attr('href'); - - if (novelUrl && !isUrlAbsolute(novelUrl)) { - novelUrl = baseUrl + novelUrl; - } - - if (novelUrl) { - const novelName = loadedCheerio(this).find('.post-title').text().trim(); - let novelCover = loadedCheerio(this).find('img').attr('src'); - - if (novelCover && !isUrlAbsolute(novelCover)) { - novelCover = baseUrl + novelCover; - } - - const novel = { - sourceId, - novelUrl, - novelName, - novelCover, - }; - - novels.push(novel); - } - }); - - return { totalPages, novels }; -}; - -const parseNovelAndChapters = async (novelUrl: string) => { - const url = novelUrl; - - const result = await fetch(url); - const body = await result.text(); - - let loadedCheerio = cheerio.load(body); - - const novel: SourceNovel = { - sourceId, - sourceName, - url: novelUrl, - novelUrl, - chapters: [], - }; - - novel.novelName = loadedCheerio('h1').text(); - - let novelCover = loadedCheerio( - '#primary > div.row.mt-5 > div.info-wrapper.d-flex.col-12.col-md-9.pt-5.mb-5 > div.col-12.col-md-4.pb-5 > img', - ).attr('src'); - - novel.novelCover = novelCover - ? isUrlAbsolute(novelCover) - ? novelCover - : baseUrl + novelCover - : undefined; - - novel.summary = loadedCheerio('#description p').text().trim(); - - novel.author = loadedCheerio( - '#primary > div.row.mt-5 > div.info-wrapper.d-flex.col-12.col-md-9.pt-5.mb-5 > div.col-12.col-md-8.mt-1.pb-5 > div:nth-child(3) > div > h2 > a', - ) - .text() - .trim(); - - novel.genre = loadedCheerio( - '#primary > div.row.mt-5 > div.info-wrapper.d-flex.col-12.col-md-9.pt-5.mb-5 > div.col-12.col-md-8.mt-1.pb-5 > div:nth-child(6) h2', - ) - .text() - .trim() - .replace(/[\t\n ]+/g, ','); - - novel.status = loadedCheerio( - '#primary > div.row.mt-5 > div.info-wrapper.d-flex.col-12.col-md-9.pt-5.mb-5 > div.col-12.col-md-8.mt-1.pb-5 > div.counting-header > span:nth-child(4) > strong', - ) - .text() - .trim(); - - loadedCheerio('.row .text-truncate').each(function () { - let chapterUrl = loadedCheerio(this).find('a').attr('href'); - - if (chapterUrl && !isUrlAbsolute(chapterUrl)) { - chapterUrl = baseUrl + chapterUrl; - } - - if (chapterUrl) { - const chapterName = loadedCheerio(this) - .find('.chapter-title') - .text() - .trim(); - const releaseDate = null; - - const chapter: SourceChapterItem = { - chapterName, - releaseDate, - chapterUrl, - }; - - novel.chapters?.push(chapter); - } - }); - - return novel; -}; - -const parseChapter = async (novelUrl: string, chapterUrl: string) => { - const url = chapterUrl; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - const chapterName = loadedCheerio('h1').text(); - const chapterText = loadedCheerio('#chapter-content').html() || ''; - - const chapter: SourceChapter = { - sourceId, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -const searchNovels = async (searchTerm: string) => { - const url = `${baseUrl}/?s=${searchTerm}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - const novels: SourceNovelItem[] = []; - - loadedCheerio('.posts.row .post-column').each(function () { - const novelUrl = loadedCheerio(this).find('a').attr('href'); - if (novelUrl) { - const novelName = loadedCheerio(this).find('.post-title').text(); - let novelCover = loadedCheerio(this).find('img').attr('src'); - - if (novelCover && !isUrlAbsolute(novelCover)) { - novelCover = baseUrl + novelCover; - } - - novels.push({ - sourceId, - novelUrl, - novelName, - novelCover, - }); - } - }); - - return novels; -}; - -const NovelsCafeScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default NovelsCafeScraper; diff --git a/src/sources/en/fastnovel.js b/src/sources/en/fastnovel.js index 7d0f9695f..f129348a5 100644 --- a/src/sources/en/fastnovel.js +++ b/src/sources/en/fastnovel.js @@ -2,12 +2,12 @@ import { fetchHtml } from '@utils/fetch/fetch'; import * as cheerio from 'cheerio'; const sourceId = 3; -const baseUrl = 'https://fastnovel.net'; -const searchUrl = 'https://fastnovel.net/search/'; +const baseUrl = 'https://fastnovel.org'; +const searchUrl = 'https://fastnovel.org/search/'; const popularNovels = async page => { let totalPages = 39; - const url = `${baseUrl}/list/most-popular.html?page=${page}`; + const url = `${baseUrl}/sort/p/?page=${page}`; const body = await fetchHtml({ url, sourceId }); @@ -15,10 +15,12 @@ const popularNovels = async page => { let novels = []; - loadedCheerio('.film-item').each(function () { - const novelName = loadedCheerio(this).find('.name').text(); - const novelCover = loadedCheerio(this).find('.img').attr('data-original'); - const novelUrl = loadedCheerio(this).find('a').attr('href').substring(1); + loadedCheerio('.list-novel .row').each(function () { + const novelName = loadedCheerio(this).find('h3.novel-title > a').text(); + const novelCover = loadedCheerio(this).find('img.cover').attr('src'); + const novelUrl = loadedCheerio(this) + .find('h3.novel-title > a') + .attr('href'); const novel = { sourceId, @@ -34,11 +36,10 @@ const popularNovels = async page => { }; const parseNovelAndChapters = async novelUrl => { - const url = `${baseUrl}/${novelUrl}/`; - + const url = `${novelUrl}/`; const body = await fetchHtml({ url, sourceId }); - const loadedCheerio = cheerio.load(body); + let loadedCheerio = cheerio.load(body); let novel = { sourceId, @@ -47,71 +48,80 @@ const parseNovelAndChapters = async novelUrl => { novelUrl, }; - novel.novelName = loadedCheerio('h1').text(); + novel.novelName = loadedCheerio('div.book > img').attr('alt'); - novel.novelCover = loadedCheerio('.book-cover').attr('data-original'); + novel.novelCover = loadedCheerio('div.book > img').attr('src'); - novel.summary = loadedCheerio('div.film-content > p').text(); + novel.summary = loadedCheerio('div.desc-text').text().trim(); - novel.author = loadedCheerio('li > label') + novel.author = loadedCheerio('ul.info > li > h3') .filter(function () { return loadedCheerio(this).text().trim() === 'Author:'; }) - .next() + .siblings() .text(); - - novel.genre = loadedCheerio('li') + novel.genre = loadedCheerio('ul.info > li') .filter(function () { - return loadedCheerio(this).find('label').text().trim() === 'Genre:'; + return loadedCheerio(this).find('h3').text().trim() === 'Genre:'; }) .text() - .replace('Genre:', ''); + .trim() + .replace('Genre:', '') + .trim(); - novel.artist = null; - - novel.status = null; - - novel.status = loadedCheerio('li') + novel.status = loadedCheerio('ul.info > li > h3') .filter(function () { - return loadedCheerio(this).find('label').text().trim() === 'Status:'; + return loadedCheerio(this).text().trim() === 'Status:'; }) - .text() - .includes('Completed') - ? 'Completed' - : 'Ongoing'; - - let novelChapters = []; - - loadedCheerio('.chapter').each(function () { - const chapterName = loadedCheerio(this).text(); - const releaseDate = null; - let chapterUrl = loadedCheerio(this).attr('href'); - chapterUrl = chapterUrl.replace(`/${novelUrl}/`, ''); - - const chapter = { - chapterName, - releaseDate, - chapterUrl, - }; + .next() + .text(); - novelChapters.push(chapter); - }); + const novelId = loadedCheerio('#rating').attr('data-novel-id'); + + const getChapters = async id => { + const chapterListUrl = baseUrl + '/ajax/chapter-option?novelId=' + id; + + const data = await fetch(chapterListUrl); + const chapters = await data.text(); + + loadedCheerio = cheerio.load(chapters); + + let novelChapters = []; - novel.chapters = novelChapters; + loadedCheerio('select > option').each(function () { + let chapterName = loadedCheerio(this).text(); + let releaseDate = null; + let chapterUrl = loadedCheerio(this).attr('value'); + + if (chapterUrl) { + novelChapters.push({ + chapterName, + releaseDate, + chapterUrl, + }); + } + }); + + return novelChapters; + }; + + if (novelId) { + novel.chapters = await getChapters(novelId); + } return novel; }; const parseChapter = async (novelUrl, chapterUrl) => { - const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; + const url = `${chapterUrl}`; const body = await fetchHtml({ url, sourceId }); const loadedCheerio = cheerio.load(body); - const chapterName = loadedCheerio('.episode-name').text(); + const chapterName = loadedCheerio('.chr-text').text(); - let chapterText = loadedCheerio('#chapter-body').html(); + let chapterText = loadedCheerio('#chr-content').html(); const chapter = { sourceId, novelUrl, @@ -124,27 +134,30 @@ const parseChapter = async (novelUrl, chapterUrl) => { }; const searchNovels = async searchTerm => { - const url = `${searchUrl}${searchTerm}`; - - const body = await fetchHtml({ url, sourceId }); + const url = searchUrl + '?keyword=' + searchTerm; + const result = await fetch(url); + const body = await result.text(); const loadedCheerio = cheerio.load(body); let novels = []; - loadedCheerio('.film-item').each(function () { - const novelName = loadedCheerio(this).find('div.title > p.name').text(); - const novelCover = loadedCheerio(this).find('.img').attr('data-original'); - const novelUrl = loadedCheerio(this).find('a').attr('href').substring(1); - - const novel = { - sourceId, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); + loadedCheerio('div.col-novel-main > div.list-novel > .row').each(function () { + const novelUrl = loadedCheerio(this) + .find('h3.novel-title > a') + .attr('href'); + + const novelName = loadedCheerio(this).find('h3.novel-title > a').text(); + const novelCover = loadedCheerio(this).find('img').attr('src'); + + if (novelUrl) { + novels.push({ + sourceId, + novelUrl, + novelName, + novelCover, + }); + } }); return novels; diff --git a/src/sources/en/jpmtl.js b/src/sources/en/jpmtl.js deleted file mode 100644 index 58fffca9d..000000000 --- a/src/sources/en/jpmtl.js +++ /dev/null @@ -1,169 +0,0 @@ -import * as cheerio from 'cheerio'; -const baseUrl = 'https://jpmtl.com/'; - -const popularNovels = async page => { - let totalPages = 157; - let url = - '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(); - - let novels = []; - - body.map(item => { - const novelName = item.title; - const novelCover = item.cover; - const novelUrl = item.id + '/'; - - const novel = { sourceId: 14, novelName, novelCover, novelUrl }; - - novels.push(novel); - }); - - return { totalPages, novels }; -}; - -const parseNovelAndChapters = async novelUrl => { - const url = `${baseUrl}books/${novelUrl}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novel = {}; - - novel.sourceId = 14; - - novel.sourceName = 'JPMTL'; - - novel.url = url; - - novel.novelUrl = novelUrl; - - novel.novelName = loadedCheerio('h1.book-sidebar__title').text(); - - novel.novelCover = loadedCheerio('.book-sidebar').find('img').attr('src'); - - loadedCheerio('.post-content_item').each(function () { - const detailName = loadedCheerio(this) - .find('.summary-heading > h5') - .text() - .replace(/[\t\n]/g, '') - .trim(); - const detail = loadedCheerio(this) - .find('.summary-content') - .text() - .replace(/[\t\n]/g, '') - .trim(); - - novel[detailName] = detail; - }); - - novel.summary = loadedCheerio('.main-book__synopsis').text(); - - novel.genre = ''; - - loadedCheerio('a.main-book__category').each(function () { - novel.genre += loadedCheerio(this).text(); - }); - - function capitalizeFirstLetter(string) { - return string.charAt(0).toUpperCase() + string.slice(1); - } - - const info = loadedCheerio('.book-sidebar__author > .book-sidebar__info') - .text() - .trim() - .split('・'); - - if (info) { - novel.author = info[0]; - - novel.status = capitalizeFirstLetter(info[1]); - } - - let novelChapters = []; - - const chapterListUrl = `https://jpmtl.com/v2/chapter/${novelUrl}/list?state=published&structured=true&d`; - - const chapterResult = await fetch(chapterListUrl); - const volumes = await chapterResult.json(); - - volumes.map(volume => { - volume.chapters.map(chapter => { - const chapterName = chapter.title; - const releaseDate = chapter.created_at; - const chapterUrl = chapter.id; - - const obj = { - sourceId: 14, - chapterName, - releaseDate, - chapterUrl, - }; - - novelChapters.push(obj); - }); - }); - - novel.chapters = novelChapters; - - return novel; -}; - -const parseChapter = async (novelUrl, chapterUrl) => { - const url = `${baseUrl}books/${novelUrl}${chapterUrl}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - const chapterName = loadedCheerio('.chapter-content__title').text(); - let chapterText = loadedCheerio('.chapter-content__content').html(); - novelUrl += '/'; - - const chapter = { - sourceId: 14, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -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); - const body = await result.json(); - - const novels = []; - - body.map(item => { - const novelName = item.title; - const novelCover = item.cover; - const novelUrl = item.id + '/'; - - const novel = { sourceId: 14, novelName, novelCover, novelUrl }; - - novels.push(novel); - }); - - return novels; -}; - -const JPMTLScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default JPMTLScraper; diff --git a/src/sources/en/kisslightnovels.js b/src/sources/en/kisslightnovels.js deleted file mode 100644 index 8d286543f..000000000 --- a/src/sources/en/kisslightnovels.js +++ /dev/null @@ -1,182 +0,0 @@ -import * as cheerio from 'cheerio'; -const baseUrl = 'https://kisslightnovels.info/'; - -const popularNovels = async page => { - let totalPages = 26; - const url = `${baseUrl}light-novel/page/${page}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.page-item-detail').each(function () { - const novelName = loadedCheerio(this).find('h5 > a').text(); - const novelCover = loadedCheerio(this).find('img').attr('src'); - - let novelUrl = loadedCheerio(this).find('h5 > a').attr('href'); - novelUrl = novelUrl.replace(`${baseUrl}novel/`, ''); - - const novel = { - sourceId: 11, - 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(); - - const loadedCheerio = cheerio.load(body); - - let novel = {}; - - novel.sourceId = 11; - - novel.sourceName = 'KissLightNovels'; - - novel.url = url; - - novel.novelUrl = novelUrl; - - novel.novelName = loadedCheerio('.post-title > h1') - .text() - .replace(/[\t\n]/g, '') - .trim(); - - novel.novelCover = loadedCheerio('.summary_image > a > img').attr('src'); - - loadedCheerio('.post-content_item').each(function () { - const detailName = loadedCheerio(this) - .find('.summary-heading > h5') - .text() - .replace(/[\t\n]/g, '') - .trim(); - const detail = loadedCheerio(this) - .find('.summary-content') - .text() - .replace(/[\t\n]/g, '') - .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; - } - }); - - loadedCheerio('.description-summary > div.summary__content') - .find('em') - .remove(); - - novel.summary = loadedCheerio('.description-summary > div.summary__content') - .text() - .replace(/[\t\n]/g, ''); - - let novelChapters = []; - - loadedCheerio('.wp-manga-chapter').each(function () { - const chapterName = loadedCheerio(this) - .find('a') - .text() - .replace(/[\t\n]/g, '') - .trim(); - - const releaseDate = loadedCheerio(this) - .find('span') - .text() - .replace(/[\t\n]/g, '') - .trim(); - - const chapterUrl = loadedCheerio(this) - .find('a') - .attr('href') - .replace(url, ''); - - 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(); - - const loadedCheerio = cheerio.load(body); - - loadedCheerio('#snippet-box').remove(); - - const chapterName = loadedCheerio('div.text-left > h4').text(); - - let chapterText = loadedCheerio('.reading-content').html(); - const chapter = { - sourceId: 11, - 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(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.c-tabs-item__content').each(function () { - const novelName = loadedCheerio(this).find('h4 > a').text(); - const novelCover = loadedCheerio(this).find('img').attr('src'); - - let novelUrl = loadedCheerio(this).find('h4 > a').attr('href'); - novelUrl = novelUrl.replace(`${baseUrl}novel/`, ''); - - const novel = { - sourceId: 11, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return novels; -}; - -const kissLightNovelScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default kissLightNovelScraper; diff --git a/src/sources/en/lightnovelfull.ts b/src/sources/en/lightnovelfull.ts new file mode 100644 index 000000000..58a931687 --- /dev/null +++ b/src/sources/en/lightnovelfull.ts @@ -0,0 +1,185 @@ +import * as cheerio from 'cheerio'; +import { + SourceChapter, + SourceChapterItem, + SourceNovel, + SourceNovelItem, +} from '../types'; + +const sourceId = 98; +const sourceName = 'LightNovelFull'; +const baseUrl = 'https://lightnovelfull.com/'; + +const popularNovels = async (page: number) => { + let totalPages = 61; + const url = `${baseUrl}/most-popular?page=${page}`; + + const result = await fetch(url); + const body = await result.text(); + + const loadedCheerio = cheerio.load(body); + + let novels: SourceNovelItem[] = []; + + loadedCheerio('.list-truyen > .row').each(function () { + const novelName = loadedCheerio(this).find('h3.truyen-title > a').text(); + const novelCover = loadedCheerio(this).find('img.cover').attr('src'); + const novelUrl = + baseUrl + loadedCheerio(this).find('h3.truyen-title > a').attr('href'); + + const novel = { + sourceId, + novelUrl, + novelName, + novelCover, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl: string) => { + const url = novelUrl; + + const result = await fetch(url); + const body = await result.text(); + + let loadedCheerio = cheerio.load(body); + + let novel: SourceNovel = { + sourceId, + sourceName, + url: novelUrl, + novelUrl, + }; + + novel.novelName = loadedCheerio('div.book > img').attr('alt'); + + novel.novelCover = loadedCheerio('div.book > img').attr('src'); + + novel.summary = loadedCheerio('div.desc-text').text().trim(); + + novel.author = loadedCheerio('div.info > div > h3') + .filter(function () { + return loadedCheerio(this).text().trim() === 'Author:'; + }) + .siblings() + .text(); + + novel.genre = loadedCheerio('div.info > div') + .filter(function () { + return loadedCheerio(this).find('h3').text().trim() === 'Genre:'; + }) + .text() + .replace('Genre:', ''); + + novel.status = loadedCheerio('div.info > div > h3') + .filter(function () { + return loadedCheerio(this).text().trim() === 'Status:'; + }) + .next() + .text(); + + const novelId = novel.novelName; + //! Doesn't work since there are multiple pages and can't find source API + const getChapters = async (id: string) => { + const chapterListUrl = baseUrl + '/ajax/update-views?novelId=' + id; + + const data = await fetch(chapterListUrl); + const chapters = await data.text(); + + loadedCheerio = cheerio.load(chapters); + + let novelChapters: SourceChapterItem[] = []; + + loadedCheerio('select > option').each(function () { + let chapterName = loadedCheerio(this).text(); + let releaseDate = null; + let chapterUrl = loadedCheerio(this) + .attr('value') + ?.replace(`/${novelUrl}`, ''); + + if (chapterUrl) { + novelChapters.push({ + chapterName, + releaseDate, + chapterUrl, + }); + } + }); + + return novelChapters; + }; + console.log(novelId); + + if (novelId) { + novel.chapters = await getChapters(novelId); + } + + return novel; +}; + +const parseChapter = async (novelUrl: string, chapterUrl: string) => { + const url = baseUrl + chapterUrl; + + const result = await fetch(url); + const body = await result.text(); + + const loadedCheerio = cheerio.load(body); + + const chapterName = loadedCheerio('.chapter-title').attr('title'); + const chapterText = loadedCheerio('#chapter-content').html() || ''; + + const chapter: SourceChapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm: string) => { + const url = `${baseUrl}/search?keyword=${searchTerm}`; + + const result = await fetch(url); + const body = await result.text(); + + const loadedCheerio = cheerio.load(body); + + let novels: SourceNovelItem[] = []; + + loadedCheerio('div.col-truyen-main > div.list-truyen > .row').each( + function () { + const novelUrl = + baseUrl + loadedCheerio(this).find('h3.truyen-title > a').attr('href'); + + const novelName = loadedCheerio(this).find('h3.truyen-title > a').text(); + const novelCover = baseUrl + loadedCheerio(this).find('img').attr('src'); + + if (novelUrl) { + novels.push({ + sourceId, + novelUrl, + novelName, + novelCover, + }); + } + }, + ); + + return novels; +}; + +const LightNovelFullScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default LightNovelFullScraper; diff --git a/src/sources/en/lnmtl.js b/src/sources/en/lnmtl.js index 7251a4b0a..643ac20db 100644 --- a/src/sources/en/lnmtl.js +++ b/src/sources/en/lnmtl.js @@ -62,14 +62,15 @@ const parseNovelAndChapters = async novelUrl => { novel.summary = loadedCheerio('div.description').text().trim(); novel.author = loadedCheerio( - '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', + '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 > a > span', ).text(); novel.status = loadedCheerio( - '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', + 'main > div:nth-child(3) > div > div.col-lg-3.col-md-4 > div:nth-child(2) > div.panel-body > dl:last-child > dd', ) .text() .trim(); + console.log(novel.status); novel.genre = loadedCheerio( 'main > div.container > div > div.col-lg-3.col-md-4 > div:nth-child(4) > div.panel-body > ul', diff --git a/src/sources/en/mtlcorner.js b/src/sources/en/mtlcorner.js deleted file mode 100644 index 4a6790c89..000000000 --- a/src/sources/en/mtlcorner.js +++ /dev/null @@ -1,161 +0,0 @@ -import * as cheerio from 'cheerio'; -import { defaultCoverUri, Status } from '../helpers/constants'; - -const sourceId = 89; -const sourceName = 'IndoWebNovel'; - -const baseUrl = 'https://mtlcorner.com/'; - -const popularNovels = async page => { - const totalPages = 1; - const url = `${baseUrl}series/`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.wp-block-latest-posts__list li').each(function () { - const novelName = loadedCheerio(this).find('a').text(); - const novelCover = defaultCoverUri; - const novelUrl = loadedCheerio(this).find('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); - const body = await result.text(); - - let loadedCheerio = cheerio.load(body); - - let novel = { - sourceId, - sourceName, - url, - novelUrl, - novelName: '', - novelCover: '', - author: '', - status: '', - genre: '', - summary: '', - chapters: [], - }; - - const novelName = loadedCheerio('.entry-title').text().trim(); - novel.novelName = novelName; - - novel.novelCover = loadedCheerio('figure > img').attr('src'); - - novel.summary = loadedCheerio('figcaption').text().trim(); - - novel.status = Status.UNKNOWN; - - novel.author = loadedCheerio( - ' li.post-author.meta-wrapper > span.meta-text > a', - ) - .text() - ?.trim(); - - let chapters = []; - - loadedCheerio('.lcp_catlist li').each(function () { - let chapterNameSuffix = loadedCheerio(this) - .find('a') - .text() - ?.trim() - .match(/([a-zA-Z\s?]+)(\d+)-(\d+)/); - - if (chapterNameSuffix) { - let chaptersTemp = []; - - let chapterUrlSuffix = loadedCheerio(this).find('a').attr('href'); - - let i = chapterNameSuffix[2]; - - while (i <= chapterNameSuffix[3]) { - const chapterName = `${chapterNameSuffix[1].replace( - novelName, - '', - )} ${i}`; - const chapterUrl = `${chapterUrlSuffix}${i - chapterNameSuffix[2] + 1}`; - const releaseDate = null; - - chaptersTemp.push({ chapterName, releaseDate, chapterUrl }); - i++; - } - - chapters.push(...chaptersTemp.reverse()); - } - }); - - novel.chapters = chapters.reverse(); - - return novel; -}; - -const parseChapter = async (novelUrl, chapterUrl) => { - const url = chapterUrl; - - const result = await fetch(url); - const body = await result.text(); - - let loadedCheerio = cheerio.load(body); - - const chapterName = ''; - const chapterText = loadedCheerio('.entry-content').html(); - - const chapter = { - sourceId, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -const searchNovels = async searchTerm => { - const url = `${baseUrl}series/`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.wp-block-latest-posts__list li').each(function () { - const novelName = loadedCheerio(this).find('a').text(); - const novelCover = defaultCoverUri; - const novelUrl = loadedCheerio(this).find('a').attr('href'); - - const novel = { sourceId, novelName, novelCover, novelUrl }; - - if (novelName.toLowerCase().includes(searchTerm.toLowerCase())) { - novels.push(novel); - } - }); - - return novels; -}; - -const MTLCornerScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default MTLCornerScraper; diff --git a/src/sources/en/mylovenovel.js b/src/sources/en/mylovenovel.js index cfd9150e6..8fe1608be 100644 --- a/src/sources/en/mylovenovel.js +++ b/src/sources/en/mylovenovel.js @@ -8,7 +8,7 @@ const baseUrl = 'https://m.mylovenovel.com/'; const popularNovels = async page => { const totalPages = 909; - const url = `${baseUrl}search----${page}.html`; + const url = `${baseUrl}all-${page}.html`; const result = await fetch(url); const body = await result.text(); @@ -16,8 +16,8 @@ const popularNovels = async page => { let novels = []; - loadedCheerio('.list > li').each(function () { - const novelName = loadedCheerio(this).find('.bookname').text(); + loadedCheerio('#article_list_content > li').each(function () { + const novelName = loadedCheerio(this).find('h3').text(); const novelCover = loadedCheerio(this).find('img').attr('src'); const novelUrl = baseUrl.slice(0, -1) + loadedCheerio(this).find('a').attr('href'); @@ -37,7 +37,6 @@ const popularNovels = async page => { const parseNovelAndChapters = async novelUrl => { const url = novelUrl; - const result = await fetch(url); const body = await result.text(); @@ -53,18 +52,28 @@ const parseNovelAndChapters = async novelUrl => { novel.novelName = loadedCheerio('p.booktitle').text().trim(); - novel.novelCover = loadedCheerio('img.bookimg').attr('src'); + novel.novelCover = loadedCheerio('div.bookimg2 > img').attr('src'); novel.summary = loadedCheerio('meta[name="description"]') .attr('content') .trim(); - novel.author = loadedCheerio('#info > div.main > div.detail > p:nth-child(3)') + novel.author = loadedCheerio('.booknav2 > :nth-child(2)') .text() ?.replace('Author:', '') .trim(); - loadedCheerio('#morelist > ul > li').each(function () { + novel.status = loadedCheerio('.booknav2 > :nth-child(5)') + .text() + ?.replace('Status:', '') + .trim(); + + novel.genre = loadedCheerio('.booknav2 > :nth-child(4)') + .text() + ?.replace('Genre:', '') + .trim(); + + loadedCheerio('ul.chapterlist > li').each(function () { const chapterName = loadedCheerio(this).find('a').text().trim(); const releaseDate = null; const chapterUrl = @@ -84,8 +93,8 @@ const parseChapter = async (novelUrl, chapterUrl) => { let loadedCheerio = cheerio.load(body); - const chapterName = loadedCheerio('.headline').text(); - const chapterText = loadedCheerio('#content').html(); + const chapterName = loadedCheerio('h1.hide720').text(); + const chapterText = loadedCheerio('.content').html(); const chapter = { sourceId, @@ -108,8 +117,8 @@ const searchNovels = async searchTerm => { let novels = []; - loadedCheerio('.list > li').each(function () { - const novelName = loadedCheerio(this).find('.bookname').text(); + loadedCheerio('#article_list_content > li').each(function () { + const novelName = loadedCheerio(this).find('h3 > a').text(); const novelCover = loadedCheerio(this).find('img').attr('src'); const novelUrl = baseUrl.slice(0, -1) + loadedCheerio(this).find('a').attr('href'); diff --git a/src/sources/en/mtnovel.ts b/src/sources/en/novelfulldotnet.ts similarity index 96% rename from src/sources/en/mtnovel.ts rename to src/sources/en/novelfulldotnet.ts index 2a2355cf5..746253f83 100644 --- a/src/sources/en/mtnovel.ts +++ b/src/sources/en/novelfulldotnet.ts @@ -7,8 +7,8 @@ import { } from '../types'; const sourceId = 120; -const sourceName = 'MT Novel'; -const baseUrl = 'https://mtnovel.com'; +const sourceName = 'NovelFull.net'; +const baseUrl = 'https://novelfull.net'; const popularNovels = async (page: number) => { let totalPages = 22; @@ -175,11 +175,11 @@ const searchNovels = async (searchTerm: string) => { return novels; }; -const MTNovelScraper = { +const NovelFullScraper = { popularNovels, parseNovelAndChapters, parseChapter, searchNovels, }; -export default MTNovelScraper; +export default NovelFullScraper; diff --git a/src/sources/en/novelpassion.js b/src/sources/en/novelpassion.js deleted file mode 100644 index 6dfb03cbc..000000000 --- a/src/sources/en/novelpassion.js +++ /dev/null @@ -1,176 +0,0 @@ -import * as cheerio from 'cheerio'; -import dayjs from 'dayjs'; - -const baseUrl = 'https://www.novelpassion.com/'; - -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(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('div.j_bookList.lis-mn.ddc') - .find('li') - .each(function () { - const novelName = loadedCheerio(this).find('h3').text(); - const novelCover = - 'https://www.novelpassion.com' + - loadedCheerio(this).find('img').attr('src'); - - let novelUrl = loadedCheerio(this).find('div.pr > a').attr('href'); - novelUrl = novelUrl.replace('/novel/', ''); - - const novel = { - sourceId: 33, - 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(); - - const loadedCheerio = cheerio.load(body); - - let novel = {}; - - novel.sourceId = 33; - - novel.sourceName = 'NovelPassion'; - - novel.url = url; - - novel.novelUrl = novelUrl; - - const novelName = loadedCheerio('h2.mh').text(); - novel.novelName = novelName; - - novel.novelCover = baseUrl + loadedCheerio('i.g_thumb > img').attr('src'); - - let novelSummary = loadedCheerio('div.j_synopsis').text().trim(); - novel.summary = novelSummary; - - loadedCheerio('address.lh20.m14.pr').each(function () { - const detailName = loadedCheerio(this).find('p.ell.dib.vam').text().trim(); - - const detail = loadedCheerio(this).find('div.dns').text().trim(); - - switch (detailName) { - case 'Categories:': - novel.genre = detail.trim().replace(/ - {2}/g, ','); - break; - case 'Author:': - novel.author = detail.trim().replace(/ - {2}/g, ', '); - break; - } - - if (detailName.includes('Status:')) { - novel.status = detailName.replace('Status:', '').trim(); - } - }); - - let novelChapters = []; - - loadedCheerio('.content-list') - .find('li') - .each(function () { - const chapterName = loadedCheerio(this).find('span.sp1').text(); - - let releaseDate = loadedCheerio(this).find('i.sp2').text(); - releaseDate = dayjs(new Date(releaseDate)).format('L'); - - const chapterUrl = loadedCheerio(this) - .find('a') - .attr('href') - .replace('/novel/' + novelUrl + '/', ''); - - 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(); - - const loadedCheerio = cheerio.load(body); - - let chapterName = loadedCheerio('div.cha-content').find('strong').text(); - - let chapterText = loadedCheerio('div.cha-content').html(); - const chapter = { - sourceId: 33, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -const searchNovels = async searchTerm => { - const url = baseUrl + 'search?keyword=' + searchTerm; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('div.j_bookList.lis-mn.ddc') - .find('li') - .each(function () { - const novelName = loadedCheerio(this).find('h3').text(); - const novelCover = - 'https://www.novelpassion.com' + - loadedCheerio(this).find('img').attr('src'); - - let novelUrl = loadedCheerio(this).find('div.pr > a').attr('href'); - novelUrl = novelUrl.replace('/novel/', ''); - - const novel = { - sourceId: 33, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return novels; -}; - -const NovelPassionScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default NovelPassionScraper; diff --git a/src/sources/en/noveltrench.js b/src/sources/en/noveltrench.js deleted file mode 100644 index f1516a6f1..000000000 --- a/src/sources/en/noveltrench.js +++ /dev/null @@ -1,183 +0,0 @@ -import * as cheerio from 'cheerio'; -const baseUrl = 'https://novelhard.com/'; - -const popularNovels = async page => { - let totalPages = 270; - let url = baseUrl + 'novel/page/' + page; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.page-item-detail').each(function () { - const novelName = loadedCheerio(this).find('.h5 > a').text(); - const novelCover = loadedCheerio(this).find('img').attr('data-src'); - - let novelUrl = loadedCheerio(this).find('.h5 > a').attr('href'); - novelUrl = novelUrl.replace(`${baseUrl}novel/`, ''); - - const novel = { - sourceId: 9, - 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(); - - let loadedCheerio = cheerio.load(body); - - let novel = {}; - - novel.sourceId = 9; - - novel.sourceName = 'NovelTrench'; - - novel.url = url; - - novel.novelUrl = novelUrl; - - novel.novelName = loadedCheerio('.post-title > h1') - .text() - .replace(/[\t\n]/g, '') - .trim(); - - novel.novelCover = loadedCheerio('.summary_image > a > img').attr('data-src'); - - loadedCheerio('.post-content_item').each(function () { - const detailName = loadedCheerio(this) - .find('.summary-heading > h5') - .text() - .replace(/[\t\n]/g, '') - .trim(); - const detail = loadedCheerio(this) - .find('.summary-content') - .text() - .replace(/[\t\n]/g, '') - .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; - } - }); - - loadedCheerio('.description-summary > div.summary__content') - .find('em') - .remove(); - - novel.summary = loadedCheerio('.description-summary > div.summary__content') - .text() - .replace(/[\t\n]/g, ''); - - let novelChapters = []; - - const data = await fetch(`${url}ajax/chapters/`, { method: 'POST' }); - const text = await data.text(); - - loadedCheerio = cheerio.load(text); - - loadedCheerio('.wp-manga-chapter').each(function () { - const chapterName = loadedCheerio(this) - .find('a') - .text() - .replace(/[\t\n]/g, '') - .trim(); - - const releaseDate = loadedCheerio(this) - .find('span') - .text() - .replace(/[\t\n]/g, '') - .trim(); - - const chapterUrl = loadedCheerio(this) - .find('a') - .attr('href') - .replace(url, ''); - 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(); - - const loadedCheerio = cheerio.load(body); - - const chapterName = loadedCheerio('h1#chapter-heading').text(); - let chapterText = loadedCheerio('.reading-content').html(); - const chapter = { - sourceId: 9, - 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(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.c-tabs-item__content').each(function () { - const novelName = loadedCheerio(this).find('.h4 > a').text(); - const novelCover = loadedCheerio(this).find('img').attr('data-src'); - - let novelUrl = loadedCheerio(this).find('.h4 > a').attr('href'); - novelUrl = novelUrl.replace(`${baseUrl}novel/`, ''); - - const novel = { - sourceId: 9, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return novels; -}; - -const novelTrenchScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default novelTrenchScraper; diff --git a/src/sources/en/novelupdatescc.js b/src/sources/en/novelupdatescc.js deleted file mode 100644 index d350dcd88..000000000 --- a/src/sources/en/novelupdatescc.js +++ /dev/null @@ -1,141 +0,0 @@ -import * as cheerio from 'cheerio'; -const baseUrl = 'https://www.novelupdates.cc/'; - -const popularNovels = async page => { - let totalPages = 1; - const result = await fetch(baseUrl); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.section-item').each(function () { - const novelName = loadedCheerio(this).find('.book-name').text(); - const novelCover = loadedCheerio(this).find('img').attr('src'); - - let novelUrl = loadedCheerio(this).find('a').attr('href').slice(1); - - const novel = { - sourceId: 18, - 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(); - - const loadedCheerio = cheerio.load(body); - - let novel = {}; - - novel.sourceId = 18; - - novel.sourceName = 'NovelUpdates.cc'; - - novel.url = url; - - novel.novelUrl = novelUrl; - - novel.novelName = loadedCheerio('.book-name').text(); - - novel.novelCover = loadedCheerio('div.book-img > img').attr('src'); - - novel.genre = loadedCheerio('div.book-catalog > span.txt').text(); - - novel.status = loadedCheerio('div.book-state > span.txt').text(); - - novel.author = loadedCheerio('div.author > span.name').text(); - - const novelSummary = loadedCheerio('div.content > p.desc').text(); - novel.summary = novelSummary.trim(); - - let novelChapters = []; - - loadedCheerio('.chapter-item').each(function () { - const chapterName = loadedCheerio(this).find('.chapter-name').text(); - const releaseDate = null; - const chapterUrl = loadedCheerio(this).attr('href').slice(1); - - novelChapters.push({ - chapterName, - releaseDate, - chapterUrl, - }); - }); - - novel.chapters = novelChapters; - - return novel; -}; - -const parseChapter = async (novelUrl, chapterUrl) => { - const url = `${baseUrl}${chapterUrl}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - const chapterName = loadedCheerio('h1.chapter-title').text(); - let chapterText = loadedCheerio('div.chapter-entity').html(); - const chapter = { - sourceId: 18, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -const searchNovels = async searchTerm => { - const url = `${baseUrl}search/${searchTerm}/1`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.list-item').each(function () { - loadedCheerio(this).find('font').remove(); - - const novelName = loadedCheerio(this).find('.book-name').text(); - const novelCover = loadedCheerio(this).find('img').attr('src'); - - let novelUrl = loadedCheerio(this).find('a').attr('href').slice(1); - - const novel = { - sourceId: 18, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return novels; -}; - -const novelUpdatesCcScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default novelUpdatesCcScraper; diff --git a/src/sources/en/novelupdatescc.ts b/src/sources/en/novelupdatescc.ts new file mode 100644 index 000000000..93d6a8278 --- /dev/null +++ b/src/sources/en/novelupdatescc.ts @@ -0,0 +1,184 @@ +import * as cheerio from 'cheerio'; +import { + SourceChapter, + SourceChapterItem, + SourceNovel, + SourceNovelItem, +} from '../types'; + +const sourceId = 108; +const sourceName = 'NovelUpdatesCC'; +const baseUrl = 'https://www.novelupdates.cc'; + +const popularNovels = async (page: number) => { + let totalPages = 61; + const url = `${baseUrl}/hot-novel/${page}.html`; + + const result = await fetch(url); + const body = await result.text(); + + const loadedCheerio = cheerio.load(body); + + let novels: SourceNovelItem[] = []; + + loadedCheerio('.list-truyen .row').each(function () { + const novelName = loadedCheerio(this).find('h3.truyen-title > a').text(); + const novelCover = loadedCheerio(this).find('img.cover').attr('src'); + const novelUrl = + baseUrl + loadedCheerio(this).find('h3.truyen-title > a').attr('href'); + + const novel = { + sourceId, + novelUrl, + novelName, + novelCover, + }; + + novels.push(novel); + }); + + return { totalPages, novels }; +}; + +const parseNovelAndChapters = async (novelUrl: string) => { + const url = novelUrl; + + const result = await fetch(url); + const body = await result.text(); + + let loadedCheerio = cheerio.load(body); + + let novel: SourceNovel = { + sourceId, + sourceName, + url: novelUrl, + novelUrl, + }; + + novel.novelName = loadedCheerio('h3.title').text().trim(); + + novel.novelCover = loadedCheerio('img.cover').attr('src'); + + novel.summary = loadedCheerio('div.desc-text').text().trim(); + + novel.author = loadedCheerio('div.info > div > h3') + .filter(function () { + return loadedCheerio(this).text().trim() === 'Author:'; + }) + .siblings() + .text(); + + novel.genre = loadedCheerio('div.info > div') + .filter(function () { + return loadedCheerio(this).find('h3').text().trim() === 'Genre:'; + }) + .text() + .replace('Genre:', ''); + + novel.status = loadedCheerio('div.info > div > h3') + .filter(function () { + return loadedCheerio(this).text().trim() === 'Status:'; + }) + .next() + .text(); + + const novelId = loadedCheerio('#rating').attr('data-novel-id'); + + const getChapters = async (id: string) => { + const chapterListUrl = baseUrl + '/ajax/chapter-option?novelId=' + id; + + const data = await fetch(chapterListUrl); + const chapters = await data.text(); + + loadedCheerio = cheerio.load(chapters); + + let novelChapters: SourceChapterItem[] = []; + + loadedCheerio('select > option').each(function () { + let chapterName = loadedCheerio(this).text(); + let releaseDate = null; + let chapterUrl = loadedCheerio(this) + .attr('value') + ?.replace(`/${novelUrl}`, ''); + + if (chapterUrl) { + novelChapters.push({ + chapterName, + releaseDate, + chapterUrl, + }); + } + }); + + return novelChapters; + }; + + if (novelId) { + novel.chapters = await getChapters(novelId); + } + + return novel; +}; + +const parseChapter = async (novelUrl: string, chapterUrl: string) => { + const url = baseUrl + chapterUrl; + + const result = await fetch(url); + const body = await result.text(); + + const loadedCheerio = cheerio.load(body); + + const chapterName = loadedCheerio('.chapter-title').attr('title'); + const chapterText = loadedCheerio('#chapter-content').html() || ''; + + const chapter: SourceChapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName, + chapterText, + }; + + return chapter; +}; + +const searchNovels = async (searchTerm: string) => { + const url = `${baseUrl}/search?keyword=${searchTerm}`; + + const result = await fetch(url); + const body = await result.text(); + + const loadedCheerio = cheerio.load(body); + + let novels: SourceNovelItem[] = []; + + loadedCheerio('div.col-truyen-main > div.list-truyen > .row').each( + function () { + const novelUrl = + baseUrl + loadedCheerio(this).find('h3.truyen-title > a').attr('href'); + + const novelName = loadedCheerio(this).find('h3.truyen-title > a').text(); + const novelCover = baseUrl + loadedCheerio(this).find('img').attr('src'); + + if (novelUrl) { + novels.push({ + sourceId, + novelUrl, + novelName, + novelCover, + }); + } + }, + ); + + return novels; +}; + +const NovelUpdatesCCScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default NovelUpdatesCCScraper; diff --git a/src/sources/en/readfreenovel.ts b/src/sources/en/readfreenovel.ts deleted file mode 100644 index 760da69c1..000000000 --- a/src/sources/en/readfreenovel.ts +++ /dev/null @@ -1,210 +0,0 @@ -import * as cheerio from 'cheerio'; -import { SourceChapter, SourceChapterItem, SourceNovelItem } from '../types'; - -const sourceId = 109; -const sourceName = 'ReadFreeNovel'; -const baseUrl = 'https://www.readfreenovel.com'; -const searchUrl = 'https://www.readfreenovel.com/detailed-search'; - -const popularNovels = async (page: number) => { - let totalPages = 1534; - const url = `${baseUrl}/top-novel/${page}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - const novels: SourceNovelItem[] = []; - - loadedCheerio('.top-novel-block').each(function () { - const novelUrl = baseUrl + loadedCheerio(this).find('h2 > a').attr('href'); - - const novelName = loadedCheerio(this).find('h2 > a').text(); - const novelCover = baseUrl + loadedCheerio(this).find('img').attr('src'); - - const novel = { - sourceId, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return { totalPages, novels }; -}; - -const parseNovelAndChapters = async (novelUrl: string) => { - const url = novelUrl; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novel; - - const novelName = loadedCheerio('.block-title > h1').text(); - - const novelCover = - baseUrl + loadedCheerio('.novel-cover > a > img').attr('src'); - - let author, artist, genre, summary, status; - - loadedCheerio('.novel-detail-item').each(function () { - const detailName = loadedCheerio(this) - .find('.novel-detail-header > h6') - .text(); - const detail = loadedCheerio(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: SourceChapterItem[] = []; - - loadedCheerio('.panel').each(function () { - let volumeName = loadedCheerio(this).find('h4.panel-title').text(); - - loadedCheerio(this) - .find('ul.chapter-chs > li') - .each(function () { - const chapterUrl = baseUrl + loadedCheerio(this).find('a').attr('href'); - - let chapterName = loadedCheerio(this).find('a').text(); - - const releaseDate = null; - - 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: string, chapterUrl: string) => { - const url = chapterUrl; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - loadedCheerio('.block-title > h1').find('a').remove(); - - const chapterName = loadedCheerio('.block-title > h1') - .text() - .replace(' - ', ''); - - loadedCheerio('.alert').remove(); - loadedCheerio('.hidden').remove(); - loadedCheerio('iframe').remove(); - loadedCheerio('button').remove(); - loadedCheerio('.hid').remove(); - loadedCheerio('center').remove(); - loadedCheerio( - 'div[style="float: left; margin-top: 20px; font-style: italic;margin-left: 50px; font-size: 14px;"]', - ).remove(); - loadedCheerio( - 'div[style="float:left;margin-top:15px;margin-bottom:15px;"]', - ).remove(); - - const chapterText = loadedCheerio('.desc').html() || ''; - - const chapter: SourceChapter = { - sourceId: 2, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -const searchNovels = async (searchTerm: string) => { - 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(); - - const loadedCheerio = cheerio.load(body); - - let novels: SourceNovelItem[] = []; - - loadedCheerio('.top-novel-block').each(function () { - const novelUrl = - baseUrl + - loadedCheerio(this).find('.top-novel-header > h2 > a').attr('href'); - - const novelName = loadedCheerio(this) - .find('.top-novel-header > h2 > a') - .text(); - const novelCover = baseUrl + loadedCheerio(this).find('img').attr('src'); - - const novel = { - sourceId, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return novels; -}; - -const ReadFreeNovelScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default ReadFreeNovelScraper; diff --git a/src/sources/en/wuxiacity.js b/src/sources/en/wuxiacity.js index 86896d634..962a2dc6f 100644 --- a/src/sources/en/wuxiacity.js +++ b/src/sources/en/wuxiacity.js @@ -8,8 +8,8 @@ const sourceName = 'wuxia.city'; const baseUrl = 'https://wuxia.city/'; const popularNovels = async page => { - const totalPages = 133; - let url = `${baseUrl}genre/all?page=` + page; + const totalPages = 3; + let url = `${baseUrl}list/editor-pick?page=` + page; const result = await fetch(url); const body = await result.text(); diff --git a/src/sources/en/wuxiaworldcloud.js b/src/sources/en/wuxiaworldcloud.js deleted file mode 100644 index c8ed3aeee..000000000 --- a/src/sources/en/wuxiaworldcloud.js +++ /dev/null @@ -1,184 +0,0 @@ -import * as cheerio from 'cheerio'; -const baseUrl = 'http://wuxiaworld.cloud/'; - -const popularNovels = async page => { - let totalPages = 41; - const url = `${baseUrl}/popular-novel?page=${page}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('div.col-novel-main > div.list-novel > .row').each(function () { - let novelUrl = loadedCheerio(this) - .find('h3.novel-title > a') - .attr('href') - .replace(baseUrl + 'novel/', ''); - novelUrl = `${novelUrl}/`; - - const novelName = loadedCheerio(this).find('h3.novel-title > a').text(); - const novelCover = loadedCheerio(this).find('img').attr('src'); - - const novel = { - sourceId: 20, - novelUrl, - novelName, - novelCover, - }; - - 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(); - - let loadedCheerio = cheerio.load(body); - - let novel = {}; - - novel.sourceId = 20; - - novel.sourceName = 'WuxiaWorld.cloud'; - - novel.url = url; - - novel.novelUrl = novelUrl; - - novel.novelName = loadedCheerio('div.book > img').attr('alt'); - - novel.novelCover = loadedCheerio('div.book > img').attr('src'); - - novel.summary = loadedCheerio('div.desc-text').text().trim(); - - novel.author = loadedCheerio('div.info > div > h3') - .filter(function () { - return loadedCheerio(this).text().trim() === 'Author:'; - }) - .siblings() - .text(); - - novel.genre = loadedCheerio('div.info > div') - .filter(function () { - return loadedCheerio(this).find('h3').text().trim() === 'Genre:'; - }) - .text() - .replace('Genre:', '') - .replace(/\s/g, ''); - - novel.artist = null; - - novel.status = loadedCheerio('div.info > div') - .filter(function () { - return loadedCheerio(this).find('h3').text().trim() === 'Status:'; - }) - .text() - .replace('Status:', '') - .replace(/\s/g, ''); - - const novelId = loadedCheerio('#rating').attr('data-novel-id'); - - const getChapters = async id => { - const chapterListUrl = baseUrl + 'ajax/chapter-option?novelId=' + id; - - const data = await fetch(chapterListUrl); - const chapters = await data.text(); - - loadedCheerio = cheerio.load(chapters); - - let novelChapters = []; - - loadedCheerio('select > option').each(function () { - let chapterName = loadedCheerio(this).text(); - let releaseDate = null; - let chapterUrl = loadedCheerio(this).attr('value'); - chapterUrl = chapterUrl.replace(baseUrl, ''); - - novelChapters.push({ - chapterName, - releaseDate, - chapterUrl, - }); - }); - return novelChapters; - }; - - novel.chapters = await getChapters(novelId); - - return novel; -}; - -const parseChapter = async (novelUrl, chapterUrl) => { - chapterUrl = encodeURI(chapterUrl); - - const url = `${baseUrl}${chapterUrl}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - const chapterName = loadedCheerio('.chr-title').attr('title'); - let chapterText = loadedCheerio('#chr-content').html(); - const chapter = { - sourceId: 20, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -const searchNovels = async searchTerm => { - const searchUrl = 'http://wuxiaworld.cloud/search?keyword='; - - const url = `${searchUrl}${searchTerm}`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('div.col-novel-main > div.list-novel > .row').each(function () { - let novelUrl = loadedCheerio(this) - .find('h3.novel-title > a') - .attr('href') - .replace(baseUrl + 'novel/', ''); - novelUrl = `${novelUrl}/`; - - const novelName = loadedCheerio(this).find('h3.novel-title > a').text(); - const novelCover = loadedCheerio(this).find('img').attr('src'); - - const novel = { - sourceId: 20, - novelUrl, - novelName, - novelCover, - }; - - novels.push(novel); - }); - - return novels; -}; - -const WuxiaWorldCloudScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default WuxiaWorldCloudScraper; diff --git a/src/sources/en/wuxiaworldsite.js b/src/sources/en/wuxiaworldsite.js deleted file mode 100644 index 509e34c95..000000000 --- a/src/sources/en/wuxiaworldsite.js +++ /dev/null @@ -1,186 +0,0 @@ -import { fetchHtml } from '@utils/fetch/fetch'; -import * as cheerio from 'cheerio'; -const baseUrl = 'https://wuxiaworldsite.co/'; -const searchUrl = 'https://wuxiaworldsite.co/search/'; - -const popularNovels = async page => { - let totalPages = 222; - const url = 'https://wuxiaworldsite.co/ajax-story-power.ajax'; - - 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 body = await fetchHtml({ - url, - sourceId, - init: { method: 'POST', body: formData }, - }); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.a_item').each((i, el) => { - const novelName = loadedCheerio(el).find('a').attr('title'); - const novelCover = baseUrl + loadedCheerio(el).find('a > img').attr('src'); - - let novelUrl = loadedCheerio(el).find('.name_views > a').attr('href'); - const novelId = novelUrl.split('/'); - novelUrl = novelId[2] + '/'; - - const novel = { - sourceId: 12, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return { totalPages, novels }; -}; - -const parseNovelAndChapters = async novelUrl => { - const url = `${baseUrl}${novelUrl}`; - - const body = await fetchHtml({ url, sourceId }); - - let loadedCheerio = cheerio.load(body); - - loadedCheerio('.category_list').remove(); - - let novel = {}; - - novel.sourceId = 12; - - novel.sourceName = 'WuxiaWorldSite'; - - novel.url = url; - - novel.novelUrl = novelUrl; - - novel.novelName = loadedCheerio('.content-reading > h1').text().trim(); - - novel.novelCover = baseUrl + loadedCheerio('.img-read> img').attr('src'); - - novel.summary = loadedCheerio('.story-introduction-content').text(); - - novel.author = loadedCheerio('.content-reading > p').text().trim(); - - novel.artist = null; - - novel.genre = ''; - - loadedCheerio('.a_tag_item').each((i, el) => { - novel.genre += loadedCheerio(el).text() + ','; - }); - - novel.genre = novel.genre.split(','); - novel.genre.pop(); - - novel.status = novel.genre.pop(); - - novel.genre = novel.genre.join(','); - - const novelID = loadedCheerio('.show-more-list').attr('data-id'); - - const getChapters = async id => { - const chapterListUrl = baseUrl + '/get-full-list.ajax?id=' + id; - - const data = await fetch(chapterListUrl); - const chapters = await data.text(); - - loadedCheerio = cheerio.load(chapters); - - let novelChapters = []; - - loadedCheerio('.new-update-content').each((i, el) => { - let chapterName = loadedCheerio(el).text().split(/\t+/); - const releaseDate = chapterName.pop(); - chapterName = chapterName[0]; - let chapterUrl = loadedCheerio(el).attr('href'); - chapterUrl = chapterUrl.split('/').pop(); - - const chapter = { - chapterName, - releaseDate, - chapterUrl, - }; - novelChapters.push(chapter); - }); - return novelChapters; - }; - - novel.chapters = await getChapters(novelID); - - return novel; -}; - -const parseChapter = async (novelUrl, chapterUrl) => { - const url = `${baseUrl}/${novelUrl}/${chapterUrl}`; - - const body = await fetchHtml({ url, sourceId }); - - const loadedCheerio = cheerio.load(body); - - let chapterName = loadedCheerio('h1.text-center.heading_read').text(); - let chapterText = loadedCheerio('.content-story').html(); - - novelUrl += '/'; - chapterUrl += '/'; - - const chapter = { - sourceId: 12, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -const searchNovels = async searchTerm => { - const url = `${searchUrl}${searchTerm}`; - - const body = await fetchHtml({ url, sourceId }); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.a_item').each((i, el) => { - const novelName = loadedCheerio(el).find('.name_views > a').attr('title'); - const novelCover = baseUrl + loadedCheerio(el).find('a > img').attr('src'); - let novelUrl = loadedCheerio(el).find('.name_views > a').attr('href'); - const novelId = novelUrl.split('/'); - novelUrl = novelId[2] + '/'; - - const novel = { - sourceId: 12, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return novels; -}; - -const WuxiaWorldSiteScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default WuxiaWorldSiteScraper; diff --git a/src/sources/multisrc/madara/MadaraGenerator.ts b/src/sources/multisrc/madara/MadaraGenerator.ts index dbc3b3bf5..9a8101a2e 100644 --- a/src/sources/multisrc/madara/MadaraGenerator.ts +++ b/src/sources/multisrc/madara/MadaraGenerator.ts @@ -11,27 +11,6 @@ export const BoxNovelScraper = new MadaraScraper( { 'useNewChapterEndpoint': true }, ); -export const SkyNovelScraper = new MadaraScraper( - 38, - 'https://skynovel.org/', - 'SkyNovel', - { 'path': { 'novels': 'manga', 'novel': 'manga', 'chapter': 'manga' } }, -); - -export const NovelCakeScraper = new MadaraScraper( - 39, - 'https://novelcake.com/', - 'NovelCake', - { 'path': { 'novels': 'series', 'novel': 'series', 'chapter': 'series' } }, -); - -export const NovelsRockScraper = new MadaraScraper( - 40, - 'https://novelsrock.com/', - 'NovelsRock', - { 'path': { 'novels': 'novels', 'novel': 'novel', 'chapter': 'novel' } }, -); - export const ZinnNovelScraper = new MadaraScraper( 41, 'https://zinnovel.com/', @@ -84,7 +63,7 @@ export const DaoNovelScraper = new MadaraScraper( 'https://daonovel.com/', 'DaoNovel', { - 'path': { 'novels': 'novel-list', 'novel': 'novel', 'chapter': 'novel' }, + 'path': { 'novels': 'novels-list', 'novel': 'novels', 'chapter': 'novels' }, 'useNewChapterEndpoint': true, }, ); @@ -112,13 +91,6 @@ export const LightNovelHeavenScraper = new MadaraScraper( }, ); -export const LightNovelsHubScraper = new MadaraScraper( - 58, - 'https://lightnovelshub.com/', - 'LightNovelsHub', - { 'useNewChapterEndpoint': true }, -); - export const ArNovelScraper = new MadaraScraper( 59, 'https://arnovel.me/', @@ -139,10 +111,10 @@ export const WebNovelLoverScraper = new MadaraScraper( 'WebNovelLover', ); -export const TipNovelScraper = new MadaraScraper( +export const CrateNovelScraper = new MadaraScraper( 63, - 'https://tipnovel.com/', - 'TipNovel', + 'https://cratenovel.com/', + 'CrateNovel', { 'useNewChapterEndpoint': true }, ); @@ -169,6 +141,7 @@ export const WuxiaWorldDotSiteScraper = new MadaraScraper( 74, 'https://wuxiaworld.site/', 'WuxiaWorld.Site', + { 'useNewChapterEndpoint': true }, ); export const MysticalSeriesScraper = new MadaraScraper( @@ -192,6 +165,7 @@ export const OnlyMTLScraper = new MadaraScraper( 91, 'https://www.onlymtl.com/', 'OnlyMTL', + { 'useNewChapterEndpoint': true }, ); export const HizoMangaScraper = new MadaraScraper( @@ -205,13 +179,6 @@ export const HizoMangaScraper = new MadaraScraper( }, ); -export const LatestNovelScraper = new MadaraScraper( - 98, - 'https://latestnovel.net/', - 'LatestNovel', - { 'totalPages': 130 }, -); - export const ArMTLScraper = new MadaraScraper( 101, 'https://ar-mtl.club/', @@ -248,13 +215,6 @@ export const TurkceLightNovelsScraper = new MadaraScraper( }, ); -export const NovelOwlScraper = new MadaraScraper( - 111, - 'https://novelowl.com/', - 'NovelOwl', - { 'totalPages': 208, 'useNewChapterEndpoint': true }, -); - export const SonicMTLScraper = new MadaraScraper( 121, 'https://www.sonicmtl.com/', @@ -347,13 +307,6 @@ export const NocturneTranslationsScraper = new MadaraScraper( }, ); -export const AsadaTranslationScraper = new MadaraScraper( - 137, - 'https://asadatranslations.com/', - 'Asada Translation', - { 'totalPages': 1, 'lang': 'English' }, -); - export const SugarBabiesScraper = new MadaraScraper( 138, 'https://sugarbbscan.com/', diff --git a/src/sources/multisrc/madara/MadaraSources.json b/src/sources/multisrc/madara/MadaraSources.json index b7e46a844..6e3aaf223 100644 --- a/src/sources/multisrc/madara/MadaraSources.json +++ b/src/sources/multisrc/madara/MadaraSources.json @@ -7,30 +7,6 @@ "useNewChapterEndpoint": true } }, - { - "sourceId": 38, - "baseUrl": "https://skynovel.org/", - "sourceName": "SkyNovel", - "options": { - "path": { "novels": "manga", "novel": "manga", "chapter": "manga" } - } - }, - { - "sourceId": 39, - "baseUrl": "https://novelcake.com/", - "sourceName": "NovelCake", - "options": { - "path": { "novels": "series", "novel": "series", "chapter": "series" } - } - }, - { - "sourceId": 40, - "baseUrl": "https://novelsrock.com/", - "sourceName": "NovelsRock", - "options": { - "path": { "novels": "novels", "novel": "novel", "chapter": "novel" } - } - }, { "sourceId": 41, "baseUrl": "https://zinnovel.com/", @@ -83,7 +59,11 @@ "baseUrl": "https://daonovel.com/", "sourceName": "DaoNovel", "options": { - "path": { "novels": "novel-list", "novel": "novel", "chapter": "novel" }, + "path": { + "novels": "novels-list", + "novel": "novels", + "chapter": "novels" + }, "useNewChapterEndpoint": true } }, @@ -109,14 +89,6 @@ "useNewChapterEndpoint": true } }, - { - "sourceId": 58, - "baseUrl": "https://lightnovelshub.com/", - "sourceName": "LightNovelsHub", - "options": { - "useNewChapterEndpoint": true - } - }, { "sourceId": 59, "baseUrl": "https://arnovel.me/", @@ -144,8 +116,8 @@ }, { "sourceId": 63, - "baseUrl": "https://tipnovel.com/", - "sourceName": "TipNovel", + "baseUrl": "https://cratenovel.com/", + "sourceName": "CrateNovel", "options": { "useNewChapterEndpoint": true } @@ -169,7 +141,10 @@ { "sourceId": 74, "baseUrl": "https://wuxiaworld.site/", - "sourceName": "WuxiaWorld.Site" + "sourceName": "WuxiaWorld.Site", + "options": { + "useNewChapterEndpoint": true + } }, { "sourceId": 75, @@ -192,7 +167,10 @@ { "sourceId": 91, "baseUrl": "https://www.onlymtl.com/", - "sourceName": "OnlyMTL" + "sourceName": "OnlyMTL", + "options": { + "useNewChapterEndpoint": true + } }, { "sourceId": 96, @@ -204,14 +182,6 @@ "lang": "Arabic" } }, - { - "sourceId": 98, - "baseUrl": "https://latestnovel.net/", - "sourceName": "LatestNovel", - "options": { - "totalPages": 130 - } - }, { "sourceId": 101, "baseUrl": "https://ar-mtl.club/", @@ -255,15 +225,6 @@ "lang": "Turkish" } }, - { - "sourceId": 111, - "baseUrl": "https://novelowl.com/", - "sourceName": "NovelOwl", - "options": { - "totalPages": 208, - "useNewChapterEndpoint": true - } - }, { "sourceId": 121, "baseUrl": "https://www.sonicmtl.com/", @@ -354,6 +315,7 @@ "sourceName": "MTL-Novel", "options": { "totalPages": 51, + "useNewChapterEndpoint": true, "lang": "English", "orderBy": "latest" } @@ -377,15 +339,6 @@ "path": { "novels": "novels", "novel": "novels", "chapter": "novels" } } }, - { - "sourceId": 137, - "baseUrl": "https://asadatranslations.com/", - "sourceName": "Asada Translation", - "options": { - "totalPages": 1, - "lang": "English" - } - }, { "sourceId": 138, "baseUrl": "https://sugarbbscan.com/", diff --git a/src/sources/sourceManager.ts b/src/sources/sourceManager.ts index 8abffcb19..0d27b6271 100644 --- a/src/sources/sourceManager.ts +++ b/src/sources/sourceManager.ts @@ -6,18 +6,13 @@ 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 FoxaHolicScraper from './en/foxaholic'; import EinharjarProjectScraper from './es/einherjarproject'; import TuNovelaLigeraScraper from './es/tunovelaligera'; @@ -27,11 +22,11 @@ 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 LightNovelFullScraper from './en/lightnovelfull'; import { ArMTLScraper, BoxNovelScraper, @@ -39,22 +34,17 @@ import { DaoNovelScraper, FirstKissNovelScraper, HizoMangaScraper, - LatestNovelScraper, LightNovelHeavenScraper, - LightNovelsHubScraper, LunarLettersScraper, MoreNovelScraper, MostNovelScraper, MysticalSeriesScraper, Novel4UpScraper, TeamXNovelScraper, - NovelCakeScraper, NovelMultiverseScraper, - NovelsRockScraper, NovelTranslateScraper, OnlyMTLScraper, ReadWebNovelsScraper, - SkyNovelScraper, SleepyTranslationsScraper, WBNovelScraper, WebNovelLoverScraper, @@ -62,9 +52,8 @@ import { ZinnNovelScraper, TurkceLightNovelsScraper, MeioNovelScraper, - TipNovelScraper, + CrateNovelScraper, FreeNovelMeScraper, - NovelOwlScraper, SonicMTLScraper, MTLNovelDotClubScraper, NoobchanTranslationScraper, @@ -77,7 +66,6 @@ import { NeoSekaiTranslationsScraper, MTLDashNovelScraper, ZetroTranslationScraper, - AsadaTranslationScraper, SugarBabiesScraper, NocturneTranslationsScraper, NovelroomDotnetScraper, @@ -121,7 +109,6 @@ import MyLoveNovelScraper from './en/mylovenovel'; import NovelRinganScraper from './id/novelringan'; import IndoWebNovelScraper from './id/indowebnovel'; import ChireadsScraper from './fr/chireads'; -import MTLCornerScraper from './en/mtlcorner'; import NitroScansScraper from './en/nitroscans'; import NovelForestScraper from './en/novelforest'; import NovelPubScraper from './en/novelpub'; @@ -136,12 +123,10 @@ import SakuraNovelScraper from './id/sakuranovel'; import { SourceChapter, SourceNovel, SourceNovelItem } from './types'; import AllNovelFullScraper from './en/allnovelfull'; -import ReadFreeNovelScraper from './en/readfreenovel'; import FreeNovelUpdatesScraper from './en/freenovelupdates'; -import NovelsCafeScraper from './en/NovelsCafe'; import LightNovelReaderScraper from './en/LightNovelReader'; import HakoLightNovelScraper from './vi/HakoLightNovel'; -import MTNovelScraper from './en/mtnovel'; +import NovelFullScraper from './en/novelfulldotnet'; import NovelTop1Scraper from './en/noveltop1'; import { SelectedFilter, SourceFilter } from './types/filterTypes'; import KolNovelScraper from './ar/KolNovel'; @@ -170,147 +155,132 @@ interface Scraper { searchNovels: (searchTerm: string) => Promise; filters?: SourceFilter[]; } - export const sourceManager = (sourceId: number): Scraper => { const scrapers: Record = { - 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: FreeNovelMeScraper, - 46: FirstKissNovelScraper, - 47: DaoNovelScraper, - 48: WuxiaBlogScraper, - 49: WuxiaCityScraper, - 50: NovelUpdatesScraper, - 51: RanobesScraper, - 52: YushuboScraper, - 53: KolNovelScraper, - 55: MostNovelScraper, - 56: NovelMultiverseScraper, - 57: LightNovelHeavenScraper, - 58: LightNovelsHubScraper, - 59: ArNovelScraper, - 60: MeioNovelScraper, - 61: WebNovelLoverScraper, - 62: WLNUpdatesScraper, - 63: TipNovelScraper, - 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, - 79: ReaperScansBrScraper, - 80: ArthurScansScraper, - 81: IdMtlNovelScraper, - 82: MTLReaderScraper, - 83: MyLoveNovelScraper, - 84: MoreNovelScraper, - 86: NovelRinganScraper, - 87: IndoWebNovelScraper, - 88: ChireadsScraper, - 89: MTLCornerScraper, - 90: NitroScansScraper, - 91: OnlyMTLScraper, - 92: NovelForestScraper, - 93: RanobeLibScraper, - 94: NovelPubScraper, - 95: BestLightNovel, - 96: HizoMangaScraper, - 97: NovelFullMeScraper, - 98: LatestNovelScraper, - 99: NovelmtScraper, - 100: LtnovelScraper, - 101: ArMTLScraper, - 103: SakuraNovelScraper, - 104: Novel4UpScraper, - 107: TeamXNovelScraper, - 108: AllNovelFullScraper, - 109: ReadFreeNovelScraper, - 110: TurkceLightNovelsScraper, - 111: NovelOwlScraper, - 112: FreeNovelUpdatesScraper, - 113: NovelsCafeScraper, - 114: LightNovelReaderScraper, - 115: HakoLightNovelScraper, - 116: RenovelsScraper, - 117: JaomixScraper, - 118: RulateScraper, - 119: RanobeRFScraper, - 120: MTNovelScraper, - 121: SonicMTLScraper, - 122: MTLNovelDotClubScraper, - 123: LiebeSchneeHiverNovelScraper, - 124: NoobchanTranslationScraper, - 125: GuavareadScraper, - 126: SweetEscapeTranslationsScraper, - 127: FansTranslationsScraper, - 128: NovelTop1Scraper, - 129: LightNovelsBrasilScraper, - 130: RiwyatScraper, - 131: NovelsticScraper, - 132: RanobesruScraper, - 133: NeoSekaiTranslationsScraper, - 134: MTLDashNovelScraper, - 135: ZetroTranslationScraper, - 136: NocturneTranslationsScraper, - 137: AsadaTranslationScraper, - 138: SugarBabiesScraper, - 139: FicbookScraper, - 140: NovelroomDotnetScraper, - 141: NovelR18Scraper, - 142: AuthorTodayScraper, - 143: WebNovelOkuScraper, - 144: NobleMtlScraper, + // @ts-ignore + 1: BoxNovelScraper, // @ts-ignore + 2: ReadLightNovelScraper, // @ts-ignore + 3: fastNovelScraper, // @ts-ignore + 4: readNovelFullScraper, // @ts-ignore + 5: mtlNovelScraper, // @ts-ignore + 6: novelhallScraper, // @ts-ignore + 7: WuxiaWorldScraper, // @ts-ignore + 8: novelFullScraper, // @ts-ignore + 10: vipNovelScraper, // @ts-ignore + 13: FreeWebNovelScraper, // @ts-ignore + 15: lightNovelPubScraper, // @ts-ignore + 16: WuxiaWorldCoScraper, // @ts-ignore + 17: tapReadScraper, // @ts-ignore + 18: novelUpdatesCcScraper, // @ts-ignore + 19: readLightNovelCcScraper, // @ts-ignore + 21: WoopReadScraper, // @ts-ignore + 22: FoxaHolicScraper, // @ts-ignore + 23: TuNovelaLigeraScraper, // @ts-ignore + 24: SkyNovelsScraper, // @ts-ignore + 25: EinharjarProjectScraper, // @ts-ignore + 26: NovelasLigeraScraper, // @ts-ignore + 27: ComradeMaoScraper, // @ts-ignore + 28: YuukiTlsScraper, // @ts-ignore + 29: HasuTlScraper, // @ts-ignore + 30: OasisTranslationsScraper, // @ts-ignore + 31: NovelaWuxiaScraper, // @ts-ignore + 34: RoyalRoadScraper, // @ts-ignore + 35: ScribbleHubScraper, // @ts-ignore + 36: SyosetuScraper, // @ts-ignore + 37: LNMTLScraper, // @ts-ignore + 41: ZinnNovelScraper, // @ts-ignore + 42: NovelTranslateScraper, // @ts-ignore + 43: LunarLettersScraper, // @ts-ignore + 44: SleepyTranslationsScraper, // @ts-ignore + 45: FreeNovelMeScraper, // @ts-ignore + 46: FirstKissNovelScraper, // @ts-ignore + 47: DaoNovelScraper, // @ts-ignore + 48: WuxiaBlogScraper, // @ts-ignore + 49: WuxiaCityScraper, // @ts-ignore + 50: NovelUpdatesScraper, // @ts-ignore + 51: RanobesScraper, // @ts-ignore + 52: YushuboScraper, // @ts-ignore + 53: KolNovelScraper, // @ts-ignore + 55: MostNovelScraper, // @ts-ignore + 56: NovelMultiverseScraper, // @ts-ignore + 57: LightNovelHeavenScraper, // @ts-ignore + 59: ArNovelScraper, // @ts-ignore + 60: MeioNovelScraper, // @ts-ignore + 61: WebNovelLoverScraper, // @ts-ignore + 62: WLNUpdatesScraper, // @ts-ignore + 63: CrateNovelScraper, // @ts-ignore + 64: ClickNovelScraper, // @ts-ignore + 65: ReadWebNovelsScraper, // @ts-ignore + 66: WBNovelScraper, // @ts-ignore + 67: ReaperScansScraper, // @ts-ignore + 68: ReadwnScraper, // @ts-ignore + 69: RanobeHubScraper, // @ts-ignore + 70: DivineDaoLibraryScraper, // @ts-ignore + 71: NovelOnlineFullScraper, // @ts-ignore + 72: LightNovelUpdatesScraper, // @ts-ignore + 73: EpikNovelScraper, // @ts-ignore + 74: WuxiaWorldDotSiteScraper, // @ts-ignore + 75: MysticalSeriesScraper, // @ts-ignore + 76: TravisTranslationsScraper, // @ts-ignore + 77: NovelDeGlaceScraper, // @ts-ignore + 78: RainOfSnowScraper, // @ts-ignore + 79: ReaperScansBrScraper, // @ts-ignore + 80: ArthurScansScraper, // @ts-ignore + 81: IdMtlNovelScraper, // @ts-ignore + 82: MTLReaderScraper, // @ts-ignore + 83: MyLoveNovelScraper, // @ts-ignore + 84: MoreNovelScraper, // @ts-ignore + 86: NovelRinganScraper, // @ts-ignore + 87: IndoWebNovelScraper, // @ts-ignore + 88: ChireadsScraper, // @ts-ignore + 90: NitroScansScraper, // @ts-ignore + 91: OnlyMTLScraper, // @ts-ignore + 92: NovelForestScraper, // @ts-ignore + 93: RanobeLibScraper, // @ts-ignore + 94: NovelPubScraper, // @ts-ignore + 95: BestLightNovel, // @ts-ignore + 96: HizoMangaScraper, // @ts-ignore + 97: NovelFullMeScraper, // @ts-ignore + 98: LightNovelFullScraper, // @ts-ignore + 99: NovelmtScraper, // @ts-ignore + 100: LtnovelScraper, // @ts-ignore + 101: ArMTLScraper, // @ts-ignore + 103: SakuraNovelScraper, // @ts-ignore + 104: Novel4UpScraper, // @ts-ignore + 107: TeamXNovelScraper, // @ts-ignore + 108: AllNovelFullScraper, // @ts-ignore + 110: TurkceLightNovelsScraper, // @ts-ignore + 112: FreeNovelUpdatesScraper, // @ts-ignore + 114: LightNovelReaderScraper, // @ts-ignore + 115: HakoLightNovelScraper, // @ts-ignore + 116: RenovelsScraper, // @ts-ignore + 117: JaomixScraper, // @ts-ignore + 118: RulateScraper, // @ts-ignore + 119: RanobeRFScraper, // @ts-ignore + 120: NovelFullScraper, // @ts-ignore + 121: SonicMTLScraper, // @ts-ignore + 122: MTLNovelDotClubScraper, // @ts-ignore + 123: LiebeSchneeHiverNovelScraper, // @ts-ignore + 124: NoobchanTranslationScraper, // @ts-ignore + 125: GuavareadScraper, // @ts-ignore + 126: SweetEscapeTranslationsScraper, // @ts-ignore + 127: FansTranslationsScraper, // @ts-ignore + 128: NovelTop1Scraper, // @ts-ignore + 129: LightNovelsBrasilScraper, // @ts-ignore + 130: RiwyatScraper, // @ts-ignore + 131: NovelsticScraper, // @ts-ignore + 132: RanobesruScraper, // @ts-ignore + 133: NeoSekaiTranslationsScraper, // @ts-ignore + 134: MTLDashNovelScraper, // @ts-ignore + 135: ZetroTranslationScraper, // @ts-ignore + 136: NocturneTranslationsScraper, // @ts-ignore + 138: SugarBabiesScraper, // @ts-ignore + 139: FicbookScraper, // @ts-ignore + 140: NovelroomDotnetScraper, // @ts-ignore + 141: NovelR18Scraper, // @ts-ignore + 142: AuthorTodayScraper, // @ts-ignore + 143: WebNovelOkuScraper, // @ts-ignore + 144: NobleMtlScraper, // @ts-ignore 145: AgitoonScraper, }; diff --git a/src/sources/sources.json b/src/sources/sources.json index 34023b273..3c7b7d6e5 100644 --- a/src/sources/sources.json +++ b/src/sources/sources.json @@ -15,7 +15,7 @@ }, { "sourceId": 3, - "url": "https://fastnovel.net/", + "url": "https://fastnovel.org/", "sourceName": "FastNovel", "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/src/en/fastnovel/icon.png?raw=true", "lang": "English" @@ -55,13 +55,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/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/icons/src/en/noveltrench/icon.png?raw=true", - "lang": "English" - }, { "sourceId": 10, "url": "https://vipnovel.com/", @@ -69,20 +62,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/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/icons/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/icons/src/en/wuxiaworldsite/icon.png?raw=true", - "lang": "English" - }, { "sourceId": 13, "url": "https://freewebnovel.com/", @@ -90,13 +69,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/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/icons/src/en/jpmtl/icon.png?raw=true", - "lang": "English" - }, { "sourceId": 15, "url": "https://www.lightnovelpub.com/", @@ -132,13 +104,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/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/icons/src/en/wuxiaworldcloud/icon.png?raw=true", - "lang": "English" - }, { "sourceId": 21, "url": "https://woopread.com/", @@ -216,13 +181,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/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/icons/src/en/novelpassion/icon.png?raw=true", - "lang": "English" - }, { "sourceId": 34, "url": "https://www.royalroad.com/", @@ -251,27 +209,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/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/icons/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/icons/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/icons/multisrc/madara/icons/novelsrock.png?raw=true", - "lang": "English" - }, { "sourceId": 41, "url": "https://zinnovel.com/", @@ -384,13 +321,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/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/icons/multisrc/madara/icons/lightnovelshub.png?raw=true", - "lang": "English" - }, { "sourceId": 59, "url": "https://arnovel.me/", @@ -421,8 +351,8 @@ }, { "sourceId": 63, - "url": "https://boxnovel.online/", - "sourceName": "BoxNovel.online", + "url": "https://cratenovel.com/", + "sourceName": "CrateNovel", "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/multisrc/madara/icons/boxnovel.online.png?raw=true", "lang": "English" }, @@ -594,13 +524,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/src/fr/chireads/icon.png?raw=true", "lang": "French" }, - { - "sourceId": 89, - "url": "https://mtlcorner.com/", - "sourceName": "MTLCorner", - "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/src/en/mtlcorner/icon.png?raw=true", - "lang": "English" - }, { "sourceId": 90, "url": "https://nitroscans.com/", @@ -659,8 +582,8 @@ }, { "sourceId": 98, - "url": "https://latestnovel.net/", - "sourceName": "LatestNovel", + "url": "https://lightnovelfull.com/", + "sourceName": "LightNovelFull", "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/multisrc/madara/icons/latestnovel.png?raw=true", "lang": "English" }, @@ -713,13 +636,6 @@ "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/src/en/allnovelfull/icon.png?raw=true", "lang": "English" }, - { - "sourceId": 109, - "sourceName": "ReadFreeNovel", - "url": "https://www.readfreenovel.com", - "lang": "English", - "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/src/en/readlightnovel/icon.png?raw=true" - }, { "sourceId": 110, "sourceName": "TurkceLightNovels", @@ -727,13 +643,6 @@ "lang": "Turkish", "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/src/tr/TurkceLightNovels/icon.png?raw=true" }, - { - "sourceId": 111, - "sourceName": "NovelOwl", - "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/multisrc/madara/icons/novelowl.png?raw=true", - "url": "https://novelowl.com/", - "lang": "English" - }, { "sourceId": 112, "sourceName": "FreeNovelUpdates", @@ -741,13 +650,6 @@ "url": "https://www.freenovelupdates.com/", "lang": "English" }, - { - "sourceId": 113, - "sourceName": "NovelsCafe", - "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/src/en/novelscafe/icon.png?raw=true", - "url": "https://novelscafe.com", - "lang": "English" - }, { "sourceId": 114, "sourceName": "LightNovelReader", @@ -792,8 +694,8 @@ }, { "sourceId": 120, - "url": "https://mtnovel.com/", - "sourceName": "MT Novel", + "url": "https://novelfull.net/", + "sourceName": "NovelFull.net", "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/src/en/allnovelfull/icon.png?raw=true", "lang": "English" }, @@ -909,13 +811,6 @@ "url": "https://nocturnetls.net/", "lang": "English" }, - { - "sourceId": 137, - "sourceName": "Asada Translations", - "icon": "/~https://github.com/LNReader/lnreader-sources/blob/main/icons/multisrc/madara/icons/asadatranslations.png?raw=true", - "url": "https://asadatranslations.com/", - "lang": "English" - }, { "sourceId": 138, "sourceName": "Sugar Babies",