Fix special characters breaking search in most sources (#1557)
This commit is contained in:
@@ -25,7 +25,7 @@ class IfreedomPlugin implements Plugin.PluginBase {
|
|||||||
this.name = metadata.sourceName;
|
this.name = metadata.sourceName;
|
||||||
this.icon = `multisrc/ifreedom/${metadata.id.toLowerCase()}/icon.png`;
|
this.icon = `multisrc/ifreedom/${metadata.id.toLowerCase()}/icon.png`;
|
||||||
this.site = metadata.sourceSite;
|
this.site = metadata.sourceSite;
|
||||||
this.version = '1.0.1';
|
this.version = '1.0.2';
|
||||||
this.filters = metadata.filters;
|
this.filters = metadata.filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,11 @@ class IfreedomPlugin implements Plugin.PluginBase {
|
|||||||
page: number | undefined = 1,
|
page: number | undefined = 1,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url =
|
const url =
|
||||||
this.site + '/vse-knigi/?searchname=' + searchTerm + '&bpage=' + page;
|
this.site +
|
||||||
|
'/vse-knigi/?searchname=' +
|
||||||
|
encodeURIComponent(searchTerm) +
|
||||||
|
'&bpage=' +
|
||||||
|
page;
|
||||||
const result = await fetchApi(url).then(res => res.text());
|
const result = await fetchApi(url).then(res => res.text());
|
||||||
const loadedCheerio = parseHTML(result);
|
const loadedCheerio = parseHTML(result);
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class LightNovelWPPlugin implements Plugin.PluginBase {
|
|||||||
this.icon = `multisrc/lightnovelwp/${metadata.id.toLowerCase()}/icon.png`;
|
this.icon = `multisrc/lightnovelwp/${metadata.id.toLowerCase()}/icon.png`;
|
||||||
this.site = metadata.sourceSite;
|
this.site = metadata.sourceSite;
|
||||||
const versionIncrements = metadata.options?.versionIncrements || 0;
|
const versionIncrements = metadata.options?.versionIncrements || 0;
|
||||||
this.version = `1.1.${6 + versionIncrements}`;
|
this.version = `1.1.${7 + versionIncrements}`;
|
||||||
this.options = metadata.options ?? ({} as LightNovelWPOptions);
|
this.options = metadata.options ?? ({} as LightNovelWPOptions);
|
||||||
this.filters = metadata.filters satisfies Filters;
|
this.filters = metadata.filters satisfies Filters;
|
||||||
|
|
||||||
@@ -103,7 +103,9 @@ class LightNovelWPPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
if (novelName && novelUrl) {
|
if (novelName && novelUrl) {
|
||||||
const novelCover =
|
const novelCover =
|
||||||
article.match(/<img [^>]*?src="([^\"]*)"[^>]*?(?: data-src="([^\"]*)")?[^>]*>/) || [];
|
article.match(
|
||||||
|
/<img [^>]*?src="([^\"]*)"[^>]*?(?: data-src="([^\"]*)")?[^>]*>/,
|
||||||
|
) || [];
|
||||||
|
|
||||||
let novelPath;
|
let novelPath;
|
||||||
if (novelUrl.includes(this.site)) {
|
if (novelUrl.includes(this.site)) {
|
||||||
@@ -444,7 +446,8 @@ class LightNovelWPPlugin implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url = this.site + 'page/' + page + '/?s=' + searchTerm;
|
const url =
|
||||||
|
this.site + 'page/' + page + '/?s=' + encodeURIComponent(searchTerm);
|
||||||
const html = await this.safeFecth(url, true);
|
const html = await this.safeFecth(url, true);
|
||||||
return this.parseNovels(html);
|
return this.parseNovels(html);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class MadaraPlugin implements Plugin.PluginBase {
|
|||||||
this.icon = `multisrc/madara/${metadata.id.toLowerCase()}/icon.png`;
|
this.icon = `multisrc/madara/${metadata.id.toLowerCase()}/icon.png`;
|
||||||
this.site = metadata.sourceSite;
|
this.site = metadata.sourceSite;
|
||||||
const versionIncrements = metadata.options?.versionIncrements || 0;
|
const versionIncrements = metadata.options?.versionIncrements || 0;
|
||||||
this.version = `1.0.${6 + versionIncrements}`;
|
this.version = `1.0.${7 + versionIncrements}`;
|
||||||
this.options = metadata.options;
|
this.options = metadata.options;
|
||||||
this.filters = metadata.filters;
|
this.filters = metadata.filters;
|
||||||
|
|
||||||
@@ -63,25 +63,40 @@ class MadaraPlugin implements Plugin.PluginBase {
|
|||||||
translateDragontea(text: Cheerio<AnyNode>): Cheerio<AnyNode> {
|
translateDragontea(text: Cheerio<AnyNode>): Cheerio<AnyNode> {
|
||||||
if (this.id !== 'dragontea') return text;
|
if (this.id !== 'dragontea') return text;
|
||||||
|
|
||||||
const $ = parseHTML(text.html()?.replace('\n', '').replace(/<br\s*\/?>/g, '\n') || '');
|
const $ = parseHTML(
|
||||||
|
text
|
||||||
|
.html()
|
||||||
|
?.replace('\n', '')
|
||||||
|
.replace(/<br\s*\/?>/g, '\n') || '',
|
||||||
|
);
|
||||||
const reverseAlpha = 'zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA';
|
const reverseAlpha = 'zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA';
|
||||||
const forwardAlpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
const forwardAlpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
|
||||||
text.html($.html());
|
text.html($.html());
|
||||||
text.find('*').addBack().contents().filter((_, el) => el.nodeType === 3).each((_, el) => {
|
text
|
||||||
const $el = $(el);
|
.find('*')
|
||||||
const translated = $el.text().normalize('NFD').split('')
|
.addBack()
|
||||||
.map(char => {
|
.contents()
|
||||||
const base = char.normalize('NFC');
|
.filter((_, el) => el.nodeType === 3)
|
||||||
const idx = forwardAlpha.indexOf(base);
|
.each((_, el) => {
|
||||||
return idx >= 0 ? reverseAlpha[idx] + char.slice(base.length) : char;
|
const $el = $(el);
|
||||||
})
|
const translated = $el
|
||||||
.join('');
|
.text()
|
||||||
$el.replaceWith(translated.replace('\n', '<br>'));
|
.normalize('NFD')
|
||||||
});
|
.split('')
|
||||||
|
.map(char => {
|
||||||
|
const base = char.normalize('NFC');
|
||||||
|
const idx = forwardAlpha.indexOf(base);
|
||||||
|
return idx >= 0
|
||||||
|
? reverseAlpha[idx] + char.slice(base.length)
|
||||||
|
: char;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
$el.replaceWith(translated.replace('\n', '<br>'));
|
||||||
|
});
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
getHostname(url: string): string {
|
getHostname(url: string): string {
|
||||||
url = url.split('/')[2];
|
url = url.split('/')[2];
|
||||||
@@ -261,7 +276,7 @@ class MadaraPlugin implements Plugin.PluginBase {
|
|||||||
if (this.options?.useNewChapterEndpoint) {
|
if (this.options?.useNewChapterEndpoint) {
|
||||||
html = await fetchApi(this.site + novelPath + 'ajax/chapters/', {
|
html = await fetchApi(this.site + novelPath + 'ajax/chapters/', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
referrer: this.site + novelPath
|
referrer: this.site + novelPath,
|
||||||
}).then(res => res.text());
|
}).then(res => res.text());
|
||||||
} else {
|
} else {
|
||||||
const novelId =
|
const novelId =
|
||||||
@@ -347,7 +362,7 @@ class MadaraPlugin implements Plugin.PluginBase {
|
|||||||
'/page/' +
|
'/page/' +
|
||||||
pageNo +
|
pageNo +
|
||||||
'/?s=' +
|
'/?s=' +
|
||||||
searchTerm +
|
encodeURIComponent(searchTerm) +
|
||||||
'&post_type=wp-manga';
|
'&post_type=wp-manga';
|
||||||
const loadedCheerio = await this.getCheerio(url, true);
|
const loadedCheerio = await this.getCheerio(url, true);
|
||||||
return this.parseNovels(loadedCheerio);
|
return this.parseNovels(loadedCheerio);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class MTLNovelPlugin implements Plugin.PluginBase {
|
|||||||
this.icon = 'multisrc/mtlnovel/mtlnovel/icon.png';
|
this.icon = 'multisrc/mtlnovel/mtlnovel/icon.png';
|
||||||
this.site = metadata.sourceSite;
|
this.site = metadata.sourceSite;
|
||||||
this.mainUrl = 'https://www.mtlnovels.com/';
|
this.mainUrl = 'https://www.mtlnovels.com/';
|
||||||
this.version = '1.1.2';
|
this.version = '1.1.3';
|
||||||
this.options = metadata.options ?? ({} as MTLNovelOptions);
|
this.options = metadata.options ?? ({} as MTLNovelOptions);
|
||||||
this.filters = metadata.filters satisfies Filters;
|
this.filters = metadata.filters satisfies Filters;
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,9 @@ class MTLNovelPlugin implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
const searchUrl =
|
const searchUrl =
|
||||||
this.site + 'wp-admin/admin-ajax.php?action=autosuggest&q=' + searchTerm;
|
this.site +
|
||||||
|
'wp-admin/admin-ajax.php?action=autosuggest&q=' +
|
||||||
|
encodeURIComponent(searchTerm);
|
||||||
|
|
||||||
const res = await this.safeFecth(searchUrl);
|
const res = await this.safeFecth(searchUrl);
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
this.icon = `multisrc/readnovelfull/${metadata.id.toLowerCase()}/icon.png`;
|
this.icon = `multisrc/readnovelfull/${metadata.id.toLowerCase()}/icon.png`;
|
||||||
this.site = metadata.sourceSite;
|
this.site = metadata.sourceSite;
|
||||||
const versionIncrements = metadata.options?.versionIncrements || 0;
|
const versionIncrements = metadata.options?.versionIncrements || 0;
|
||||||
this.version = `1.0.${0 + versionIncrements}`;
|
this.version = `1.0.${1 + versionIncrements}`;
|
||||||
this.options = metadata.options;
|
this.options = metadata.options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
this.site +
|
this.site +
|
||||||
'/' +
|
'/' +
|
||||||
this.options?.searchPage
|
this.options?.searchPage
|
||||||
.replace('%%SEARCH%%', searchTerm)
|
.replace('%%SEARCH%%', encodeURIComponent(searchTerm))
|
||||||
.replace('%%PAGE%%', page.toString());
|
.replace('%%PAGE%%', page.toString());
|
||||||
const $ = await this.getCheerio(url, true);
|
const $ = await this.getCheerio(url, true);
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class RulatePlugin implements Plugin.PluginBase {
|
|||||||
this.name = metadata.sourceName;
|
this.name = metadata.sourceName;
|
||||||
this.icon = `multisrc/rulate/${metadata.id.toLowerCase()}/icon.png`;
|
this.icon = `multisrc/rulate/${metadata.id.toLowerCase()}/icon.png`;
|
||||||
this.site = metadata.sourceSite;
|
this.site = metadata.sourceSite;
|
||||||
this.version = '1.0.' + (1 + metadata.versionIncrements);
|
this.version = '1.0.' + (2 + metadata.versionIncrements);
|
||||||
this.filters = metadata.filters;
|
this.filters = metadata.filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +217,9 @@ class RulatePlugin implements Plugin.PluginBase {
|
|||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
const result: response[] = await fetchApi(
|
const result: response[] = await fetchApi(
|
||||||
this.site + '/search/autocomplete?query=' + searchTerm,
|
this.site +
|
||||||
|
'/search/autocomplete?query=' +
|
||||||
|
encodeURIComponent(searchTerm),
|
||||||
).then(res => res.json());
|
).then(res => res.json());
|
||||||
|
|
||||||
result.forEach(novel => {
|
result.forEach(novel => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class AllNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
name = 'AllNovelFull';
|
name = 'AllNovelFull';
|
||||||
icon = 'src/en/allnovelfull/icon.png';
|
icon = 'src/en/allnovelfull/icon.png';
|
||||||
site = 'https://allnovelfull.net';
|
site = 'https://allnovelfull.net';
|
||||||
version = '1.0.1';
|
version = '1.0.2';
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI) {
|
parseNovels(loadedCheerio: CheerioAPI) {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
@@ -140,7 +140,7 @@ class AllNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}/search?keyword=${searchTerm}&page=${page}`;
|
const url = `${this.site}/search?keyword=${encodeURIComponent(searchTerm)}&page=${page}`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { defaultCover } from '@libs/defaultCover';
|
|||||||
class ArchiveOfOurOwn implements Plugin.PluginBase {
|
class ArchiveOfOurOwn implements Plugin.PluginBase {
|
||||||
id = 'archiveofourown';
|
id = 'archiveofourown';
|
||||||
name = 'Archive Of Our Own';
|
name = 'Archive Of Our Own';
|
||||||
version = '1.0.2';
|
version = '1.0.3';
|
||||||
icon = 'src/en/ao3/icon.png';
|
icon = 'src/en/ao3/icon.png';
|
||||||
site = 'https://archiveofourown.org/';
|
site = 'https://archiveofourown.org/';
|
||||||
|
|
||||||
@@ -282,7 +282,7 @@ class ArchiveOfOurOwn implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const searchUrl = `${this.site}works/search?page=${page}&work_search%5Blanguage_id%5D=en&work_search%5Bquery%5D=${searchTerm}`;
|
const searchUrl = `${this.site}works/search?page=${page}&work_search%5Blanguage_id%5D=en&work_search%5Bquery%5D=${encodeURIComponent(searchTerm)}`;
|
||||||
|
|
||||||
const result = await fetchApi(searchUrl);
|
const result = await fetchApi(searchUrl);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class BLN implements Plugin.PluginBase {
|
|||||||
name = 'BestLightNovel';
|
name = 'BestLightNovel';
|
||||||
icon = 'src/en/bestlightnovel/icon.png';
|
icon = 'src/en/bestlightnovel/icon.png';
|
||||||
site = 'https://bestlightnovel.com/';
|
site = 'https://bestlightnovel.com/';
|
||||||
version = '1.0.0';
|
version = '1.0.1';
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI) {
|
parseNovels(loadedCheerio: CheerioAPI) {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
@@ -160,7 +160,7 @@ class BLN implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}search_novels/${searchTerm}?page=${page}`;
|
const url = `${this.site}search_novels/${encodeURIComponent(searchTerm)}?page=${page}`;
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class ComradeMaoPlugin implements Plugin.PluginBase {
|
|||||||
id = 'comrademao';
|
id = 'comrademao';
|
||||||
name = 'Comrade Mao';
|
name = 'Comrade Mao';
|
||||||
site = 'https://comrademao.com/';
|
site = 'https://comrademao.com/';
|
||||||
version = '1.0.0';
|
version = '1.0.1';
|
||||||
icon = 'src/en/comrademao/icon.png';
|
icon = 'src/en/comrademao/icon.png';
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI) {
|
parseNovels(loadedCheerio: CheerioAPI) {
|
||||||
@@ -149,7 +149,7 @@ class ComradeMaoPlugin implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
pageNo: number,
|
pageNo: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}page/${pageNo}/?s=${searchTerm}&post_type=novel`;
|
const url = `${this.site}page/${pageNo}/?s=${encodeURIComponent(searchTerm)}&post_type=novel`;
|
||||||
|
|
||||||
const body = await fetchApi(url).then(result => result.text());
|
const body = await fetchApi(url).then(result => result.text());
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import { Filters, FilterTypes, FilterValueWithType } from '@libs/filterInputs';
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
|
|
||||||
|
//TODO: this looks similar to fictioneer source? maybe use multisrc someday
|
||||||
|
|
||||||
class DDLPlugin implements Plugin.PluginBase {
|
class DDLPlugin implements Plugin.PluginBase {
|
||||||
id = 'DDL.com';
|
id = 'DDL.com';
|
||||||
name = 'Divine Dao Library';
|
name = 'Divine Dao Library';
|
||||||
site = 'https://www.divinedaolibrary.com/';
|
site = 'https://www.divinedaolibrary.com/';
|
||||||
version = '1.1.0';
|
version = '1.1.1';
|
||||||
icon = 'src/en/divinedaolibrary/icon.png';
|
icon = 'src/en/divinedaolibrary/icon.png';
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Plugin } from '@typings/plugin';
|
|||||||
class EarlyNovelPlugin implements Plugin.PagePlugin {
|
class EarlyNovelPlugin implements Plugin.PagePlugin {
|
||||||
id = 'earlynovel';
|
id = 'earlynovel';
|
||||||
name = 'Early Novel';
|
name = 'Early Novel';
|
||||||
version = '1.0.0';
|
version = '1.0.1';
|
||||||
icon = 'src/en/earlynovel/icon.png';
|
icon = 'src/en/earlynovel/icon.png';
|
||||||
site = 'https://earlynovel.net/';
|
site = 'https://earlynovel.net/';
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ class EarlyNovelPlugin implements Plugin.PagePlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}search?keyword=${searchTerm}`;
|
const url = `${this.site}search?keyword=${encodeURIComponent(searchTerm)}`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class Genesis implements Plugin.PluginBase {
|
|||||||
icon = 'src/en/genesis/icon.png';
|
icon = 'src/en/genesis/icon.png';
|
||||||
customCSS = 'src/en/genesis/customCSS.css';
|
customCSS = 'src/en/genesis/customCSS.css';
|
||||||
site = 'https://genesistudio.com';
|
site = 'https://genesistudio.com';
|
||||||
version = '1.1.0';
|
version = '1.1.1';
|
||||||
|
|
||||||
imageRequestInit?: Plugin.ImageRequestInit | undefined = {
|
imageRequestInit?: Plugin.ImageRequestInit | undefined = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -465,7 +465,7 @@ class Genesis implements Plugin.PluginBase {
|
|||||||
pageNo: number,
|
pageNo: number,
|
||||||
): Promise<Plugin.SourceNovel[]> {
|
): Promise<Plugin.SourceNovel[]> {
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
const url = `${this.site}/api/novels/search?serialization=All&sort=Popular&title=${searchTerm}`;
|
const url = `${this.site}/api/novels/search?serialization=All&sort=Popular&title=${encodeURIComponent(searchTerm)}`;
|
||||||
const json = await fetchApi(url).then(r => r.json());
|
const json = await fetchApi(url).then(r => r.json());
|
||||||
return this.parseNovels(json);
|
return this.parseNovels(json);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Plugin } from '@typings/plugin';
|
|||||||
class MTLReader implements Plugin.PluginBase {
|
class MTLReader implements Plugin.PluginBase {
|
||||||
id = 'mtlreader';
|
id = 'mtlreader';
|
||||||
name = 'MTL Reader';
|
name = 'MTL Reader';
|
||||||
version = '1.0.0';
|
version = '1.0.1';
|
||||||
icon = 'src/en/mtlreader/icon.png';
|
icon = 'src/en/mtlreader/icon.png';
|
||||||
site = 'https://mtlreader.com/';
|
site = 'https://mtlreader.com/';
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ class MTLReader implements Plugin.PluginBase {
|
|||||||
const tokenCheerio = parseHTML(body);
|
const tokenCheerio = parseHTML(body);
|
||||||
const token = tokenCheerio('input[name="_token"]').attr('value');
|
const token = tokenCheerio('input[name="_token"]').attr('value');
|
||||||
|
|
||||||
const searchUrl = `${this.site}search?_token=${token}&input=${searchTerm}`;
|
const searchUrl = `${this.site}search?_token=${token}&input=${encodeURIComponent(searchTerm)}`;
|
||||||
const seacrhBody = await fetchApi(searchUrl).then(r => r.text());
|
const seacrhBody = await fetchApi(searchUrl).then(r => r.text());
|
||||||
const loadedCheerio = parseHTML(seacrhBody);
|
const loadedCheerio = parseHTML(seacrhBody);
|
||||||
return this.parseNovels(loadedCheerio);
|
return this.parseNovels(loadedCheerio);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class NovelBin implements Plugin.PluginBase {
|
|||||||
icon = 'src/en/novelbin/icon.png';
|
icon = 'src/en/novelbin/icon.png';
|
||||||
site = 'https://novelbin.com/';
|
site = 'https://novelbin.com/';
|
||||||
subsite = 'https://novelbin.lanovels.net/';
|
subsite = 'https://novelbin.lanovels.net/';
|
||||||
version = '1.2.0';
|
version = '1.2.1';
|
||||||
imageRequestInit?: Plugin.ImageRequestInit | undefined = {
|
imageRequestInit?: Plugin.ImageRequestInit | undefined = {
|
||||||
headers: {
|
headers: {
|
||||||
'referrer': this.site,
|
'referrer': this.site,
|
||||||
@@ -144,7 +144,7 @@ class NovelBin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}search/?keyword=${searchTerm}`;
|
const url = `${this.site}search/?keyword=${encodeURIComponent(searchTerm)}`;
|
||||||
const body = await fetchApi(url).then(r => r.text());
|
const body = await fetchApi(url).then(r => r.text());
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class NovelBuddy implements Plugin.PluginBase {
|
|||||||
id = 'novelbuddy';
|
id = 'novelbuddy';
|
||||||
name = 'NovelBuddy.io';
|
name = 'NovelBuddy.io';
|
||||||
site = 'https://novelbuddy.io/';
|
site = 'https://novelbuddy.io/';
|
||||||
version = '1.0.2';
|
version = '1.0.3';
|
||||||
icon = 'src/en/novelbuddy/icon.png';
|
icon = 'src/en/novelbuddy/icon.png';
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI) {
|
parseNovels(loadedCheerio: CheerioAPI) {
|
||||||
@@ -177,7 +177,7 @@ class NovelBuddy implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}search?q=${searchTerm}&page=${page}`;
|
const url = `${this.site}search?q=${encodeURIComponent(searchTerm)}&page=${page}`;
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Plugin } from '@typings/plugin';
|
|||||||
class NovelFire implements Plugin.PluginBase {
|
class NovelFire implements Plugin.PluginBase {
|
||||||
id = 'novelfire';
|
id = 'novelfire';
|
||||||
name = 'Novel Fire';
|
name = 'Novel Fire';
|
||||||
version = '1.0.1';
|
version = '1.0.2';
|
||||||
icon = 'src/en/novelfire/icon.png';
|
icon = 'src/en/novelfire/icon.png';
|
||||||
site = 'https://novelfire.net/';
|
site = 'https://novelfire.net/';
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ class NovelFire implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}search?keyword=${searchTerm}&page=${page}`;
|
const url = `${this.site}search?keyword=${encodeURIComponent(searchTerm)}&page=${page}`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Filters, FilterTypes } from '@libs/filterInputs';
|
|||||||
class NovelFull implements Plugin.PluginBase {
|
class NovelFull implements Plugin.PluginBase {
|
||||||
id = 'novelfull';
|
id = 'novelfull';
|
||||||
name = 'NovelFull';
|
name = 'NovelFull';
|
||||||
version = '1.0.2';
|
version = '1.0.3';
|
||||||
icon = 'src/en/novelfull/icon.png';
|
icon = 'src/en/novelfull/icon.png';
|
||||||
site = 'https://novelfull.com/';
|
site = 'https://novelfull.com/';
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ class NovelFull implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const searchUrl = `${this.site}search?keyword=${searchTerm}&page=${page}`;
|
const searchUrl = `${this.site}search?keyword=${encodeURIComponent(searchTerm)}&page=${page}`;
|
||||||
|
|
||||||
const result = await fetchApi(searchUrl);
|
const result = await fetchApi(searchUrl);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { defaultCover } from '@libs/defaultCover';
|
|||||||
class NovelHall implements Plugin.PluginBase {
|
class NovelHall implements Plugin.PluginBase {
|
||||||
id = 'novelhall';
|
id = 'novelhall';
|
||||||
name = 'Novel Hall';
|
name = 'Novel Hall';
|
||||||
version = '1.0.1';
|
version = '1.0.2';
|
||||||
icon = 'src/en/novelhall/icon.png';
|
icon = 'src/en/novelhall/icon.png';
|
||||||
site = 'https://novelhall.com/';
|
site = 'https://novelhall.com/';
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ class NovelHall implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}index.php?s=so&module=book&keyword=${searchTerm}`;
|
const url = `${this.site}index.php?s=so&module=book&keyword=${encodeURIComponent(searchTerm)}`;
|
||||||
const body = await fetchApi(url).then(r => r.text());
|
const body = await fetchApi(url).then(r => r.text());
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { NovelStatus } from '@libs/novelStatus';
|
|||||||
class Novelight implements Plugin.PagePlugin {
|
class Novelight implements Plugin.PagePlugin {
|
||||||
id = 'novelight';
|
id = 'novelight';
|
||||||
name = 'Novelight';
|
name = 'Novelight';
|
||||||
version = '1.1.0';
|
version = '1.1.1';
|
||||||
icon = 'src/en/novelight/icon.png';
|
icon = 'src/en/novelight/icon.png';
|
||||||
site = 'https://novelight.net/';
|
site = 'https://novelight.net/';
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ class Novelight implements Plugin.PagePlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}catalog/?search=${searchTerm}`;
|
const url = `${this.site}catalog/?search=${encodeURIComponent(searchTerm)}`;
|
||||||
const body = await fetchApi(url).then(r => r.text());
|
const body = await fetchApi(url).then(r => r.text());
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Plugin } from '@typings/plugin';
|
|||||||
class NovelUpdates implements Plugin.PluginBase {
|
class NovelUpdates implements Plugin.PluginBase {
|
||||||
id = 'novelupdates';
|
id = 'novelupdates';
|
||||||
name = 'Novel Updates';
|
name = 'Novel Updates';
|
||||||
version = '0.7.15';
|
version = '0.7.16';
|
||||||
icon = 'src/en/novelupdates/icon.png';
|
icon = 'src/en/novelupdates/icon.png';
|
||||||
customCSS = 'src/en/novelupdates/customCSS.css';
|
customCSS = 'src/en/novelupdates/customCSS.css';
|
||||||
site = 'https://www.novelupdates.com/';
|
site = 'https://www.novelupdates.com/';
|
||||||
@@ -1004,7 +1004,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
);
|
);
|
||||||
searchTerm = longestSearchTerm.replace(/[‘’]/g, "'").replace(/\s+/g, '+');
|
searchTerm = longestSearchTerm.replace(/[‘’]/g, "'").replace(/\s+/g, '+');
|
||||||
|
|
||||||
const url = `${this.site}series-finder/?sf=1&sh=${searchTerm}&sort=srank&order=asc&pg=${page}`;
|
const url = `${this.site}series-finder/?sf=1&sh=${encodeURIComponent(searchTerm)}&sort=srank&order=asc&pg=${page}`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Filters, FilterTypes } from '@libs/filterInputs';
|
|||||||
class PawRead implements Plugin.PluginBase {
|
class PawRead implements Plugin.PluginBase {
|
||||||
id = 'pawread';
|
id = 'pawread';
|
||||||
name = 'PawRead';
|
name = 'PawRead';
|
||||||
version = '2.0.0';
|
version = '2.0.1';
|
||||||
icon = 'src/en/pawread/icon.png';
|
icon = 'src/en/pawread/icon.png';
|
||||||
site = 'https://m.pawread.com/';
|
site = 'https://m.pawread.com/';
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ class PawRead implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const searchUrl = `${this.site}search/?keywords=${searchTerm}&page=${page}`;
|
const searchUrl = `${this.site}search/?keywords=${encodeURIComponent(searchTerm)}&page=${page}`;
|
||||||
|
|
||||||
const result = await fetchApi(searchUrl);
|
const result = await fetchApi(searchUrl);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class Rainofsnow implements Plugin.PagePlugin {
|
|||||||
name = 'Rainofsnow';
|
name = 'Rainofsnow';
|
||||||
icon = 'src/en/rainofsnow/icon.png';
|
icon = 'src/en/rainofsnow/icon.png';
|
||||||
site = 'https://rainofsnow.com/';
|
site = 'https://rainofsnow.com/';
|
||||||
version = '1.1.1';
|
version = '1.1.2';
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI) {
|
parseNovels(loadedCheerio: CheerioAPI) {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
@@ -154,7 +154,7 @@ class Rainofsnow implements Plugin.PagePlugin {
|
|||||||
// no page number, infinite scroll
|
// no page number, infinite scroll
|
||||||
|
|
||||||
const newSearch = searchTerm.replace(/\s+/g, '+');
|
const newSearch = searchTerm.replace(/\s+/g, '+');
|
||||||
const url = this.site + '?s=' + newSearch;
|
const url = this.site + '?s=' + encodeURIComponent(newSearch);
|
||||||
|
|
||||||
const result = await fetch(url);
|
const result = await fetch(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import dayjs from 'dayjs';
|
|||||||
class ReadLiteNovel implements Plugin.PluginBase {
|
class ReadLiteNovel implements Plugin.PluginBase {
|
||||||
id = 'rln.app';
|
id = 'rln.app';
|
||||||
name = 'ReadLiteNovel';
|
name = 'ReadLiteNovel';
|
||||||
version = '1.0.0';
|
version = '1.0.1';
|
||||||
icon = 'src/en/readlitenovel/icon.png';
|
icon = 'src/en/readlitenovel/icon.png';
|
||||||
site = 'https://rln.app';
|
site = 'https://rln.app';
|
||||||
|
|
||||||
@@ -154,7 +154,8 @@ class ReadLiteNovel implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const url =
|
const url =
|
||||||
this.site + `/search/autocomplete?dataType=json&query=${searchTerm}`;
|
this.site +
|
||||||
|
`/search/autocomplete?dataType=json&query=${encodeURIComponent(searchTerm)}`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.json();
|
const body = await result.json();
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { NovelStatus } from '@libs/novelStatus';
|
|||||||
class RoyalRoad implements Plugin.PluginBase {
|
class RoyalRoad implements Plugin.PluginBase {
|
||||||
id = 'royalroad';
|
id = 'royalroad';
|
||||||
name = 'Royal Road';
|
name = 'Royal Road';
|
||||||
version = '2.1.2';
|
version = '2.1.3';
|
||||||
icon = 'src/en/royalroad/icon.png';
|
icon = 'src/en/royalroad/icon.png';
|
||||||
site = 'https://www.royalroad.com/';
|
site = 'https://www.royalroad.com/';
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@ class RoyalRoad implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const searchUrl = `${this.site}fictions/search?page=${page}&title=${searchTerm}`;
|
const searchUrl = `${this.site}fictions/search?page=${page}&title=${encodeURIComponent(searchTerm)}`;
|
||||||
|
|
||||||
const body = await fetchApi(searchUrl).then(r => r.text());
|
const body = await fetchApi(searchUrl).then(r => r.text());
|
||||||
return this.parseNovels(body);
|
return this.parseNovels(body);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class ScribbleHubPlugin implements Plugin.PluginBase {
|
|||||||
name = 'Scribble Hub';
|
name = 'Scribble Hub';
|
||||||
icon = 'src/en/scribblehub/icon.png';
|
icon = 'src/en/scribblehub/icon.png';
|
||||||
site = 'https://www.scribblehub.com/';
|
site = 'https://www.scribblehub.com/';
|
||||||
version = '1.0.1';
|
version = '1.0.2';
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI) {
|
parseNovels(loadedCheerio: CheerioAPI) {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
@@ -177,7 +177,7 @@ class ScribbleHubPlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}?s=${searchTerm}&post_type=fictionposts`;
|
const url = `${this.site}?s=${encodeURIComponent(searchTerm)}&post_type=fictionposts`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { storage } from '@libs/storage';
|
|||||||
class Webnovel implements Plugin.PluginBase {
|
class Webnovel implements Plugin.PluginBase {
|
||||||
id = 'webnovel';
|
id = 'webnovel';
|
||||||
name = 'Webnovel';
|
name = 'Webnovel';
|
||||||
version = '1.0.2';
|
version = '1.0.3';
|
||||||
icon = 'src/en/webnovel/icon.png';
|
icon = 'src/en/webnovel/icon.png';
|
||||||
site = 'https://www.webnovel.com';
|
site = 'https://www.webnovel.com';
|
||||||
headers = {
|
headers = {
|
||||||
@@ -240,7 +240,7 @@ class Webnovel implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm = searchTerm.replace(/\s+/g, '+');
|
searchTerm = searchTerm.replace(/\s+/g, '+');
|
||||||
|
|
||||||
const url = `${this.site}/search?keywords=${searchTerm}&pageIndex=${pageNo}${type ? `&type=${type}` : ''}`;
|
const url = `${this.site}/search?keywords=${encodeURIComponent(searchTerm)}&pageIndex=${pageNo}${type ? `&type=${type}` : ''}`;
|
||||||
const result = await fetchApi(url, {
|
const result = await fetchApi(url, {
|
||||||
headers: this.headers,
|
headers: this.headers,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class WuxiaWorld implements Plugin.PluginBase {
|
|||||||
site = 'https://www.wuxiaworld.com/';
|
site = 'https://www.wuxiaworld.com/';
|
||||||
apiSite = 'https://api2.wuxiaworld.com/wuxiaworld.api.v2.';
|
apiSite = 'https://api2.wuxiaworld.com/wuxiaworld.api.v2.';
|
||||||
filters?: Filters | undefined;
|
filters?: Filters | undefined;
|
||||||
version = '0.5.0';
|
version = '0.5.1';
|
||||||
|
|
||||||
parseNovels(data: { items: NovelEntry[] }) {
|
parseNovels(data: { items: NovelEntry[] }) {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
@@ -270,7 +270,8 @@ class WuxiaWorld implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const url = this.site + 'api/novels/search?query=' + searchTerm;
|
const url =
|
||||||
|
this.site + 'api/novels/search?query=' + encodeURIComponent(searchTerm);
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const data = await result.json();
|
const data = await result.json();
|
||||||
|
|||||||
Reference in New Issue
Block a user