ytdlp_ejs/builtin/
quickjs.rs

1//! QuickJS JS Challenge Provider
2
3use crate::provider::JsChallengeError;
4use rquickjs::{Context, Function, Object, Runtime};
5
6/// QuickJS-based JavaScript Challenge Provider
7pub struct QuickJSJCP {
8    context: Context,
9}
10
11impl QuickJSJCP {
12    pub fn new(code: &str) -> Result<Self, JsChallengeError> {
13        let runtime = Runtime::new()
14            .map_err(|e| JsChallengeError::Runtime(format!("Failed to create runtime: {}", e)))?;
15        let context = Context::full(&runtime)
16            .map_err(|e| JsChallengeError::Runtime(format!("Failed to create context: {}", e)))?;
17
18        context.with(|ctx| {
19            let globals = ctx.globals();
20            let result_obj = Object::new(ctx.clone()).map_err(|e| {
21                JsChallengeError::Runtime(format!("Failed to create object: {}", e))
22            })?;
23            globals
24                .set("_result", result_obj)
25                .map_err(|e| JsChallengeError::Runtime(format!("Failed to set _result: {}", e)))?;
26
27            ctx.eval::<(), _>(code).map_err(|e| {
28                let err_msg = match &e {
29                    rquickjs::Error::Exception => {
30                        let exc = ctx.catch();
31                        if exc.is_null() || exc.is_undefined() {
32                            "Exception (no details)".to_string()
33                        } else {
34                            format!("Exception: {:?}", exc)
35                        }
36                    }
37                    _ => format!("{:?}", e),
38                };
39                JsChallengeError::Runtime(format!("Failed to execute: {}", err_msg))
40            })?;
41
42            Ok::<(), JsChallengeError>(())
43        })?;
44
45        Ok(Self { context })
46    }
47
48    pub fn solve_n(&self, challenge: &str) -> Result<String, JsChallengeError> {
49        self.call_solver("n", challenge)
50    }
51
52    pub fn solve_sig(&self, challenge: &str) -> Result<String, JsChallengeError> {
53        self.call_solver("sig", challenge)
54    }
55
56    fn call_solver(&self, func_name: &str, challenge: &str) -> Result<String, JsChallengeError> {
57        self.context.with(|ctx| {
58            let globals = ctx.globals();
59            let result: Object = globals
60                .get("_result")
61                .map_err(|e| JsChallengeError::Runtime(format!("Failed to get _result: {}", e)))?;
62            let func: Function = result.get(func_name).map_err(|e| {
63                JsChallengeError::Runtime(format!("Failed to get {} function: {}", func_name, e))
64            })?;
65
66            let result: String = func.call((challenge,)).map_err(|e| {
67                let err_msg = match &e {
68                    rquickjs::Error::Exception => {
69                        let exc = ctx.catch();
70                        if exc.is_null() || exc.is_undefined() {
71                            "Exception (no details)".to_string()
72                        } else {
73                            format!("Exception: {:?}", exc)
74                        }
75                    }
76                    _ => format!("{:?}", e),
77                };
78                JsChallengeError::Runtime(format!("Failed to call {}: {}", func_name, err_msg))
79            })?;
80            Ok(result)
81        })
82    }
83}