Skip to content

Commit e35fffd

Browse files
authored
fix: Novelight Browse + Chapter Parse + Status Parse (#1934)
* Feat (novelight): Browse latest + filters * Fix (novelight): Catch 403 errors * Fix (novelight): Parse with catching * Fix (novelight): Parse status
1 parent 6332d37 commit e35fffd

1 file changed

Lines changed: 159 additions & 11 deletions

File tree

plugins/english/novelight.ts

Lines changed: 159 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import { load as parseHTML } from 'cheerio';
22
import { fetchApi } from '@libs/fetch';
33
import { Plugin } from '@/types/plugin';
4+
import { NovelStatus } from '@libs/novelStatus';
5+
import { Filters, FilterTypes } from '@libs/filterInputs';
46
import { defaultCover } from '@libs/defaultCover';
57
import dayjs from 'dayjs';
68
import { storage } from '@libs/storage';
7-
import { NovelStatus } from '@libs/novelStatus';
89

910
class Novelight implements Plugin.PagePlugin {
1011
id = 'novelight';
1112
name = 'Novelight';
12-
version = '1.1.1';
13+
version = '1.1.2';
1314
icon = 'src/en/novelight/icon.png';
1415
site = 'https://novelight.net/';
1516

@@ -22,8 +23,39 @@ class Novelight implements Plugin.PagePlugin {
2223
},
2324
};
2425

25-
async popularNovels(page: number): Promise<Plugin.NovelItem[]> {
26-
const url = `${this.site}catalog/?ordering=popularity&page=${page}`;
26+
async popularNovels(
27+
pageNo: number,
28+
{
29+
showLatestNovels,
30+
filters,
31+
}: Plugin.PopularNovelsOptions<typeof this.filters>,
32+
): Promise<Plugin.NovelItem[]> {
33+
let url = `${this.site}catalog/`;
34+
if (showLatestNovels) {
35+
url += `?ordering=-time_updated&page=${pageNo}`;
36+
} else if (filters) {
37+
const params = new URLSearchParams();
38+
for (const country of filters.country.value) {
39+
params.append('country', country);
40+
}
41+
for (const genre of filters.genres.value) {
42+
params.append('genre', genre);
43+
}
44+
for (const translation of filters.translation.value) {
45+
params.append('translation', translation);
46+
}
47+
for (const status of filters.status.value) {
48+
params.append('status', status);
49+
}
50+
for (const novel_type of filters.novel_type.value) {
51+
params.append('type', novel_type);
52+
}
53+
params.append('ordering', filters.sort.value);
54+
params.append('page', pageNo.toString());
55+
url += `?${params.toString()}`;
56+
} else {
57+
url += `?&ordering=popularity&page=${pageNo}`;
58+
}
2759

2860
const body = await fetchApi(url).then(r => r.text());
2961

@@ -74,10 +106,18 @@ class Novelight implements Plugin.PagePlugin {
74106
for (const child of info) {
75107
const type = loadedCheerio(child).find('.sub-header').text().trim();
76108
if (type === 'Status') {
77-
status = loadedCheerio(child).find('div.info').text().trim();
109+
status = loadedCheerio(child)
110+
.find('div.info')
111+
.text()
112+
.trim()
113+
.toLowerCase();
78114
}
79115
if (type === 'Translation') {
80-
translation = loadedCheerio(child).find('div.info').text().trim();
116+
translation = loadedCheerio(child)
117+
.find('div.info')
118+
.text()
119+
.trim()
120+
.toLowerCase();
81121
}
82122
if (type === 'Author') {
83123
novel.author = loadedCheerio(child).find('div.info').text().trim();
@@ -110,18 +150,26 @@ class Novelight implements Plugin.PagePlugin {
110150
?.match(/([0-9]+)/)?.[1] ?? '1',
111151
);
112152

113-
const chaptersRaw = await fetchApi(
114-
`${this.site}/book/ajax/chapter-pagination?csrfmiddlewaretoken=${csrftoken}&book_id=${bookId}&page=${totalPages - parseInt(page) + 1}`,
153+
const r = await fetchApi(
154+
`${this.site}book/ajax/chapter-pagination?csrfmiddlewaretoken=${csrftoken}&book_id=${bookId}&page=${totalPages - parseInt(page) + 1}`,
115155
{
116156
headers: {
117157
'Host': this.site.replace('https://', '').replace('/', ''),
118158
'Referer': this.site + novelPath,
119159
'X-Requested-With': 'XMLHttpRequest',
120160
},
121161
},
122-
)
123-
.then(r => r.json())
124-
.then(r => r.html);
162+
);
163+
164+
let chaptersRaw;
165+
try {
166+
chaptersRaw = await r.json();
167+
chaptersRaw = chaptersRaw.html;
168+
} catch (error) {
169+
console.error('Error Parsing Response');
170+
console.error(error);
171+
throw new Error(error);
172+
}
125173

126174
const chapter: Plugin.ChapterItem[] = [];
127175

@@ -214,6 +262,106 @@ class Novelight implements Plugin.PagePlugin {
214262

215263
return novels;
216264
}
265+
266+
filters = {
267+
sort: {
268+
label: 'Sort Results By',
269+
value: 'popularity',
270+
options: [
271+
{ label: 'Title (A>Z)', value: 'title' },
272+
{ label: 'Publication Date', value: '-time_created' },
273+
{ label: 'Update Date (Newest)', value: '-time_updated' },
274+
{ label: 'Year Release', value: '-year_of_release' },
275+
{ label: 'Popularity', value: 'popularity' },
276+
],
277+
type: FilterTypes.Picker,
278+
},
279+
translation: {
280+
label: 'Translation Status',
281+
value: [],
282+
options: [
283+
{ label: 'Ongoing', value: 'ongoing' },
284+
{ label: 'Completed', value: 'completed' },
285+
{ label: 'Paused', value: 'paused' },
286+
{ label: 'Dropped', value: 'dropped' },
287+
{ label: 'None', value: 'none' },
288+
],
289+
type: FilterTypes.CheckboxGroup,
290+
},
291+
status: {
292+
label: 'Status',
293+
value: [],
294+
options: [
295+
{ label: 'Releasing', value: 'releasing' },
296+
{ label: 'Completed', value: 'completed' },
297+
{ label: 'Cancelled', value: 'cancelled' },
298+
{ label: 'Not yet released', value: 'not+yet+released' },
299+
],
300+
type: FilterTypes.CheckboxGroup,
301+
},
302+
novel_type: {
303+
label: 'Type',
304+
value: [],
305+
options: [
306+
{ label: 'Fan Fiction', value: '4' },
307+
{ label: 'Light Novel', value: '1' },
308+
{ label: 'Published Novel', value: '2' },
309+
{ label: 'Web Novel', value: '3' },
310+
],
311+
type: FilterTypes.CheckboxGroup,
312+
},
313+
genres: {
314+
label: 'Genres',
315+
value: [],
316+
options: [
317+
{ label: 'Thriller', value: '1' },
318+
{ label: 'Supernatural', value: '2' },
319+
{ label: 'Sports', value: '3' },
320+
{ label: 'Slice of Life', value: '4' },
321+
{ label: 'Sci-Fi', value: '5' },
322+
{ label: 'Romance', value: '6' },
323+
{ label: 'Psychological', value: '7' },
324+
{ label: 'Mystery', value: '8' },
325+
{ label: 'Mecha', value: '9' },
326+
{ label: 'Horror', value: '10' },
327+
{ label: 'Fantasy', value: '11' },
328+
{ label: 'Ecchi', value: '12' },
329+
{ label: 'Drama', value: '13' },
330+
{ label: 'Comedy', value: '14' },
331+
{ label: 'Adventure', value: '15' },
332+
{ label: 'Action', value: '16' },
333+
{ label: 'Adult', value: '17' },
334+
{ label: 'Isekai', value: '18' },
335+
{ label: 'Wuxia', value: '19' },
336+
{ label: 'Shounen', value: '20' },
337+
{ label: 'Yuri', value: '21' },
338+
{ label: 'Shoujo', value: '22' },
339+
{ label: 'Shoujo Ai', value: '23' },
340+
{ label: 'Harem', value: '24' },
341+
{ label: 'Seinen', value: '25' },
342+
{ label: 'Tragedy', value: '26' },
343+
{ label: 'Mature', value: '27' },
344+
{ label: 'Martial Arts', value: '28' },
345+
{ label: 'Gender Bender', value: '29' },
346+
{ label: 'School Life', value: '30' },
347+
{ label: 'Xuanhuan', value: '31' },
348+
{ label: 'Yaoi', value: '32' },
349+
{ label: 'Historical', value: '33' },
350+
],
351+
type: FilterTypes.CheckboxGroup,
352+
},
353+
country: {
354+
label: 'Country',
355+
value: [],
356+
options: [
357+
{ label: 'China', value: '1' },
358+
{ label: 'Japan', value: '2' },
359+
{ label: 'Korea', value: '3' },
360+
{ label: 'Other', value: '6' },
361+
],
362+
type: FilterTypes.CheckboxGroup,
363+
},
364+
} satisfies Filters;
217365
}
218366

219367
export default new Novelight();

0 commit comments

Comments
 (0)