Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sources/en.comix/res/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"info": {
"id": "en.comix",
"name": "Comix",
"version": 10,
"version": 11,
"url": "https://comix.to",
"contentRating": 1,
"languages": ["en"]
Expand Down
120 changes: 95 additions & 25 deletions sources/en.comix/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,95 @@ use aidoku::{
prelude::*,
};

pub fn create_web_view() -> Result<WebView> {
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<String>,
installer_fn: Option<String>,
}

pub fn create_web_view() -> Result<ComixWebView> {
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<String> {
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<String> {
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 '';
}}
Expand All @@ -35,17 +107,16 @@ pub fn get_token(web_view: &WebView, path: &str) -> Result<String> {
Ok(token)
}

pub fn decode_response(web_view: &WebView, url: &str, encoded_res: &str) -> Result<String> {
let result = web_view.eval(&format!(
pub fn decode_response(web_view: &ComixWebView, url: &str, encoded_res: &str) -> Result<String> {
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) {{
Expand All @@ -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: '',
Expand All @@ -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;
Expand Down
Loading