Fix special characters breaking search in most sources (#1557)

This commit is contained in:
Adam Kemish
2025-03-15 15:35:31 +08:00
committed by GitHub
parent 3a10e24545
commit ea7965bdda
28 changed files with 103 additions and 73 deletions
+6 -2
View File
@@ -25,7 +25,7 @@ class IfreedomPlugin implements Plugin.PluginBase {
this.name = metadata.sourceName;
this.icon = `multisrc/ifreedom/${metadata.id.toLowerCase()}/icon.png`;
this.site = metadata.sourceSite;
this.version = '1.0.1';
this.version = '1.0.2';
this.filters = metadata.filters;
}
@@ -166,7 +166,11 @@ class IfreedomPlugin implements Plugin.PluginBase {
page: number | undefined = 1,
): Promise<Plugin.NovelItem[]> {
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 loadedCheerio = parseHTML(result);
+6 -3
View File
@@ -42,7 +42,7 @@ class LightNovelWPPlugin implements Plugin.PluginBase {
this.icon = `multisrc/lightnovelwp/${metadata.id.toLowerCase()}/icon.png`;
this.site = metadata.sourceSite;
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.filters = metadata.filters satisfies Filters;
@@ -103,7 +103,9 @@ class LightNovelWPPlugin implements Plugin.PluginBase {
if (novelName && novelUrl) {
const novelCover =
article.match(/<img [^>]*?src="([^\"]*)"[^>]*?(?: data-src="([^\"]*)")?[^>]*>/) || [];
article.match(
/<img [^>]*?src="([^\"]*)"[^>]*?(?: data-src="([^\"]*)")?[^>]*>/,
) || [];
let novelPath;
if (novelUrl.includes(this.site)) {
@@ -444,7 +446,8 @@ class LightNovelWPPlugin implements Plugin.PluginBase {
searchTerm: string,
page: number,
): 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);
return this.parseNovels(html);
}
+34 -19
View File
@@ -45,7 +45,7 @@ class MadaraPlugin implements Plugin.PluginBase {
this.icon = `multisrc/madara/${metadata.id.toLowerCase()}/icon.png`;
this.site = metadata.sourceSite;
const versionIncrements = metadata.options?.versionIncrements || 0;
this.version = `1.0.${6 + versionIncrements}`;
this.version = `1.0.${7 + versionIncrements}`;
this.options = metadata.options;
this.filters = metadata.filters;
@@ -62,26 +62,41 @@ class MadaraPlugin implements Plugin.PluginBase {
translateDragontea(text: Cheerio<AnyNode>): Cheerio<AnyNode> {
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 forwardAlpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
text.html($.html());
text.find('*').addBack().contents().filter((_, el) => el.nodeType === 3).each((_, el) => {
const $el = $(el);
const translated = $el.text().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>'));
});
text
.find('*')
.addBack()
.contents()
.filter((_, el) => el.nodeType === 3)
.each((_, el) => {
const $el = $(el);
const translated = $el
.text()
.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;
}
}
getHostname(url: string): string {
url = url.split('/')[2];
@@ -261,7 +276,7 @@ class MadaraPlugin implements Plugin.PluginBase {
if (this.options?.useNewChapterEndpoint) {
html = await fetchApi(this.site + novelPath + 'ajax/chapters/', {
method: 'POST',
referrer: this.site + novelPath
referrer: this.site + novelPath,
}).then(res => res.text());
} else {
const novelId =
@@ -347,7 +362,7 @@ class MadaraPlugin implements Plugin.PluginBase {
'/page/' +
pageNo +
'/?s=' +
searchTerm +
encodeURIComponent(searchTerm) +
'&post_type=wp-manga';
const loadedCheerio = await this.getCheerio(url, true);
return this.parseNovels(loadedCheerio);
+4 -2
View File
@@ -33,7 +33,7 @@ class MTLNovelPlugin implements Plugin.PluginBase {
this.icon = 'multisrc/mtlnovel/mtlnovel/icon.png';
this.site = metadata.sourceSite;
this.mainUrl = 'https://www.mtlnovels.com/';
this.version = '1.1.2';
this.version = '1.1.3';
this.options = metadata.options ?? ({} as MTLNovelOptions);
this.filters = metadata.filters satisfies Filters;
}
@@ -203,7 +203,9 @@ class MTLNovelPlugin implements Plugin.PluginBase {
): Promise<Plugin.NovelItem[]> {
if (pageNo !== 1) return [];
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 result = await res.json();
+2 -2
View File
@@ -34,7 +34,7 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
this.icon = `multisrc/readnovelfull/${metadata.id.toLowerCase()}/icon.png`;
this.site = metadata.sourceSite;
const versionIncrements = metadata.options?.versionIncrements || 0;
this.version = `1.0.${0 + versionIncrements}`;
this.version = `1.0.${1 + versionIncrements}`;
this.options = metadata.options;
}
@@ -229,7 +229,7 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
this.site +
'/' +
this.options?.searchPage
.replace('%%SEARCH%%', searchTerm)
.replace('%%SEARCH%%', encodeURIComponent(searchTerm))
.replace('%%PAGE%%', page.toString());
const $ = await this.getCheerio(url, true);
+4 -2
View File
@@ -26,7 +26,7 @@ class RulatePlugin implements Plugin.PluginBase {
this.name = metadata.sourceName;
this.icon = `multisrc/rulate/${metadata.id.toLowerCase()}/icon.png`;
this.site = metadata.sourceSite;
this.version = '1.0.' + (1 + metadata.versionIncrements);
this.version = '1.0.' + (2 + metadata.versionIncrements);
this.filters = metadata.filters;
}
@@ -217,7 +217,9 @@ class RulatePlugin implements Plugin.PluginBase {
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
const novels: Plugin.NovelItem[] = [];
const result: response[] = await fetchApi(
this.site + '/search/autocomplete?query=' + searchTerm,
this.site +
'/search/autocomplete?query=' +
encodeURIComponent(searchTerm),
).then(res => res.json());
result.forEach(novel => {