Skip to content

Commit 33ef367

Browse files
author
yemen
committed
feat(ar/markazriwayat): convert from madara to standalone plugin
Remove Markazriwayat from madara multisrc and add as standalone plugin with custom HTML parsing, filters, and WordPress REST API search.
1 parent e935c40 commit 33ef367

4 files changed

Lines changed: 157 additions & 233 deletions

File tree

‎plugins/arabic/Markazriwayat.ts‎

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import { load as parseHTML } from 'cheerio';
2+
import { fetchApi } from '@libs/fetch';
3+
import { Plugin } from '@/types/plugin';
4+
import { Filters, FilterTypes } from '@libs/filterInputs';
5+
import { defaultCover } from '@libs/defaultCover';
6+
7+
class Markazriwayat implements Plugin.PluginBase {
8+
id = 'markazriwayat';
9+
name = 'مركز الروايات';
10+
version = '1.0.0';
11+
icon = 'src/ar/markazriwayat/icon.png';
12+
site = 'https://markazriwayat.com/';
13+
14+
private async fetchCheerio(url: string) {
15+
const res = await fetchApi(url);
16+
if (!res.ok)
17+
throw new Error('Could not reach site (' + res.status + ')');
18+
return parseHTML(await res.text());
19+
}
20+
21+
parseNovels($: CheerioAPI): Plugin.NovelItem[] {
22+
const novels: Plugin.NovelItem[] = [];
23+
$('a.lib-card, a.novel-card').each((_, el) => {
24+
const $el = $(el);
25+
const name = $el.find('.lib-card__title, .novel-card__title').text().trim();
26+
const href = $el.attr('href') || '';
27+
const img = $el.find('img');
28+
const cover =
29+
img.attr('data-src') || img.attr('data-lazy-src') || img.attr('src') || defaultCover;
30+
if (name && href) {
31+
novels.push({
32+
name,
33+
path: href.replace(this.site, ''),
34+
cover,
35+
});
36+
}
37+
});
38+
return novels;
39+
}
40+
41+
async popularNovels(
42+
page: number,
43+
{
44+
showLatestNovels,
45+
filters,
46+
}: Plugin.PopularNovelsOptions<typeof this.filters>,
47+
): Promise<Plugin.NovelItem[]> {
48+
let url = this.site;
49+
if (showLatestNovels) {
50+
url += 'new/';
51+
} else if (filters) {
52+
const range = filters.sortOptions?.value || 'week';
53+
url += `popular/?range=${range}&page=${page}`;
54+
} else {
55+
url += `popular/?range=week&page=${page}`;
56+
}
57+
const $ = await this.fetchCheerio(url);
58+
return this.parseNovels($);
59+
}
60+
61+
async parseNovel(novelUrl: string): Promise<Plugin.SourceNovel> {
62+
const url = new URL(novelUrl, this.site).toString();
63+
const $ = await this.fetchCheerio(url);
64+
65+
const novel: Plugin.SourceNovel = {
66+
path: novelUrl,
67+
name: $('h1').first().text().trim() || 'Untitled',
68+
cover:
69+
$('.lib-card__img img, .novel-card__img img, .summary_image img')
70+
.first()
71+
.attr('data-src') ||
72+
$('.lib-card__img img, .novel-card__img img, .summary_image img')
73+
.first()
74+
.attr('src') ||
75+
defaultCover,
76+
chapters: [],
77+
};
78+
79+
const statusEl = $('span.status-pill');
80+
if (statusEl.length) {
81+
const statusText = statusEl.text().trim();
82+
if (statusText.includes('مكتملة')) novel.status = 'Completed';
83+
else if (statusText.includes('مستمرة')) novel.status = 'Ongoing';
84+
else if (statusText.includes('متوقفة')) novel.status = 'On Hiatus';
85+
}
86+
87+
const authorEl = $('span:contains("مترجم")');
88+
if (authorEl.length) {
89+
novel.author = authorEl.text().replace(/.*:/, '').trim();
90+
}
91+
92+
const summaryEl = $('div.summary, .entry-content, .manga-excerpt, p:contains("القصة")');
93+
if (summaryEl.length) {
94+
novel.summary = summaryEl.text().trim();
95+
}
96+
97+
const genres: string[] = [];
98+
$('a[href*="manga-genre"], .genres-content a, .wp-manga-genre a').each((_, el) => {
99+
const genre = $(el).text().trim();
100+
if (genre) genres.push(genre);
101+
});
102+
if (genres.length) novel.genres = genres.join(', ');
103+
104+
const chapters: Plugin.ChapterItem[] = [];
105+
$('li.wp-manga-chapter, .chapter-item').each((i, el) => {
106+
const $el = $(el);
107+
const a = $el.find('a');
108+
const name = a.text().trim();
109+
const href = a.attr('href') || '';
110+
const date = $el.find('.chapter-release-date, span.chapter-time').text().trim();
111+
if (name && href) {
112+
chapters.push({
113+
name,
114+
path: href.replace(this.site, ''),
115+
releaseTime: date || null,
116+
chapterNumber: chapters.length + 1,
117+
});
118+
}
119+
});
120+
novel.chapters = chapters.reverse();
121+
122+
return novel;
123+
}
124+
125+
async parseChapter(chapterUrl: string): Promise<string> {
126+
const url = new URL(chapterUrl, this.site).toString();
127+
const $ = await this.fetchCheerio(url);
128+
const content =
129+
$('.reading-content, .text-left, .entry-content, .chapter-content').html() || '';
130+
return content || '<p>المحتوى غير متاح.</p>';
131+
}
132+
133+
async searchNovels(
134+
searchTerm: string,
135+
page: number,
136+
): Promise<Plugin.NovelItem[]> {
137+
const url = `${this.site}?s=${encodeURIComponent(searchTerm)}&post_type=wp-manga&page=${page}`;
138+
const $ = await this.fetchCheerio(url);
139+
return this.parseNovels($);
140+
}
141+
142+
filters = {
143+
sortOptions: {
144+
value: 'week',
145+
label: 'الترتيب حسب',
146+
options: [
147+
{ label: 'الأسبوع', value: 'week' },
148+
{ label: 'اليوم', value: 'day' },
149+
{ label: 'الشهر', value: 'month' },
150+
{ label: 'كل الوقت', value: 'all' },
151+
],
152+
type: FilterTypes.Picker,
153+
},
154+
} satisfies Filters;
155+
}
156+
157+
export default new Markazriwayat();

‎plugins/multisrc/madara/filters/markazriwayat.json‎

Lines changed: 0 additions & 224 deletions
This file was deleted.

‎plugins/multisrc/madara/sources.json‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -633,15 +633,6 @@
633633
"useNewChapterEndpoint": true
634634
}
635635
},
636-
{
637-
"id": "markazriwayat",
638-
"sourceSite": "https://markazriwayat.com/",
639-
"sourceName": "Markazriwayat",
640-
"options": {
641-
"lang": "Arabic",
642-
"useNewChapterEndpoint": true
643-
}
644-
},
645636
{
646637
"id": "lullobox",
647638
"sourceSite": "https://lullobox.com/",
39.1 KB
Loading

0 commit comments

Comments
 (0)