-
Notifications
You must be signed in to change notification settings - Fork 321
feat(ar/rewayahfans): add روايه فانز plugin #2306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
hamedhani1998
wants to merge
11
commits into
lnreader:master
Choose a base branch
from
hamedhani1998:feat-rewayahfans
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+234
−0
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
9993885
feat(ar/rewayahfans): add روايه فانز plugin
13d6a29
Add files via upload
hamedhani1998 fd16c78
Delete public/static/src/ar/rewayahfans/resized-icon (4).jpg
hamedhani1998 0042ebf
Add files via upload
hamedhani1998 b562fd4
Delete public/static/src/ar/rewayahfans/icon.jpg
hamedhani1998 221931d
Add files via upload
hamedhani1998 b414dc9
Merge branch 'master' into feat-rewayahfans
hamedhani1998 0cc97e0
Apply suggestion from @K1ngfish3r
hamedhani1998 1a9233e
Update rewayahfans.ts
hamedhani1998 bd2cb75
fix: clean up rewayahfans plugin after review feedback
f6a9df2
fix(ar/rewayahfans): fix all reviewer issues
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,234 @@ | ||
| import { load as parseHTML } from 'cheerio'; | ||
| import { fetchApi } from '@libs/fetch'; | ||
| import { Plugin } from '@/types/plugin'; | ||
|
|
||
| type WPPage = { | ||
| title: { rendered: string }; | ||
| slug: string; | ||
| date: string; | ||
| content?: { rendered: string }; | ||
| _embedded?: { | ||
| 'wp:featuredmedia'?: { source_url: string }[]; | ||
| }; | ||
| }; | ||
|
|
||
| class RewayahFans implements Plugin.PluginBase { | ||
| id = 'rewayahfans'; | ||
| name = 'روايه فانز'; | ||
| version = '1.0.0'; | ||
| icon = 'src/ar/rewayahfans/icon.png'; | ||
| site = 'https://rewayahfans.net/'; | ||
|
|
||
| private allNovels: Plugin.NovelItem[] = []; | ||
|
|
||
| private async fetchJson<T>(url: string): Promise<T> { | ||
| const res = await fetchApi(url); | ||
| if (!res.ok) throw new Error(`Request failed: ${res.status}`); | ||
| return res.json() as Promise<T>; | ||
| } | ||
|
|
||
| private async fetchHtml(url: string): Promise<string> { | ||
| const res = await fetchApi(url); | ||
| if (!res.ok) throw new Error(`Request failed: ${res.status}`); | ||
| return res.text(); | ||
| } | ||
|
|
||
| private async loadAllNovels(): Promise<Plugin.NovelItem[]> { | ||
| if (this.allNovels.length > 0) return this.allNovels; | ||
|
|
||
| const html = await this.fetchHtml( | ||
| `${this.site}%d9%82%d8%a7%d8%a6%d9%85%d8%a9-%d8%a7%d9%84%d8%b1%d9%88%d8%a7%d9%8a%d8%a7%d8%aa/`, | ||
| ); | ||
| const $ = parseHTML(html); | ||
| const novels: Plugin.NovelItem[] = []; | ||
| const seen = new Set<string>(); | ||
|
|
||
| $('figure.wp-block-image').each((_, el) => { | ||
| const fig = $(el); | ||
| const linkEl = fig.find('figcaption a').first(); | ||
| const href = | ||
| linkEl.attr('href') || fig.find('a').first().attr('href') || ''; | ||
| const name = linkEl.text().trim(); | ||
| const cover = fig.find('img').attr('src') || ''; | ||
| const decodedCover = cover.replace(/&/g, '&').replace(/&/g, '&'); | ||
|
|
||
| if (name && href) { | ||
| const path = href.replace(this.site, '').replace(/\/$/, ''); | ||
| if (!seen.has(path)) { | ||
| seen.add(path); | ||
| novels.push({ name, path, cover: decodedCover }); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| this.allNovels = novels; | ||
| return novels; | ||
| } | ||
|
|
||
| async popularNovels( | ||
| page: number, | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| _options: Plugin.PopularNovelsOptions, | ||
| ): Promise<Plugin.NovelItem[]> { | ||
| const allNovels = await this.loadAllNovels(); | ||
| if (page > 1) return []; | ||
| return allNovels; | ||
| } | ||
|
|
||
| async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> { | ||
| const novel: Plugin.SourceNovel = { | ||
| path: novelPath, | ||
| name: '', | ||
| cover: '', | ||
| summary: '', | ||
| author: '', | ||
| genres: '', | ||
| status: '', | ||
| chapters: [], | ||
| }; | ||
|
|
||
| const html = await this.fetchHtml(`${this.site}${novelPath}`); | ||
| const $ = parseHTML(html); | ||
|
|
||
| const titleTag = $('title').text().trim(); | ||
| novel.name = | ||
| titleTag.split(' - ')[0].trim() || | ||
| titleTag.split('\u2013')[0].trim() || | ||
| 'بدون عنوان'; | ||
|
|
||
| novel.name = this.extractNovelName(novel.name); | ||
|
|
||
| const ogImage = $('meta[property="og:image"]').attr('content') || ''; | ||
| novel.cover = ogImage || ''; | ||
|
|
||
| const summaryParts: string[] = []; | ||
| let inStory = false; | ||
| let inChapters = false; | ||
| $('.entry-content > *').each((_, el) => { | ||
| const $el = $(el); | ||
| const tag = $el.prop('tagName')?.toLowerCase() || ''; | ||
| const text = $el.text().trim(); | ||
|
|
||
| if (tag === 'p' && text.startsWith('القصة')) { | ||
| inStory = true; | ||
| return; | ||
| } | ||
| if (inStory) { | ||
| if (tag === 'p' && text.startsWith('الفصول')) { | ||
| inChapters = true; | ||
| return false; | ||
| } | ||
| if (tag === 'p' && text && !inChapters) { | ||
| summaryParts.push(text); | ||
| } | ||
| } | ||
| }); | ||
| novel.summary = summaryParts.join('\n') || ''; | ||
|
|
||
| const metadataMap: Record<string, string> = {}; | ||
| $('ul.wp-block-list li').each((_, el) => { | ||
| const text = $(el).text().trim(); | ||
| const colonIdx = text.indexOf(':'); | ||
| if (colonIdx > 0) { | ||
| const key = text.substring(0, colonIdx).trim(); | ||
| const value = text.substring(colonIdx + 1).trim(); | ||
| metadataMap[key] = value; | ||
| } | ||
| }); | ||
|
|
||
| novel.author = metadataMap['المؤلف'] || ''; | ||
| novel.genres = metadataMap['التصنيفات'] || ''; | ||
| novel.status = metadataMap['الحالة'] || ''; | ||
|
|
||
| const chapterSet = new Set<string>(); | ||
|
|
||
| const chapterSection = $( | ||
| 'p:contains("الفصول"), p:contains("Chapters"), p:contains("Fichier"), p:contains("Capitulos"), p:contains("Capítulos"), p:contains("Chapitres"), p:contains("Kapitel"), p:contains("Файл"), p:contains("Глава"), p:contains("章")', | ||
| ); | ||
|
|
||
| if (chapterSection.length > 0) { | ||
| chapterSection.nextAll().each((_, el) => { | ||
| const $el = $(el); | ||
| if ($el.hasClass('wp-block-paragraph') || $el.is('p')) { | ||
| $el.find('a').each((_, aEl) => { | ||
| const href = $(aEl).attr('href') || ''; | ||
| const text = $(aEl).text().trim(); | ||
| if (!href || !text) return; | ||
| if (!href.startsWith(this.site)) return; | ||
| const chapterPath = href.replace(this.site, '').replace(/\/$/, ''); | ||
| if (chapterPath === novelPath) return; | ||
| if (chapterSet.has(chapterPath)) return; | ||
| const numMatch = text.match(/(\d+)/); | ||
| if (!numMatch) return; | ||
| const chapterNum = parseInt(numMatch[1], 10); | ||
| if (chapterNum === 0) return; | ||
| const cleanName = text.replace(/\bnew\b/i, '').trim(); | ||
| if (cleanName.includes('النهاية')) return; | ||
| if (cleanName.includes('اضغط هنا')) return; | ||
| if (cleanName.includes('Summary')) return; | ||
| chapterSet.add(chapterPath); | ||
| novel.chapters!.push({ | ||
| name: cleanName, | ||
| path: chapterPath, | ||
| chapterNumber: chapterNum, | ||
| }); | ||
| }); | ||
| } else { | ||
| return false; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| novel.chapters!.sort( | ||
| (a, b) => (a.chapterNumber || 0) - (b.chapterNumber || 0), | ||
| ); | ||
|
|
||
| if (!novel.name && novel.chapters!.length > 0) { | ||
| novel.name = this.extractNovelName(novel.chapters![0].name); | ||
| } | ||
|
|
||
| return novel; | ||
| } | ||
|
|
||
| async parseChapter(chapterPath: string): Promise<string> { | ||
| const pages = await this.fetchJson<WPPage[]>( | ||
| `${this.site}wp-json/wp/v2/pages?slug=${chapterPath}&_fields=content`, | ||
| ); | ||
|
|
||
| const arr = Array.isArray(pages) ? pages : [pages]; | ||
| if (arr.length > 0 && arr[0].content?.rendered) { | ||
| const $ = parseHTML(arr[0].content.rendered); | ||
| $( | ||
| 'script, style, .sharedaddy, .jp-relatedposts, .wp-block-spacer, .simplefavorite-button, .wp-block-jetpack-rating-star', | ||
| ).remove(); | ||
| return $.html(); | ||
| } | ||
|
|
||
| const html = await this.fetchHtml(`${this.site}${chapterPath}/`); | ||
| const $ = parseHTML(html); | ||
| const content = | ||
| $('article .entry-content, .post-content, .entry-content').html() || ''; | ||
| return content || '<p>المحتوى غير متاح.</p>'; | ||
| } | ||
|
|
||
| async searchNovels( | ||
| searchTerm: string, | ||
| page: number, | ||
| ): Promise<Plugin.NovelItem[]> { | ||
| const allNovels = await this.loadAllNovels(); | ||
| const lower = searchTerm.toLowerCase(); | ||
| const filtered = allNovels.filter(n => | ||
| n.name.toLowerCase().includes(lower), | ||
| ); | ||
| const perPage = 20; | ||
| const start = (page - 1) * perPage; | ||
| return filtered.slice(start, start + perPage); | ||
| } | ||
|
|
||
| private extractNovelName(title: string): string { | ||
| const match = title.match(/^(.+?)\s+\d+$/); | ||
| return match ? match[1].trim() : title.trim(); | ||
| } | ||
| } | ||
|
|
||
| export default new RewayahFans(); | ||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.