8522c89195
* update * gitignore * base file * template * json generator * remove valid method * Update README.md README * update template * Update README.md * update template * not support async arrow function * remove valid method * basic plugin validation. * Update README.md * update template * Update README.md * add fetchFile lib. empy .language file * comment fetch * update * comment fetch * auto comment, update template, template.min * Update README.md * add option to test * reset version * Update README.md * small fix for LightNovelReader * comment fetch * minor: LL date format * refactor: rename project, remove html2text lib * fix: error if there's no plugin changed * implement cookies bypass * update template.min.js * add syosetu * fix: get last file in case removing. shorter id * shoter native language name * change app project name * testing with UI would be less painful :) * remove console.log * Update README.md for UI testing * fix Syosetu * remove try catch * add ranobehub * add json for ranobehub * wrong icon path :D * fix ranobehub * fix status ranobe * add french * fix css * add chireads * add indonesian * add sakura novel * add sakuranovel json * add spanish * add skynovels * fix skynovels * add agitoon * add turkish * add epiknovel * add portuguese * update template * cheerio_space * add novel Fullme * minor * lol, this source havent been finished before. cant help * cant bypass this * add hasutl * novelasligera not working properly * add novelpub, but no searching * add jaomix * fix jaomix * add ranoberf * add indowebnovel * add novelringan * add noveldeglace * search not work for this * add oasisTL, this should have better popularNovels * add tunovelaligera * this source need to fix search * add bestlightnovel * add comrade mao * minor fix * add divinedaolibrary * add freewebnovel * lol xD * minor * update json * fix icon novelawuxia * update site in for pluginItem * some changes for more convenient * remove try catch * Update README.md * remove * Update README.md * trying to use pre-commit * update README * feat: .env for repo config * add foxaholic (#2) * rebase fox * fox officially 1.0.0 --------- Co-authored-by: K1ngfish3r <trulyihu@gmail.com>
205 lines
6.9 KiB
JavaScript
205 lines
6.9 KiB
JavaScript
const cheerio = require('cheerio');
|
||
const languages = require('@libs/languages');
|
||
const fetchApi = require('@libs/fetchApi');
|
||
const fetchFile = require('@libs/fetchFile');
|
||
// const novelStatus = require('@libs/novelStatus');
|
||
// const isUrlAbsolute = require('@libs/isAbsoluteUrl');
|
||
// const parseDate = require('@libs/parseDate');
|
||
|
||
const pluginId = 'yomou.syosetu';
|
||
|
||
const defaultCoverUri =
|
||
'https://github.com/LNReader/lnreader-sources/blob/main/icons/src/coverNotAvailable.jpg?raw=true';
|
||
|
||
const searchUrl = (pagenum, order) => {
|
||
return `https://yomou.syosetu.com/search.php?order=${order || 'hyoka'}${!isNaN((pagenum = parseInt(pagenum, 10))) // check if pagenum is a number
|
||
? `&p=${pagenum <= 1 || pagenum > 100 ? '1' : pagenum}` // check if pagenum is between 1 and 100
|
||
: '' // if isn't don't set ?p
|
||
}`;
|
||
};
|
||
|
||
const novelCover = defaultCoverUri;
|
||
|
||
async function popularNovels(pageNo) {
|
||
let novels = [];
|
||
|
||
async function getNovelsFromPage(pagenumber) {
|
||
// load page
|
||
const result = await fetchApi(searchUrl(pagenumber || null));
|
||
const body = await result.text();
|
||
// Cheerio it!
|
||
const cheerioQuery = cheerio.load(body, { decodeEntities: false });
|
||
let pageNovels = [];
|
||
// find class=searchkekka_box
|
||
cheerioQuery('.searchkekka_box').each(function (i, e) {
|
||
// get div with link and name
|
||
const novelDIV = cheerioQuery(this).find('.novel_h');
|
||
// get link element
|
||
const novelA = novelDIV.children()[0];
|
||
// add new novel to array
|
||
pageNovels.push({
|
||
name: novelDIV.text(), // get the name
|
||
url: novelA.attribs.href, // get last part of the link
|
||
cover: novelCover, // TODO: IDK what to do about covers... On Syo they don't have them
|
||
});
|
||
});
|
||
// return all novels from this page
|
||
return pageNovels;
|
||
};
|
||
|
||
novels = await getNovelsFromPage(pageNo);
|
||
|
||
return novels;
|
||
};
|
||
|
||
async function parseNovelAndChapters(novelUrl) {
|
||
let chapters = [];
|
||
const result = await fetchApi(novelUrl);
|
||
const body = await result.text();
|
||
const cheerioQuery = cheerio.load(body, { decodeEntities: false });
|
||
|
||
// create novel object
|
||
let novel = {
|
||
url: novelUrl,
|
||
name: cheerioQuery('.novel_title').text(),
|
||
author: cheerioQuery('.novel_writername').text().replace('作者:', ''),
|
||
cover: novelCover,
|
||
};
|
||
|
||
// Get all the chapters
|
||
const cqGetChapters = cheerioQuery('.novel_sublist2');
|
||
if (cqGetChapters.length !== 0) {
|
||
// has more than 1 chapter
|
||
novel.summary = cheerioQuery('#novel_ex')
|
||
.text()
|
||
.replace(/<\s*br.*?>/g, '\n');
|
||
cqGetChapters.each(function (i, e) {
|
||
const chapterA = cheerioQuery(this).find('a');
|
||
const [chapterName, releaseDate, chapterUrl] = [
|
||
// set the variables
|
||
chapterA.text(),
|
||
cheerioQuery(this)
|
||
.find('dt') // get title
|
||
.text() // get text
|
||
.replace(/(.)/g, '') // remove "(edited)" mark
|
||
.trim(), // trim spaces
|
||
'https://ncode.syosetu.com' + chapterA.attr('href'),
|
||
];
|
||
chapters.push({ name: chapterName, releaseTime: releaseDate, url: chapterUrl });
|
||
});
|
||
} else {
|
||
/**
|
||
* Because there are oneshots on the site, they have to be treated with special care
|
||
* that's what pisses me off in Shosetsu app. They have this extension,
|
||
* but every oneshot is set as "there are no chapters" and all contents are thrown into the description!!
|
||
*/
|
||
// get summary for oneshot chapter
|
||
|
||
const nameResult = await fetchApi(searchUrl() + `&word=${novel.name}`);
|
||
const nameBody = await nameResult.text();
|
||
const summaryQuery = cheerio.load(nameBody, { decodeEntities: false });
|
||
const foundText = summaryQuery('.searchkekka_box')
|
||
.first()
|
||
.find('.ex')
|
||
.text()
|
||
.replace(/\s{2,}/g, '\n');
|
||
novel.summary = foundText;
|
||
|
||
// add single chapter
|
||
chapters.push({
|
||
name: 'Oneshot',
|
||
releaseTime: cheerioQuery('head')
|
||
.find("meta[name='WWWC']")
|
||
.attr('content'), // get date from metadata
|
||
url: novelUrl, // set chapterUrl to oneshot so that chapterScraper knows it's a one-shot
|
||
});
|
||
}
|
||
novel.chapters = chapters;
|
||
return novel;
|
||
};
|
||
|
||
async function parseChapter(chapterUrl) {
|
||
const result = await fetchApi(chapterUrl);
|
||
const body = await result.text();
|
||
|
||
// create cheerioQuery
|
||
const cheerioQuery = cheerio.load(body, {
|
||
decodeEntities: false,
|
||
});
|
||
|
||
let chapterText = cheerioQuery('#novel_honbun') // get chapter text
|
||
.html();
|
||
return chapterText;
|
||
};
|
||
|
||
|
||
async function searchNovels(searchTerm, pageNo) {
|
||
let novels = [];
|
||
|
||
// returns list of novels from given page
|
||
async function getNovelsFromPage (pagenumber) {
|
||
// load page
|
||
const result = await fetchApi(
|
||
searchUrl(pagenumber || null, null) + `&word=${searchTerm}`
|
||
);
|
||
const body = await result.text();
|
||
// Cheerio it!
|
||
const cheerioQuery = cheerio.load(body, { decodeEntities: false });
|
||
|
||
let pageNovels = [];
|
||
// find class=searchkekka_box
|
||
cheerioQuery('.searchkekka_box').each(function (i, e) {
|
||
// get div with link and name
|
||
const novelDIV = cheerioQuery(this).find('.novel_h');
|
||
// get link element
|
||
const novelA = novelDIV.children()[0];
|
||
// add new novel to array
|
||
pageNovels.push({
|
||
name: novelDIV.text(), // get the name
|
||
url: novelA.attribs.href, // get last part of the link
|
||
cover: novelCover, // TODO: IDK what to do about covers... On Syo they don't have them
|
||
});
|
||
});
|
||
// return all novels from this page
|
||
return pageNovels;
|
||
};
|
||
|
||
// counter of loaded pages
|
||
// let pagesLoaded = 0;
|
||
// do {
|
||
// // always load first one
|
||
// novels.push(...(await getNovelsFromPage(pagesLoaded + 1)));
|
||
// pagesLoaded++;
|
||
// } while (pagesLoaded < maxPageLoad && isNext); // check if we should load more
|
||
|
||
novels = await getNovelsFromPage(1);
|
||
|
||
/** Use
|
||
* novels.push(...(await getNovelsFromPage(pageNumber)))
|
||
* if you want to load more
|
||
*/
|
||
|
||
// respond with novels!
|
||
return novels;
|
||
};
|
||
|
||
async function fetchImage(url) {
|
||
|
||
return await fetchFile(url);
|
||
};
|
||
|
||
module.exports = {
|
||
id: pluginId,
|
||
name: 'Syosetu',
|
||
icon: 'src/jp/syosetu/icon.png',
|
||
version: '1.0.0',
|
||
site: 'https://yomou.syosetu.com/',
|
||
lang: languages.Japanese,
|
||
description: '小説を読もう!は「小説家になろう」に投稿された',
|
||
protected: false,
|
||
fetchImage,
|
||
popularNovels,
|
||
parseNovelAndChapters,
|
||
parseChapter,
|
||
searchNovels,
|
||
} |