fix: Broken Chapter Scraper Due to Layout Change (Fenrir) (#1884)

* fenrir: Fix broken chapter scrapper due to layout change

Also remove comment nodes to reduce chapter size.

Signed-off-by: Martian Potato <2260cse01024@manarat.ac.bd>

* fenrir: Add type annotations

Signed-off-by: Martian Potato <2260cse01024@manarat.ac.bd>

---------

Signed-off-by: Martian Potato <2260cse01024@manarat.ac.bd>
This commit is contained in:
The Couch Potato (on Mars)
2025-11-17 19:48:26 +00:00
committed by GitHub
parent 78d7a86f42
commit 0e92630eea
+60 -12
View File
@@ -1,16 +1,44 @@
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@/types/plugin';
import { Node } from 'domhandler';
import { load as loadCheerio } from 'cheerio';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { storage } from '@libs/storage';
import { defaultCover } from '@libs/defaultCover';
type APINovel = {
title: string;
slug: string;
cover: string;
description: string;
status: string;
genres: { name: string }[];
};
type APIChapter = {
locked: { price: number } | null;
group: null | {
index: number;
slug: string;
};
title: string;
number: number;
created_at: string;
};
type ChapterInfo = {
name: string;
path: string;
releaseTime: string;
chapterNumber: number;
};
class FenrirRealmPlugin implements Plugin.PluginBase {
id = 'fenrir';
name = 'Fenrir Realm';
icon = 'src/en/fenrirrealm/icon.png';
site = 'https://fenrirealm.com';
version = '1.0.11';
version = '1.0.12';
imageRequestInit?: Plugin.ImageRequestInit | undefined = undefined;
hideLocked = storage.get('hideLocked');
@@ -48,7 +76,7 @@ class FenrirRealmPlugin implements Plugin.PluginBase {
}),
);
return res.data.map(r => this.parseNovelFromApi(r));
return res.data.map((r: APINovel) => this.parseNovelFromApi(r));
}
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
@@ -88,14 +116,14 @@ class FenrirRealmPlugin implements Plugin.PluginBase {
).then(r => r.json());
if (this.hideLocked) {
chapters = chapters.filter(c => !c.locked?.price);
chapters = chapters.filter((c: APIChapter) => !c.locked?.price);
}
novel.chapters = chapters
.map(c => ({
.map((c: APIChapter) => ({
name:
(c.locked?.price ? '🔒 ' : '') +
(c.group?.index === null ? '' : 'Vol ' + c.group.index + ' ') +
(c.group?.index === null ? '' : 'Vol ' + c.group?.index + ' ') +
'Chapter ' +
c.number +
(c.title && c.title.trim() != 'Chapter ' + c.number
@@ -103,13 +131,15 @@ class FenrirRealmPlugin implements Plugin.PluginBase {
: ''),
path:
novelPath +
(c.group?.index === null ? '' : '/' + c.group.slug) +
(c.group?.index === null ? '' : '/' + c.group?.slug) +
'/chapter-' +
c.number,
releaseTime: c.created_at,
chapterNumber: c.number + c.group?.index * 1000000000000,
chapterNumber: c.number + (c.group?.index || 0) * 1000000000000,
}))
.sort((a, b) => a.chapterNumber - b.chapterNumber);
.sort(
(a: ChapterInfo, b: ChapterInfo) => a.chapterNumber - b.chapterNumber,
);
return novel;
}
@@ -117,7 +147,15 @@ class FenrirRealmPlugin implements Plugin.PluginBase {
const page = await fetchApi(this.site + '/series/' + chapterPath, {}).then(
r => r.text(),
);
return loadCheerio(page)('#reader-area').html() || '';
const chapter = loadCheerio(page)('[id^="reader-area-"]');
chapter
.contents()
.filter((_, node: Node) => {
return node.type === 'comment';
})
.remove();
return chapter.html() || '';
}
async searchNovels(
@@ -128,10 +166,12 @@ class FenrirRealmPlugin implements Plugin.PluginBase {
`${this.site}/api/series/filter?page=${pageNo}&per_page=20&search=${encodeURIComponent(searchTerm)}`,
)
.then(r => r.json())
.then(r => r.data.map(novel => this.parseNovelFromApi(novel)));
.then(r =>
r.data.map((novel: APINovel) => this.parseNovelFromApi(novel)),
);
}
parseNovelFromApi(apiData) {
parseNovelFromApi(apiData: APINovel) {
return {
name: apiData.title,
path: apiData.slug,
@@ -249,11 +289,19 @@ class FenrirRealmPlugin implements Plugin.PluginBase {
export default new FenrirRealmPlugin();
type GenreData = {
name: string;
id: number;
};
//paste into console on site to load
async function getUpdatedGenres() {
const data = await fetch(
'https://fenrirealm.com/api/novels/taxonomy/genres',
).then(d => d.json());
const genreData = data.map(g => ({ label: g.name, value: g.id.toString() }));
const genreData = data.map((g: GenreData) => ({
label: g.name,
value: g.id.toString(),
}));
console.log(JSON.stringify(genreData));
}