diff --git a/sources/en.comix/res/source.json b/sources/en.comix/res/source.json index c248491b..c71fd169 100644 --- a/sources/en.comix/res/source.json +++ b/sources/en.comix/res/source.json @@ -2,7 +2,7 @@ "info": { "id": "en.comix", "name": "Comix", - "version": 10, + "version": 11, "url": "https://comix.to", "contentRating": 1, "languages": ["en"] diff --git a/sources/en.comix/src/web.rs b/sources/en.comix/src/web.rs index 9d5af9a6..fec9cca3 100644 --- a/sources/en.comix/src/web.rs +++ b/sources/en.comix/src/web.rs @@ -7,23 +7,95 @@ use aidoku::{ prelude::*, }; -pub fn create_web_view() -> Result { +const GET_VMOBJ_JS: &str = "\ +const vmKey = Object.keys(window).find(key => key.startsWith('vm'));\ +const vmObj = window[vmKey];\ +if (!vmObj || typeof vmObj !== 'object' || vmObj === window) {\ + return '';\ +}"; + +pub struct ComixWebView { + web_view: WebView, + signer_fn: Option, + installer_fn: Option, +} + +pub fn create_web_view() -> Result { let web_view = WebView::new(); web_view.load_blocking(Request::get(BASE_URL)?)?; - Ok(web_view) + let mut comix_web_view = ComixWebView { + web_view, + signer_fn: None, + installer_fn: None, + }; + find_functions(&mut comix_web_view)?; + Ok(comix_web_view) } -/// * `path`: API path, e.g. "/manga/some-hash/chapters" -pub fn get_token(web_view: &WebView, path: &str) -> Result { - let token = web_view.eval(&format!( +fn find_functions(web_view: &mut ComixWebView) -> Result<()> { + let result = web_view.web_view.eval(&format!( "(() => {{ try {{ - const vmKey = Object.keys(window).find(key => key.startsWith('vm')); - const vmObj = window[vmKey]; - if (!vmObj || typeof vmObj.Qi !== 'function') {{ - return ''; + {GET_VMOBJ_JS} + let probe = '/manga/x/chapters'; + let tokenRe = /^[A-Za-z0-9_-]{{40,200}}$/; + let sig = '', inst = ''; + let fnames = Object.keys(vmObj); + for (let j = 0; j < fnames.length; j++) {{ + let fn = vmObj[fnames[j]]; + if (typeof fn !== 'function') continue; + let ref = 'window[' + JSON.stringify(vmKey) + '].' + fnames[j]; + if (!sig) {{ + try {{ + let out = fn(probe); + if (typeof out === 'string' && out !== probe && tokenRe.test(out)) {{ + sig = ref; + continue; + }} + }} catch (e) {{}} + }} + if (!inst) {{ + try {{ + let got = false; + fn({{ + interceptors: {{ + request:{{ use: function() {{}} }}, + response: {{ use: function() {{ got = true; }} }} + }}, + defaults: {{ + headers: {{ common: {{}} }}, + transformRequest: [], + transformResponse: [] + }} + }}); + if (got) inst = ref; + }} catch (e) {{}} + }} }} - return vmObj.Qi('{path}'); + return sig + '||' + inst; + }} catch(e) {{ + return ''; + }} + }})()", + ))?; + let Some((sig_expr, inst_expr)) = result.split_once("||") else { + bail!("Failed to find signer and installer functions") + }; + web_view.signer_fn = Some(sig_expr.into()); + web_view.installer_fn = Some(inst_expr.into()); + Ok(()) +} + +/// * `path`: API path, e.g. "/manga/some-hash/chapters" +pub fn get_token(web_view: &ComixWebView, path: &str) -> Result { + let Some(signer_fn) = web_view.signer_fn.as_ref() else { + bail!("Missing installer function") + }; + let token = web_view.web_view.eval(&format!( + "(() => {{ + try {{ + {GET_VMOBJ_JS} + return {signer_fn}('{path}'); }} catch(e) {{ return ''; }} @@ -35,17 +107,16 @@ pub fn get_token(web_view: &WebView, path: &str) -> Result { Ok(token) } -pub fn decode_response(web_view: &WebView, url: &str, encoded_res: &str) -> Result { - let result = web_view.eval(&format!( +pub fn decode_response(web_view: &ComixWebView, url: &str, encoded_res: &str) -> Result { + let Some(installer_fn) = web_view.installer_fn.as_ref() else { + bail!("Missing installer function") + }; + let result = web_view.web_view.eval(&format!( "(() => {{ try {{ - const vmKey = Object.keys(window).find(key => key.startsWith('vm')); - const vmObj = window[vmKey]; - if (!vmObj || typeof vmObj.Qi !== 'function') {{ - return ''; - }} - var captured = {{ req: null, res: null }}; - var fakeAxios = {{ + {GET_VMOBJ_JS} + let captured = {{ req: null, res: null }}; + {installer_fn}({{ interceptors: {{ request: {{ use: function (fn) {{ @@ -63,13 +134,12 @@ pub fn decode_response(web_view: &WebView, url: &str, encoded_res: &str) -> Resu transformRequest: [], transformResponse: [], }}, - }}; - vmObj.v(fakeAxios); + }}); - var raw = JSON.parse('{encoded_res}'); - var bodyOut; + let raw = JSON.parse('{encoded_res}'); + let bodyOut; if (raw && typeof raw === 'object' && 'e' in raw && captured.res) {{ - var fakeResp = {{ + let fakeResp = {{ data: raw, status: 200, statusText: '', @@ -79,7 +149,7 @@ pub fn decode_response(web_view: &WebView, url: &str, encoded_res: &str) -> Resu config: {{ url: '{url}', method: 'get', baseURL: '/api/v1' }}, request: {{}}, }}; - var decoded = captured.res(fakeResp); + let decoded = captured.res(fakeResp); bodyOut = JSON.stringify({{ result: decoded && decoded.data }}); }} else if (raw && typeof raw === 'object' && 'result' in raw) {{ bodyOut = text;