refactor: Update Read From Net to Use Desktop UA, URL Method, and Inline Tags in parseChapter (#2117)
This commit is contained in:
+74
-68
@@ -4,14 +4,22 @@ import { Filters } from '@libs/filterInputs';
|
||||
import { CheerioAPI, load as loadCheerio } from 'cheerio';
|
||||
import { defaultCover } from '@libs/defaultCover';
|
||||
|
||||
const pluginHeaders = {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
|
||||
};
|
||||
|
||||
class ReadFromPlugin implements Plugin.PluginBase {
|
||||
id = 'readfrom';
|
||||
name = 'Read From Net';
|
||||
icon = 'src/en/readfrom/icon.png';
|
||||
site = 'https://readfrom.net';
|
||||
version = '1.0.2';
|
||||
site = 'https://readfrom.net/';
|
||||
version = '1.1.0';
|
||||
filters: Filters | undefined = undefined;
|
||||
imageRequestInit?: Plugin.ImageRequestInit | undefined = undefined;
|
||||
headers = new Headers(pluginHeaders);
|
||||
imageRequestInit: Plugin.ImageRequestInit = {
|
||||
headers: pluginHeaders,
|
||||
};
|
||||
|
||||
//flag indicates whether access to LocalStorage, SesesionStorage is required.
|
||||
webStorageUtilized?: boolean;
|
||||
@@ -34,6 +42,9 @@ class ReadFromPlugin implements Plugin.PluginBase {
|
||||
(isSearch ? 'div.text' : '#dle-content') + ' > article.box',
|
||||
)
|
||||
.map((i, el) => {
|
||||
const $el = loadedCheerio(el);
|
||||
const novelPath = $el.find('h2.title a').attr('href');
|
||||
if (!novelPath) return;
|
||||
const summary = loadedCheerio(el).find(
|
||||
isSearch ? 'div.text5' : 'div.text3',
|
||||
)[0];
|
||||
@@ -41,11 +52,7 @@ class ReadFromPlugin implements Plugin.PluginBase {
|
||||
loadedCheerio(summary).find('a').remove();
|
||||
return {
|
||||
name: loadedCheerio(el).find('h2.title').text().trim(),
|
||||
path: loadedCheerio(el)
|
||||
.find('h2.title > a')
|
||||
.attr('href')!
|
||||
.replace('https://readfrom.net/', '')
|
||||
.replace(/^\//, ''),
|
||||
path: new URL(novelPath, this.site).pathname.substring(1),
|
||||
cover: loadedCheerio(el).find('img').attr('src') || defaultCover,
|
||||
summary:
|
||||
loadedCheerio(summary).text().trim() +
|
||||
@@ -78,21 +85,20 @@ class ReadFromPlugin implements Plugin.PluginBase {
|
||||
|
||||
async popularNovels(
|
||||
pageNo: number,
|
||||
{
|
||||
showLatestNovels,
|
||||
filters,
|
||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||
{ showLatestNovels }: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||
) {
|
||||
const type = showLatestNovels ? 'last_added_books' : 'allbooks';
|
||||
const res = await fetchApi(
|
||||
'https://readfrom.net/' + type + '/page/' + pageNo + '/',
|
||||
);
|
||||
const res = await fetchApi(this.site + type + '/page/' + pageNo, {
|
||||
headers: this.headers,
|
||||
});
|
||||
const text = await res.text();
|
||||
return this.parseNovels(loadCheerio(text));
|
||||
}
|
||||
|
||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||
const data = await fetchApi('https://readfrom.net/' + novelPath);
|
||||
const data = await fetchApi(this.site + novelPath, {
|
||||
headers: this.headers,
|
||||
});
|
||||
const text = await data.text();
|
||||
const loadedCheerio = loadCheerio(text);
|
||||
|
||||
@@ -109,25 +115,22 @@ class ReadFromPlugin implements Plugin.PluginBase {
|
||||
loadedCheerio('article.box > div > center > div > a > img').attr('src') ||
|
||||
defaultCover;
|
||||
|
||||
novel.chapters = loadedCheerio(loadedCheerio('div.pages').get()[0])
|
||||
const rawChapters = loadedCheerio('div.pages')
|
||||
.first()
|
||||
.find('> a')
|
||||
.map((i, el) => {
|
||||
return {
|
||||
name: loadedCheerio(el).text().trim(),
|
||||
path: loadedCheerio(el)
|
||||
.attr('href')!
|
||||
.replace('https://readfrom.net/', '')
|
||||
.replace(/^\//, ''),
|
||||
// releaseTime: '',
|
||||
chapterNumber: i + 2,
|
||||
};
|
||||
})
|
||||
.toArray();
|
||||
novel.chapters.unshift({
|
||||
name: '1',
|
||||
path: novelPath,
|
||||
chapterNumber: 1,
|
||||
});
|
||||
.toArray()
|
||||
.flatMap(el => {
|
||||
const href = loadedCheerio(el).attr('href');
|
||||
if (!href) return [];
|
||||
|
||||
const path = new URL(href, this.site).pathname.substring(1);
|
||||
return path ? [{ name: loadedCheerio(el).text().trim(), path }] : [];
|
||||
});
|
||||
|
||||
novel.chapters = [
|
||||
{ name: '1', path: novelPath, chapterNumber: 1 },
|
||||
...rawChapters.map((ch, i) => ({ ...ch, chapterNumber: i + 2 })),
|
||||
];
|
||||
|
||||
let moreNovelInfo = this.loadedNovelCache.find(
|
||||
novel => novel.path === novelPath,
|
||||
@@ -156,43 +159,45 @@ class ReadFromPlugin implements Plugin.PluginBase {
|
||||
}
|
||||
|
||||
async parseChapter(chapterPath: string): Promise<string> {
|
||||
const data = await fetchApi('https://readfrom.net/' + chapterPath);
|
||||
const text = await data.text();
|
||||
const loadedCheerio = loadCheerio(text);
|
||||
loadedCheerio('#textToRead > span:empty').remove();
|
||||
loadedCheerio('#textToRead > center').remove();
|
||||
|
||||
const textToRead = loadedCheerio('#textToRead');
|
||||
|
||||
let paragraph: string[] = [];
|
||||
const chapterHtml: string[] = [];
|
||||
|
||||
textToRead.contents().each((_, element) => {
|
||||
switch (element.type) {
|
||||
case 'text':
|
||||
const content = element.data.trim();
|
||||
if (content) {
|
||||
paragraph.push(content);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'tag':
|
||||
if (paragraph.length > 0) {
|
||||
chapterHtml.push(`<p>${paragraph.join(' ')}</p>`);
|
||||
paragraph = [];
|
||||
}
|
||||
if (element.tagName !== 'br') {
|
||||
chapterHtml.push(loadedCheerio.html(element));
|
||||
}
|
||||
break;
|
||||
}
|
||||
const response = await fetchApi(this.site + chapterPath, {
|
||||
headers: this.headers,
|
||||
});
|
||||
const $ = loadCheerio(await response.text());
|
||||
$('#textToRead > span:empty, #textToRead > center').remove();
|
||||
|
||||
// Close any remaining paragraph
|
||||
if (paragraph.length > 0) {
|
||||
chapterHtml.push(`<p>${paragraph.join(' ')}</p>`);
|
||||
const chapterHtml: string[] = [];
|
||||
let p: string[] = [];
|
||||
|
||||
const allowed = new Set(['b', 'i', 'u', 'strong', 'em', 'a']);
|
||||
const flush = () =>
|
||||
p.length && (chapterHtml.push(`<p>${p.join(' ').trim()}</p>`), (p = []));
|
||||
|
||||
for (const el of $('#textToRead').contents().toArray()) {
|
||||
switch (el.type) {
|
||||
case 'comment':
|
||||
continue;
|
||||
case 'text':
|
||||
if (el.data.trim()) {
|
||||
// Convert _text_ to <i>text</i>
|
||||
const jbText = el.data.trim().replace(/_([^_]+)_/g, '<i>$1</i>');
|
||||
p.push(jbText);
|
||||
}
|
||||
continue;
|
||||
case 'tag':
|
||||
if (allowed.has(el.name)) {
|
||||
p.push($.html(el));
|
||||
continue;
|
||||
}
|
||||
if (el.name === 'br') {
|
||||
flush();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
flush();
|
||||
chapterHtml.push($.html(el));
|
||||
}
|
||||
|
||||
flush();
|
||||
return chapterHtml.join('');
|
||||
}
|
||||
|
||||
@@ -201,12 +206,13 @@ class ReadFromPlugin implements Plugin.PluginBase {
|
||||
const res = await fetchApi(
|
||||
'https://readfrom.net/build_in_search/?q=' +
|
||||
encodeURIComponent(searchTerm),
|
||||
{ headers: this.headers },
|
||||
);
|
||||
const text = await res.text();
|
||||
return this.parseNovels(loadCheerio(text), true);
|
||||
}
|
||||
|
||||
resolveUrl = (path: string, isNovel?: boolean) => this.site + '/' + path;
|
||||
// resolveUrl = (path: string, isNovel?: boolean) => this.site + '/' + path;
|
||||
}
|
||||
|
||||
export default new ReadFromPlugin();
|
||||
|
||||
Reference in New Issue
Block a user