import { fetchApi } from '@libs/fetch'; import { Plugin } from '@/types/plugin'; import { Filters } from '@libs/filterInputs'; import { NovelStatus } from '@libs/novelStatus'; class FictionZonePlugin implements Plugin.PluginBase { id = 'fictionzone'; name = 'Fiction Zone'; icon = 'src/en/fictionzone/icon.png'; site = 'https://fictionzone.net'; version = '1.0.2'; filters: Filters | undefined = undefined; async popularNovels( pageNo: number, { showLatestNovels, // filters, }: Plugin.PopularNovelsOptions, ): Promise { return await this.getPage( `/platform/browse?page=${pageNo}&page_size=20&sort_by=${showLatestNovels ? 'created_at' : 'bookmark_count'}&sort_order=desc&include_genres=true`, ); } // TODO: Fix URLs, current URL leads to 404 async getData(url: string): Promise> { return await fetchApi(this.site + '/api/__api_party/fictionzone', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ 'path': url, 'headers': [ ['content-type', 'application/json'], ['x-request-time', new Date().toISOString()], ], 'method': 'GET', }), }).then(r => r.json()); } async getPage(url: string): Promise { const data = await this.getData<{ novels: NovelItem[] }>(url); return data.data.novels.map(n => ({ name: n.title, cover: `https://cdn.fictionzone.net/insecure/rs:fill:165:250/${n.image}.webp`, path: `novel/${n.slug}`, })); } async getChapterPage( id: string, novelPath: string, ): Promise { const data = await this.getData<{ chapters: ChapterItem[] }>( '/platform/chapter-lists?novel_id=' + id, ); return data.data.chapters.map(n => ({ name: n.title, number: n.chapter_number, date: n.published_date ? new Date(n.published_date).toISOString() : undefined, path: `${novelPath}/${n.chapter_id}|/platform/chapter-content?novel_id=${id}&chapter_id=${n.chapter_id}`, })); } async parseNovel(novelPath: string): Promise { const novelSlug = novelPath.replace('novel/', ''); const data = await this.getData( `/platform/novel-details?slug=${novelSlug}`, ); return { path: novelPath, name: data.data.title, cover: `https://cdn.fictionzone.net/insecure/rs:fill:165:250/${data.data.image}.webp`, genres: [ ...data.data.genres.map(g => g.name), ...data.data.tags.map(g => g.name), ].join(','), status: data.data.status == 1 ? NovelStatus.Ongoing : data.data.status == 0 ? NovelStatus.Completed : NovelStatus.Unknown, author: data.data.contributors.filter(c => c.role == 'author')[0] ?.display_name || '', summary: data.data.synopsis, chapters: await this.getChapterPage(data.data.id, novelPath), }; } async parseChapter(chapterPath: string): Promise { const data = await this.getData<{ content: string }>( chapterPath.split('|')[1], ); return '

' + data.data.content.split('\n').join('

') + '

'; } async searchNovels( searchTerm: string, pageNo: number, ): Promise { return await this.getPage( `/platform/browse?search=${encodeURIComponent(searchTerm)}&page=${pageNo}&page_size=20&search_in_synopsis=true&sort_by=bookmark_count&sort_order=desc&include_genres=true`, ); } // resolveUrl = (path: string, isNovel?: boolean) => // this.site + '/' + path.split('|')[0]; } export default new FictionZonePlugin(); type Response = { data: T; }; type NovelItem = { title: string; image: string; slug: string; }; type ChapterItem = { title: string; chapter_number: number; published_date: string; chapter_id: string; }; type NovelDetails = { id: string; title: string; image: string; genres: { name: string }[]; tags: { name: string }[]; status: number; contributors: { role: string; display_name: string }[]; synopsis: string; };