From 164a1af1b8a6f55a531da10eeb8f8fbf3b3578b2 Mon Sep 17 00:00:00 2001 From: Karol Date: Mon, 31 Mar 2025 15:17:12 +0200 Subject: [PATCH] Reaperscans (#1603) * add gzip and brotli support to FetchMode.PROXY mode * add reaperscans source * fix chapter ordering for reaperscans * fix reaperscans duplicated chapter content --- public/static/src/en/reaperscans/icon.png | Bin 0 -> 9101 bytes src/plugins/english/reaperscans.ts | 126 ++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 public/static/src/en/reaperscans/icon.png create mode 100644 src/plugins/english/reaperscans.ts diff --git a/public/static/src/en/reaperscans/icon.png b/public/static/src/en/reaperscans/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ee8eeb5b850034b2bc4bb89e2ddbf6df5a6b0304 GIT binary patch literal 9101 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 { + data: T; + meta: { + total: number; + per_page: number; + current_page: number; + last_page: number; + }; +} + +interface ReaperNovel { + title: string; + thumbnail: string; + series_slug: string; +} + +class ReaperScans implements Plugin.PluginBase { + id = 'reaperscans.com'; + name = 'Reaper Scans'; + version = '1.0.0'; + icon = 'src/en/reaperscans/icon.png'; + site = 'https://reaperscans.com'; + + async popularNovels(page: number): Promise { + return this.query(page); + } + + async parseNovel(novelPath: string): Promise { + const novelResp = await fetchApi(`${API_BASE}/series/${novelPath}`); + const novel: { + title: string; + thumbnail: string; + description: string; + tags: string[]; + rating: number; + status: string; + alternative_names: string; + author: string; + studio: string; + } = await novelResp.json(); + + const chaptersResp = await fetchApi( + `${API_BASE}/chapters/${novelPath}?perPage=${Number.MAX_SAFE_INTEGER}`, + ); + const chapters: { + chapter_slug: string; + chapter_name: string; + index: string; + created_at: string; + }[] = (await chaptersResp.json()).data; + + return { + name: novel.title, + cover: this.getCoverUrl(novel.thumbnail), + author: novel.author, + artist: novel.studio, + status: novel.status, + rating: novel.rating, + summary: novel.description, + genres: novel.tags.join(','), + path: novelPath, + chapters: chapters.reverse().map(chapter => ({ + path: `${novelPath}/${chapter.chapter_slug}`, + name: chapter.chapter_name, + chapterNumber: Number.parseFloat(chapter.index), + releaseTime: chapter.created_at.substring(0, 10), + })), + }; + } + + async parseChapter(chapterPath: string): Promise { + const result = await fetchApi(`${this.site}/series/${chapterPath}`, { + headers: { RSC: '1' }, + }); + const body = await result.text(); + return this.extractChapterContent(body); + } + + private extractChapterContent(chapter: string): string { + const content = chapter + .split('\n') + .find(e => e.substring(0, 50).includes('') + 1, + ); + } + + async searchNovels(searchTerm: string): Promise { + return this.query(1, searchTerm); + } + + private async query(page = 1, search = ''): Promise { + const link = `${API_BASE}/query?page=${page}&perPage=20&series_type=Novel&query_string=${search}&order=desc&orderBy=created_at&adult=true&status=All&tags_ids=[]`; + const result = await fetchApi(link); + const json: ApiResponse = await result.json(); + + return json.data.map(novel => ({ + name: novel.title, + cover: novel.thumbnail.startsWith('novels/') + ? MEDIA_BASE + novel.thumbnail + : novel.thumbnail, + path: novel.series_slug, + })); + } + + private getCoverUrl(thumbnail: string): string { + return thumbnail.startsWith('novels/') ? MEDIA_BASE + thumbnail : thumbnail; + } +} + +export default new ReaperScans();