Skip to content

Commit da990de

Browse files
committed
fix: refactor novelfire popular,search and summary
1 parent 2871f50 commit da990de

1 file changed

Lines changed: 95 additions & 99 deletions

File tree

plugins/english/novelfire.ts

Lines changed: 95 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import { storage } from '@libs/storage';
99
class NovelFire implements Plugin.PluginBase {
1010
id = 'novelfire';
1111
name = 'Novel Fire';
12-
version = '1.1.9';
12+
version = '1.2.0';
1313
icon = 'src/en/novelfire/icon.png';
1414
site = 'https://novelfire.net/';
1515

16-
novelList = [];
16+
novelList: string[] = [];
1717

1818
singlePage = storage.get('singlePage');
1919
pluginSettings = {
@@ -40,63 +40,29 @@ class NovelFire implements Plugin.PluginBase {
4040
return $;
4141
}
4242

43-
async popularNovels(
44-
pageNo: number,
45-
{
46-
showLatestNovels,
47-
filters,
48-
}: Plugin.PopularNovelsOptions<typeof this.filters>,
49-
): Promise<Plugin.NovelItem[]> {
50-
if (pageNo == 1) {
51-
this.novelList = [];
52-
}
53-
let url = this.site + 'search-adv';
54-
if (showLatestNovels) {
55-
url += `?ctgcon=and&totalchapter=0&ratcon=min&rating=0&status=-1&sort=date&tagcon=and&page=${pageNo}`;
56-
} else if (filters) {
57-
const params = new URLSearchParams();
58-
for (const language of filters.language.value) {
59-
params.append('country_id[]', language);
60-
}
61-
params.append('ctgcon', filters.genre_operator.value);
62-
for (const genre of filters.genres.value) {
63-
params.append('categories[]', genre);
64-
}
65-
params.append('totalchapter', filters.chapters.value);
66-
params.append('ratcon', filters.rating_operator.value);
67-
params.append('rating', filters.rating.value);
68-
params.append('status', filters.status.value);
69-
params.append('sort', filters.sort.value);
70-
params.append('page', pageNo.toString());
71-
url += `?${params.toString()}`;
72-
} else {
73-
url += `?ctgcon=and&totalchapter=0&ratcon=min&rating=0&status=-1&sort=rank-top&page=${pageNo}`;
74-
}
75-
76-
const loadedCheerio = await this.getCheerio(url, false);
43+
parseNovels(
44+
loadedCheerio: CheerioAPI,
45+
selector = '.novel-item',
46+
): Plugin.NovelItem[] {
47+
return loadedCheerio(selector)
48+
.map((_, el) => {
49+
const titleElement = loadedCheerio(el).find('.novel-title > a');
50+
const fallbackElement = loadedCheerio(el).find('a');
7751

78-
return loadedCheerio('.novel-item')
79-
.map((index, ele) => {
8052
const novelName =
81-
loadedCheerio(ele).find('.novel-title > a').text() ||
53+
titleElement.text() ||
54+
fallbackElement.attr('title') ||
8255
'No Title Found';
56+
57+
const imgElement = loadedCheerio(el).find('.novel-cover > img');
8358
const novelCover =
8459
this.site +
85-
deSlash(
86-
loadedCheerio(ele).find('.novel-cover > img').attr('data-src') ||
87-
'',
88-
);
89-
const novelPath = loadedCheerio(ele)
90-
.find('.novel-title > a')
91-
.attr('href');
60+
deSlash(imgElement.attr('data-src') || imgElement.attr('src') || '');
9261

93-
if (!novelPath) return;
62+
const novelPath =
63+
titleElement.attr('href') || fallbackElement.attr('href');
9464

95-
if (this.novelList.includes(novelPath)) {
96-
return;
97-
} else {
98-
this.novelList.push(novelPath);
99-
}
65+
if (!novelPath) return null;
10066

10167
return {
10268
name: novelName,
@@ -105,15 +71,57 @@ class NovelFire implements Plugin.PluginBase {
10571
};
10672
})
10773
.get()
108-
.filter(novel => novel !== null);
74+
.filter(novel => novel !== null)
75+
.filter(novel => {
76+
if (this.novelList.includes(novel.path)) {
77+
return false;
78+
} else {
79+
this.novelList.push(novel.path);
80+
return true;
81+
}
82+
});
83+
}
84+
85+
async popularNovels(
86+
pageNo: number,
87+
{
88+
showLatestNovels,
89+
filters,
90+
}: Plugin.PopularNovelsOptions<typeof this.filters>,
91+
): Promise<Plugin.NovelItem[]> {
92+
if (pageNo === 1) {
93+
this.novelList = [];
94+
}
95+
const url = this.site + 'search-adv';
96+
const params = new URLSearchParams();
97+
98+
for (const language of filters.language.value) {
99+
params.append('country_id[]', language);
100+
}
101+
params.append('ctgcon', filters.genre_operator.value);
102+
for (const genre of filters.genres.value) {
103+
params.append('categories[]', genre);
104+
}
105+
params.append('totalchapter', filters.chapters.value);
106+
params.append('ratcon', filters.rating_operator.value);
107+
params.append('rating', filters.rating.value);
108+
params.append('status', filters.status.value);
109+
params.append('sort', showLatestNovels ? 'date' : filters.sort.value);
110+
params.append('tagcon', 'and');
111+
params.append('page', pageNo.toString());
112+
113+
const loadedCheerio = await this.getCheerio(
114+
`${url}?${params.toString()}`,
115+
false,
116+
);
117+
118+
return this.parseNovels(loadedCheerio);
109119
}
110120

111121
async getAllChapters(
112122
novelPath: string,
113123
post_id: string,
114124
): Promise<Plugin.ChapterItem[]> {
115-
const allChapters: Plugin.ChapterItem[] = [];
116-
117125
const url = `${this.site}listChapterDataAjax?post_id=${post_id}`;
118126
const result = await fetchApi(url);
119127
const body = await result.text();
@@ -128,7 +136,7 @@ class NovelFire implements Plugin.PluginBase {
128136

129137
const json = JSON.parse(body);
130138
const chapters = json.data
131-
.map(index => {
139+
.map((index: { title?: string; slug: string; n_sort: number }) => {
132140
const chapterName = load(index.title || index.slug).text();
133141
const chapterPath = `${novelPath}/chapter-${index.n_sort}`;
134142
const sortNumber = index.n_sort;
@@ -141,9 +149,11 @@ class NovelFire implements Plugin.PluginBase {
141149
chapterNumber: Number(sortNumber),
142150
};
143151
})
144-
.filter(chapter => chapter !== null) as Plugin.ChapterItem[];
152+
.filter(
153+
(chapter: Plugin.ChapterItem | null) => chapter !== null,
154+
) as Plugin.ChapterItem[];
145155
const sortedChapters = chapters.sort(function (a, b) {
146-
return a.chapterNumber - b.chapterNumber;
156+
return (a.chapterNumber || 0) - (b.chapterNumber || 0);
147157
});
148158

149159
return sortedChapters;
@@ -161,7 +171,7 @@ class NovelFire implements Plugin.PluginBase {
161171
const retryCount = 10;
162172
const sleepTime = 3.5; // Rate limit seems to be around ~10s, so usually 3 retries should be enough for another ~30 pages.
163173

164-
const chaptersArray: Plugin.ChapterItem[][] = [];
174+
const chaptersArray: Plugin.SourcePage[] = [];
165175

166176
for (let i = 0; i < pagesArray.length; i += chunkSize) {
167177
const pagesArrayChunk = pagesArray.slice(i, i + chunkSize);
@@ -202,11 +212,11 @@ class NovelFire implements Plugin.PluginBase {
202212
}
203213

204214
// Merge all chapters into a single array
205-
for (let chapters of chaptersArray) {
206-
// For some reason it's formatted this way, this fixes it.
207-
chapters = chapters.chapters;
208-
for (let i = 0; i < Object.keys(chapters).length; i++) {
209-
allChapters.push(chapters[i]);
215+
for (const page of chaptersArray) {
216+
if (page.chapters) {
217+
for (const chapter of page.chapters) {
218+
allChapters.push(chapter);
219+
}
210220
}
211221
}
212222
return allChapters;
@@ -244,19 +254,19 @@ class NovelFire implements Plugin.PluginBase {
244254
.toArray()
245255
.join(',');
246256

247-
let summary = $('.summary .content')
248-
.find('br')
249-
.replaceWith('\n')
250-
.end()
251-
.text()
252-
.trim();
257+
const summary = $('.summary .content');
258+
summary.find('.expand').remove();
259+
summary.find('br').replaceWith('\n');
260+
summary.find('p').before('\n').after('\n\n');
253261

254-
if (summary) {
255-
summary = summary.replace('Show More', '');
256-
novel.summary = summary;
257-
} else {
258-
novel.summary = 'No Summary Found';
259-
}
262+
novel.summary =
263+
summary
264+
.text()
265+
.split('\n')
266+
.map(line => line.trim())
267+
.join('\n')
268+
?.replace(/\n{3,}/g, '\n\n')
269+
.trim() || 'Summary Not Found';
260270

261271
novel.author = $('.author .property-item > span').text();
262272

@@ -291,7 +301,7 @@ class NovelFire implements Plugin.PluginBase {
291301
novelPath,
292302
novel.totalPages,
293303
);
294-
if (novel.totalPages > 1 && novel.chapters.length > 100) {
304+
if (novel.totalPages > 1 && novel.chapters.length > 50) {
295305
novel.totalPages = 1;
296306
}
297307
}
@@ -350,33 +360,19 @@ class NovelFire implements Plugin.PluginBase {
350360
searchTerm: string,
351361
page: number,
352362
): Promise<Plugin.NovelItem[]> {
353-
const url = `${this.site}search?keyword=${encodeURIComponent(searchTerm)}&page=${page}`;
363+
if (page === 1) {
364+
this.novelList = [];
365+
}
366+
const params = new URLSearchParams();
367+
params.append('keyword', searchTerm);
368+
params.append('page', page.toString());
369+
const url = `${this.site}search?${params.toString()}`;
354370
const result = await fetchApi(url);
355371
const body = await result.text();
356372

357373
const loadedCheerio = load(body);
358374

359-
return loadedCheerio('.novel-list.chapters .novel-item')
360-
.map((index, ele) => {
361-
const novelName =
362-
loadedCheerio(ele).find('a').attr('title') || 'No Title Found';
363-
const novelCover =
364-
this.site +
365-
deSlash(
366-
loadedCheerio(ele).find('.novel-cover > img').attr('src') || '',
367-
);
368-
const novelPath = loadedCheerio(ele).find('a').attr('href');
369-
370-
if (!novelPath) return null;
371-
372-
return {
373-
name: novelName,
374-
cover: novelCover,
375-
path: deSlash(novelPath.replace(this.site, '')),
376-
};
377-
})
378-
.get()
379-
.filter(novel => novel !== null);
375+
return this.parseNovels(loadedCheerio, '.novel-list.chapters .novel-item');
380376
}
381377

382378
filters = {

0 commit comments

Comments
 (0)