Skip to content

Commit 713bfe3

Browse files
author
yemen
committed
feat(ar/rewayahfans): add روايه فانز plugin
Add support for rewayahfans.net with WordPress REST API for novel listing, chapter parsing, and search functionality.
1 parent c619733 commit 713bfe3

3 files changed

Lines changed: 200 additions & 0 deletions

File tree

plugins/arabic/rewayahfans.ts

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { load as parseHTML } from 'cheerio';
2+
import { fetchApi } from '@libs/fetch';
3+
import { Plugin } from '@/types/plugin';
4+
5+
type WPPage = {
6+
id: number;
7+
title: { rendered: string };
8+
slug: string;
9+
link: string;
10+
content: { rendered: string };
11+
date: string;
12+
};
13+
14+
class RewayahFans implements Plugin.PluginBase {
15+
id = 'rewayahfans';
16+
name = 'روايه فانز';
17+
version = '4.0.0';
18+
icon = 'src/ar/rewayahfans/icon.png';
19+
site = 'https://rewayahfans.net/';
20+
21+
private async fetchJson<T>(url: string): Promise<T> {
22+
const res = await fetchApi(url);
23+
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
24+
return res.json() as Promise<T>;
25+
}
26+
27+
private async fetchHtml(url: string): Promise<string> {
28+
const res = await fetchApi(url);
29+
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
30+
return res.text();
31+
}
32+
33+
async popularNovels(
34+
page: number,
35+
{ showLatestNovels }: Plugin.PopularNovelsOptions,
36+
): Promise<Plugin.NovelItem[]> {
37+
if (showLatestNovels) {
38+
const pages = await this.fetchJson<WPPage[]>(
39+
`${this.site}wp-json/wp/v2/pages?per_page=20&page=${page}&orderby=date&order=desc&_fields=slug,title`,
40+
);
41+
return pages.map(p => ({
42+
name: this.extractNovelName(p.title.rendered),
43+
path: p.slug,
44+
cover: '',
45+
}));
46+
}
47+
48+
const allNovels = await this.getAllNovels();
49+
const pageSize = 20;
50+
const start = (page - 1) * pageSize;
51+
return allNovels.slice(start, start + pageSize);
52+
}
53+
54+
private async getAllNovels(): Promise<Plugin.NovelItem[]> {
55+
const seen = new Set<string>();
56+
const novels: Plugin.NovelItem[] = [];
57+
58+
let pg = 1;
59+
let hasMore = true;
60+
61+
while (hasMore) {
62+
const pages = await this.fetchJson<WPPage[]>(
63+
`${this.site}wp-json/wp/v2/pages?per_page=100&page=${pg}&_fields=slug,title`,
64+
);
65+
66+
if (pages.length === 0) {
67+
hasMore = false;
68+
break;
69+
}
70+
71+
for (const page of pages) {
72+
const novelName = this.extractNovelName(page.title.rendered);
73+
if (novelName && !seen.has(novelName)) {
74+
seen.add(novelName);
75+
novels.push({
76+
name: novelName,
77+
path: page.slug,
78+
cover: '',
79+
});
80+
}
81+
}
82+
83+
if (pages.length < 100) hasMore = false;
84+
pg++;
85+
}
86+
87+
return novels;
88+
}
89+
90+
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
91+
const novel: Plugin.SourceNovel = {
92+
path: novelPath,
93+
name: '',
94+
chapters: [],
95+
};
96+
97+
const slugBase = novelPath.replace(/\/$/, '').split('/').pop() || novelPath;
98+
const searchTerm = slugBase
99+
.replace(/-\d+$/, '')
100+
.replace(/-/g, ' ');
101+
102+
let pg = 1;
103+
let hasMore = true;
104+
105+
while (hasMore) {
106+
const pages = await this.fetchJson<WPPage[]>(
107+
`${this.site}wp-json/wp/v2/pages?search=${encodeURIComponent(searchTerm)}&per_page=100&page=${pg}&_fields=slug,title,date`,
108+
);
109+
110+
if (pages.length === 0) {
111+
hasMore = false;
112+
break;
113+
}
114+
115+
for (const page of pages) {
116+
const postSlug = page.slug;
117+
if (postSlug.startsWith(slugBase.replace(/-\d+$/, ''))) {
118+
if (!novel.name) {
119+
novel.name = this.extractNovelName(page.title.rendered);
120+
}
121+
const numMatch = postSlug.match(/(\d+)$/);
122+
const chapterNum = numMatch ? parseInt(numMatch[1], 10) : 0;
123+
124+
novel.chapters!.push({
125+
name: page.title.rendered,
126+
path: postSlug,
127+
chapterNumber: chapterNum,
128+
releaseTime: page.date,
129+
});
130+
}
131+
}
132+
133+
if (pages.length < 100) hasMore = false;
134+
pg++;
135+
}
136+
137+
if (novel.chapters!.length > 0) {
138+
novel.chapters!.sort((a, b) => (a.chapterNumber || 0) - (b.chapterNumber || 0));
139+
if (!novel.name && novel.chapters!.length > 0) {
140+
novel.name = this.extractNovelName(novel.chapters![0].name);
141+
}
142+
}
143+
144+
return novel;
145+
}
146+
147+
async parseChapter(chapterPath: string): Promise<string> {
148+
const pages = await this.fetchJson<WPPage[]>(
149+
`${this.site}wp-json/wp/v2/pages?slug=${chapterPath}&_fields=content`,
150+
);
151+
152+
const arr = Array.isArray(pages) ? pages : [pages];
153+
if (arr.length > 0 && arr[0].content?.rendered) {
154+
const $ = parseHTML(arr[0].content.rendered);
155+
$('script, style, .sharedaddy, .jp-relatedposts, .wp-block-spacer, .simplefavorite-button').remove();
156+
return $.html();
157+
}
158+
159+
const html = await this.fetchHtml(`${this.site}${chapterPath}/`);
160+
const $ = parseHTML(html);
161+
const content =
162+
$('article .entry-content, .post-content, .entry-content').html() || '';
163+
return content || '<p>المحتوى غير متاح.</p>';
164+
}
165+
166+
async searchNovels(
167+
searchTerm: string,
168+
page: number,
169+
): Promise<Plugin.NovelItem[]> {
170+
const pages = await this.fetchJson<WPPage[]>(
171+
`${this.site}wp-json/wp/v2/pages?search=${encodeURIComponent(searchTerm)}&per_page=20&page=${page}&_fields=slug,title`,
172+
);
173+
174+
const seen = new Set<string>();
175+
const novels: Plugin.NovelItem[] = [];
176+
177+
for (const page of pages) {
178+
const novelName = this.extractNovelName(page.title.rendered);
179+
if (novelName && !seen.has(novelName)) {
180+
seen.add(novelName);
181+
novels.push({
182+
name: novelName,
183+
path: page.slug,
184+
cover: '',
185+
});
186+
}
187+
}
188+
189+
return novels;
190+
}
191+
192+
private extractNovelName(title: string): string {
193+
const match = title.match(/^(.+?)\s+\d+$/);
194+
return match ? match[1].trim() : title.trim();
195+
}
196+
}
197+
198+
export default new RewayahFans();

plugins/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ import p_248 from '@plugins/vietnamese/lightnovelvn';
251251
import p_249 from '@plugins/vietnamese/LNHako';
252252
import p_250 from '@plugins/vietnamese/nettruyen';
253253
import p_251 from '@plugins/vietnamese/truyenss';
254+
import p_252 from '@plugins/arabic/rewayahfans';
254255

255256
const PLUGINS: Plugin.PluginBase[] = [
256257
p_0,
@@ -505,5 +506,6 @@ const PLUGINS: Plugin.PluginBase[] = [
505506
p_249,
506507
p_250,
507508
p_251,
509+
p_252,
508510
];
509511
export default PLUGINS;
169 KB
Loading

0 commit comments

Comments
 (0)