fix: StorySeedling (#1857)
* Story seedling beginning * Font decoding * Bump version number * Remove spam * Remove story seedling ad * Live update nonce * add error for captcha * JSON instead of text compare * Fix proxy crashing on error * Fix captcha error * Fix updatedNonce check * Compression headers should only be forwarded by proxy's that keep the content compressed * Revert index.ts change
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import { CheerioAPI, load } from 'cheerio';
|
||||
import { Plugin } from '@/types/plugin';
|
||||
import { fetchApi } from '@libs/fetch';
|
||||
import { fetchApi, fetchText } from '@libs/fetch';
|
||||
import { NovelStatus } from '@libs/novelStatus';
|
||||
import { defaultCover } from '@libs/defaultCover';
|
||||
|
||||
class StorySeedlingPlugin implements Plugin.PluginBase {
|
||||
id = 'storyseedling';
|
||||
name = 'StorySeedling';
|
||||
icon = 'src/en/storyseedling/icon.png';
|
||||
site = 'https://storyseedling.com/';
|
||||
version = '1.0.3';
|
||||
version = '1.0.4';
|
||||
nonce: string | undefined;
|
||||
|
||||
async getCheerio(url: string, search: boolean): Promise<CheerioAPI> {
|
||||
const r = await fetchApi(url);
|
||||
@@ -162,24 +162,66 @@ class StorySeedlingPlugin implements Plugin.PluginBase {
|
||||
return novel as Plugin.SourceNovel;
|
||||
}
|
||||
|
||||
async parseChapter(chapterPath: string): Promise<string> {
|
||||
async updateNonce(chapterPath: string) {
|
||||
const $ = await this.getCheerio(this.site + chapterPath, false);
|
||||
|
||||
const xdata = $('div[ax-load][x-data]').attr('x-data');
|
||||
|
||||
const t = $('div.justify-center > div.mb-4');
|
||||
let chapterText = t.html() || '';
|
||||
|
||||
if (xdata) {
|
||||
chapterText =
|
||||
chapterText +
|
||||
"\n\n Error parsing chapter: Turnstile detected. Advise just reading in web view until there's a fix.";
|
||||
// const listXdata = xdata?.split("'");
|
||||
// const dataNovelId = listXdata[1];
|
||||
// const dataNovelN = listXdata[3];
|
||||
this.nonce = $('div.mb-4:has(h1.text-xl) > div')
|
||||
.attr('x-data')
|
||||
?.match(/loadChapter\('.+?', '(.+?)'\)/)[1];
|
||||
}
|
||||
|
||||
return chapterText;
|
||||
async parseChapter(chapterPath: string): Promise<string> {
|
||||
const updatedNonce = !this.nonce;
|
||||
if (!this.nonce) await this.updateNonce(chapterPath);
|
||||
const text = await fetchApi(this.site + chapterPath + '/content', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'referrer': this.site + chapterPath + '/',
|
||||
'x-nonce': this.nonce,
|
||||
},
|
||||
body: JSON.stringify({ 'captcha_response': '' }),
|
||||
}).then(r => r.text());
|
||||
let textJson;
|
||||
try {
|
||||
textJson = JSON.parse(text);
|
||||
} catch (_) {
|
||||
//not json :fire: we have chapter
|
||||
}
|
||||
if (textJson && !textJson.success) {
|
||||
if (textJson.message === 'Invalid security.') {
|
||||
if (updatedNonce) {
|
||||
throw new Error(`Failed to find code!`);
|
||||
}
|
||||
this.nonce = '';
|
||||
return await this.parseChapter(chapterPath);
|
||||
}
|
||||
if (textJson.captcha) {
|
||||
throw new Error(
|
||||
`Failed to bypass turnstile captcha (read in webview until it stops ig)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
let html = text
|
||||
.replace(/cls[a-f0-9]+/g, '')
|
||||
.split('')
|
||||
.map(char => {
|
||||
const code = char.charCodeAt(0);
|
||||
const offset = code > 12123 ? 12027 : 12033;
|
||||
const decoded = code - offset;
|
||||
return decoded >= 32 && decoded <= 126
|
||||
? String.fromCharCode(decoded)
|
||||
: char;
|
||||
})
|
||||
.join('');
|
||||
let $ = load(html);
|
||||
|
||||
$('span').text((_, txt) =>
|
||||
txt.toLowerCase().includes('storyseedling') ||
|
||||
txt.toLowerCase().includes('story seedling')
|
||||
? ''
|
||||
: txt,
|
||||
);
|
||||
|
||||
return $.html();
|
||||
}
|
||||
|
||||
async searchNovels(
|
||||
|
||||
@@ -21,13 +21,7 @@ const settings: ServerSetting = {
|
||||
'sec-fetch-dest',
|
||||
'pragma',
|
||||
],
|
||||
disAllowResponseHeaders: [
|
||||
'link',
|
||||
'set-cookie',
|
||||
'set-cookie2',
|
||||
'content-encoding',
|
||||
'content-length',
|
||||
],
|
||||
disAllowResponseHeaders: ['link', 'set-cookie', 'set-cookie2'],
|
||||
useUserAgent: true,
|
||||
};
|
||||
|
||||
@@ -173,7 +167,11 @@ const proxyRequest: Connect.SimpleHandleFunction = (req, res) => {
|
||||
.then(([res2, text]) => {
|
||||
res.statusCode = res2.status;
|
||||
res2.headers.forEach((val, key) => {
|
||||
if (!settings.disAllowResponseHeaders.includes(key)) {
|
||||
if (
|
||||
!settings.disAllowResponseHeaders.includes(key) &&
|
||||
key !== 'content-encoding' &&
|
||||
key !== 'content-length'
|
||||
) {
|
||||
res.setHeader(key, val);
|
||||
}
|
||||
});
|
||||
@@ -186,11 +184,20 @@ const proxyRequest: Connect.SimpleHandleFunction = (req, res) => {
|
||||
res.end();
|
||||
});
|
||||
} else if (settings.fetchMode === FetchMode.PROXY) {
|
||||
proxy.web(req, res, {
|
||||
proxy.web(
|
||||
req,
|
||||
res,
|
||||
{
|
||||
target: _url.origin,
|
||||
selfHandleResponse: true,
|
||||
followRedirects: true,
|
||||
});
|
||||
},
|
||||
err => {
|
||||
console.error(err);
|
||||
res.statusCode = 500;
|
||||
res.end();
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user