diff --git a/plugins/english/StorySeedling.ts b/plugins/english/StorySeedling.ts index b1a904b84..0d1ecb643 100644 --- a/plugins/english/StorySeedling.ts +++ b/plugins/english/StorySeedling.ts @@ -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 { const r = await fetchApi(url); @@ -162,24 +162,66 @@ class StorySeedlingPlugin implements Plugin.PluginBase { return novel as Plugin.SourceNovel; } - async parseChapter(chapterPath: string): Promise { + async updateNonce(chapterPath: string) { const $ = await this.getCheerio(this.site + chapterPath, false); + this.nonce = $('div.mb-4:has(h1.text-xl) > div') + .attr('x-data') + ?.match(/loadChapter\('.+?', '(.+?)'\)/)[1]; + } - 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]; + async parseChapter(chapterPath: string): Promise { + 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 chapterText; + return $.html(); } async searchNovels( diff --git a/proxy.ts b/proxy.ts index db93a11e0..7a62431a0 100644 --- a/proxy.ts +++ b/proxy.ts @@ -19,13 +19,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, }; @@ -171,7 +165,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); } }); @@ -184,11 +182,20 @@ const proxyRequest: Connect.SimpleHandleFunction = (req, res) => { res.end(); }); } else if (settings.fetchMode === FetchMode.PROXY) { - proxy.web(req, res, { - target: _url.origin, - selfHandleResponse: true, - followRedirects: true, - }); + proxy.web( + req, + res, + { + target: _url.origin, + selfHandleResponse: true, + followRedirects: true, + }, + err => { + console.error(err); + res.statusCode = 500; + res.end(); + }, + ); } };