diff --git a/app/src/main/java/com/fongmi/android/tv/api/JsLoader.java b/app/src/main/java/com/fongmi/android/tv/api/JsLoader.java index 9f78ba813..24d481bf3 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/JsLoader.java +++ b/app/src/main/java/com/fongmi/android/tv/api/JsLoader.java @@ -1,7 +1,5 @@ package com.fongmi.android.tv.api; -import android.content.Context; - import com.fongmi.android.tv.App; import com.github.catvod.crawler.Spider; import com.github.catvod.crawler.SpiderNull; @@ -34,8 +32,8 @@ public class JsLoader { public Spider getSpider(String key, String api, String ext) { try { if (spiders.containsKey(key)) return spiders.get(key); - Method method = loader.getClass().getMethod("spider", Context.class, String.class, String.class); - Spider spider = (Spider) method.invoke(loader, App.get(), api, ext); + Method method = loader.getClass().getMethod("spider", String.class, String.class); + Spider spider = (Spider) method.invoke(loader, api, ext); spider.init(App.get(), ext); spiders.put(key, spider); return spider; diff --git a/drpy/src/main/java/com/hiker/drpy/Loader.java b/drpy/src/main/java/com/hiker/drpy/Loader.java index 30bd958a7..4b0ab60b0 100644 --- a/drpy/src/main/java/com/hiker/drpy/Loader.java +++ b/drpy/src/main/java/com/hiker/drpy/Loader.java @@ -1,107 +1,34 @@ package com.hiker.drpy; -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Base64; -import android.util.Log; - -import com.hiker.drpy.net.OkHttp; -import com.whl.quickjs.wrapper.JSArray; -import com.whl.quickjs.wrapper.JSObject; -import com.whl.quickjs.wrapper.QuickJSContext; - -import org.json.JSONObject; - -import java.io.IOException; - -import okhttp3.Response; +import com.github.tvbox.quickjs.JSModule; +import com.github.tvbox.quickjs.QuickJSContext; +import com.hiker.drpy.method.Console; +import com.hiker.drpy.method.Global; +import com.hiker.drpy.method.Local; public class Loader { - private final String TAG = Loader.class.getSimpleName(); - private QuickJSContext jsContext; - private Context context; - - private void init(Context context) { - System.loadLibrary("quickjs-android-wrapper"); - this.jsContext = QuickJSContext.create(); - this.context = context; - initConsole(); - initLocal(); - initReq(); - } - - public Spider spider(Context context, String name, String ext) { - if (jsContext == null) init(context); - return new Spider(); - } - - private void initConsole() { - jsContext.evaluate("var console = {};"); - JSObject console = (JSObject) jsContext.getGlobalObject().getProperty("console"); - console.setProperty("log", args -> { - StringBuilder b = new StringBuilder(); - for (Object o : args) b.append(o == null ? "null" : o.toString()); - Log.e(TAG, b.toString()); - return null; - }); - } + private QuickJSContext ctx; - private void initLocal() { - jsContext.evaluate("var local = {};"); - JSObject local = (JSObject) jsContext.getGlobalObject().getProperty("local"); - local.setProperty("get", args -> { - SharedPreferences pref = context.getSharedPreferences("js_engine_" + args[0].toString(), Context.MODE_PRIVATE); - return pref.getString(args[1].toString(), ""); - }); - local.setProperty("set", args -> { - SharedPreferences pref = context.getSharedPreferences("js_engine_" + args[0].toString(), Context.MODE_PRIVATE); - pref.edit().putString(args[1].toString(), args[2].toString()).apply(); - return null; - }); - local.setProperty("delete", args -> { - SharedPreferences pref = context.getSharedPreferences("js_engine_" + args[0].toString(), Context.MODE_PRIVATE); - pref.edit().remove(args[1].toString()).apply(); - return null; - }); + static { + System.loadLibrary("quickjs"); } - private void initReq() { - jsContext.getGlobalObject().setProperty("req", args -> { - JSObject jsObject = jsContext.createNewJSObject(); - JSObject jsHeader = jsContext.createNewJSObject(); - setReqObject(jsObject, jsHeader, args); - return jsObject; + public Loader() { + Worker.submit(() -> { + JSModule.setModuleLoader(name -> Module.get().load(name)); + initCtx(); }); } - private void setReqObject(JSObject jsObject, JSObject jsHeader, Object... args) { - try { - JSONObject obj = new JSONObject(jsContext.stringify((JSObject) args[1])); - Response response = OkHttp.get().newCall(args[0].toString(), obj).execute(); - for (String name : response.headers().names()) jsHeader.setProperty(name, response.header(name)); - jsObject.setProperty("headers", jsHeader); - setReqContent(jsObject, obj, response); - } catch (Throwable e) { - jsObject.setProperty("headers", jsHeader); - jsObject.setProperty("content", ""); - } + private void initCtx() { + ctx = QuickJSContext.create(); + ctx.getGlobalObject().setProperty("console", Console.class); + ctx.getGlobalObject().setProperty("local", Local.class); + Global.create(ctx).setProperty(); } - private void setReqContent(JSObject jsObject, JSONObject obj, Response response) throws IOException { - switch (obj.optInt("buffer")) { - case 1: - byte[] bytes = response.body().bytes(); - JSArray array = jsContext.createNewJSArray(); - for (int i = 0; i < bytes.length; i++) array.set(bytes[i], i); - jsObject.setProperty("content", array); - break; - case 2: - jsObject.setProperty("content", Base64.encodeToString(response.body().bytes(), Base64.DEFAULT)); - break; - default: - jsObject.setProperty("content", response.body().string()); - break; - } + public Spider spider(String api, String ext) { + return new Spider(ctx, api, ext); } } diff --git a/drpy/src/main/java/com/hiker/drpy/Module.java b/drpy/src/main/java/com/hiker/drpy/Module.java new file mode 100644 index 000000000..7f2f71a10 --- /dev/null +++ b/drpy/src/main/java/com/hiker/drpy/Module.java @@ -0,0 +1,29 @@ +package com.hiker.drpy; + +import com.hiker.drpy.net.OkHttp; + +import java.util.concurrent.ConcurrentHashMap; + +public class Module { + + private final ConcurrentHashMap cache; + + private static class Loader { + static volatile Module INSTANCE = new Module(); + } + + public static Module get() { + return Loader.INSTANCE; + } + + public Module() { + this.cache = new ConcurrentHashMap<>(); + } + + public String load(String name) { + if (cache.contains(name)) return cache.get(name); + String content = OkHttp.get().module(name); + cache.put(name, content); + return content; + } +} diff --git a/drpy/src/main/java/com/hiker/drpy/Spider.java b/drpy/src/main/java/com/hiker/drpy/Spider.java index e2dd43170..dcc9dbedb 100644 --- a/drpy/src/main/java/com/hiker/drpy/Spider.java +++ b/drpy/src/main/java/com/hiker/drpy/Spider.java @@ -1,6 +1,78 @@ package com.hiker.drpy; +import android.content.Context; + +import com.github.tvbox.quickjs.JSObject; +import com.github.tvbox.quickjs.QuickJSContext; + +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + public class Spider extends com.github.catvod.crawler.Spider { + private QuickJSContext ctx; + private JSObject jsObject; + private String api; + private String ext; + private String key; + + public Spider(QuickJSContext ctx, String api, String ext) { + this.key = "__" + UUID.randomUUID().toString().replace("-", "") + "__"; + this.ctx = ctx; + this.api = api; + this.ext = ext; + } + + private String getContent() { + return Module.get().load(api) + .replace("export default{", "globalThis." + key + " ={") + .replace("export default {", "globalThis." + key + " ={") + .replace("__JS_SPIDER__", "globalThis." + key); + } + + @Override + public void init(Context context, String extend) throws Exception { + super.init(context, extend); + Worker.submit(() -> { + ctx.evaluateModule(getContent(), api); + jsObject = (JSObject) ctx.getProperty(ctx.getGlobalObject(), key); + jsObject.getJSFunction("init").call(ext); + }); + } + + private String post(String func, Object... args) throws ExecutionException, InterruptedException { + return (String) Worker.submit(() -> (String) jsObject.getJSFunction(func).call(args)).get(); + } + + @Override + public String homeContent(boolean filter) throws Exception { + return post("home", filter); + } + + @Override + public String homeVideoContent() throws Exception { + return post("homeVod"); + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) throws Exception { + return post("category", tid, pg, filter, null); + } + + @Override + public String detailContent(List ids) throws Exception { + return post("detail", ids.get(0)); + } + + @Override + public String searchContent(String key, boolean quick) throws Exception { + return post("search", key, quick); + } + @Override + public String playerContent(String flag, String id, List vipFlags) throws Exception { + return post("play", flag, id, null); + } } diff --git a/drpy/src/main/java/com/hiker/drpy/Worker.java b/drpy/src/main/java/com/hiker/drpy/Worker.java new file mode 100644 index 000000000..2fd4361b9 --- /dev/null +++ b/drpy/src/main/java/com/hiker/drpy/Worker.java @@ -0,0 +1,31 @@ +package com.hiker.drpy; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class Worker { + + private final ExecutorService executor; + + private static class Loader { + static volatile Worker INSTANCE = new Worker(); + } + + private static Worker get() { + return Loader.INSTANCE; + } + + public Worker() { + executor = Executors.newSingleThreadExecutor(); + } + + public static void submit(Runnable runnable) { + get().executor.submit(runnable); + } + + public static Future submit(Callable callable) { + return get().executor.submit(callable); + } +} diff --git a/drpy/src/main/java/com/hiker/drpy/method/Console.java b/drpy/src/main/java/com/hiker/drpy/method/Console.java new file mode 100644 index 000000000..9e3c905cc --- /dev/null +++ b/drpy/src/main/java/com/hiker/drpy/method/Console.java @@ -0,0 +1,15 @@ +package com.hiker.drpy.method; + +import android.util.Log; + +import com.github.tvbox.quickjs.JSMethod; + +public class Console { + + private final String TAG = Console.class.getSimpleName(); + + @JSMethod + public void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/drpy/src/main/java/com/hiker/drpy/method/Global.java b/drpy/src/main/java/com/hiker/drpy/method/Global.java new file mode 100644 index 000000000..65b88243a --- /dev/null +++ b/drpy/src/main/java/com/hiker/drpy/method/Global.java @@ -0,0 +1,126 @@ +package com.hiker.drpy.method; + +import android.text.TextUtils; +import android.util.Base64; + +import com.github.tvbox.quickjs.JSArray; +import com.github.tvbox.quickjs.JSMethod; +import com.github.tvbox.quickjs.JSObject; +import com.github.tvbox.quickjs.QuickJSContext; +import com.google.gson.Gson; +import com.hiker.drpy.Parser; +import com.hiker.drpy.net.OkHttp; + +import org.json.JSONObject; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; + +import okhttp3.Response; + +public class Global { + + private final QuickJSContext ctx; + + public static Global create(QuickJSContext jsContext) { + return new Global(jsContext); + } + + private Global(QuickJSContext ctx) { + this.ctx = ctx; + } + + public void setProperty() { + for (Method method : getClass().getMethods()) { + if (!method.isAnnotationPresent(JSMethod.class)) continue; + ctx.getGlobalObject().setProperty(method.getName(), args -> { + try { + return method.invoke(this, args); + } catch (IllegalAccessException | InvocationTargetException e) { + return null; + } + }); + } + } + + @JSMethod + public JSObject req(String url, JSObject object) { + try { + JSObject jsObject = ctx.createNewJSObject(); + JSObject jsHeader = ctx.createNewJSObject(); + JSONObject obj = new JSONObject(ctx.stringify(object)); + Response response = OkHttp.get().newCall(url, obj).execute(); + for (String name : response.headers().names()) jsHeader.setProperty(name, response.header(name)); + jsObject.setProperty("headers", jsHeader); + setReqContent(jsObject, obj, response); + return jsObject; + } catch (Throwable e) { + return null; + } + } + + @JSMethod + public String pd(String html, String rule, String urlKey) { + try { + return Parser.parseDomForUrl(html, rule, urlKey); + } catch (Throwable e) { + e.printStackTrace(); + return ""; + } + } + + @JSMethod + public JSObject pdfa(String html, String rule) { + try { + return ctx.parseJSON(new Gson().toJson(Parser.parseDomForList(html, rule))); + } catch (Throwable e) { + e.printStackTrace(); + return null; + } + } + + @JSMethod + public String pdfh(String html, String rule) { + try { + return Parser.parseDomForUrl(html, rule, ""); + } catch (Throwable e) { + e.printStackTrace(); + return ""; + } + } + + @JSMethod + public JSObject pdfl(String html, String p1, String list_text, String list_url, String urlKey) { + return null; + } + + @JSMethod + public String joinUrl(String parent, String child) { + try { + if (TextUtils.isEmpty(parent)) return child; + return new URL(new URL(parent), child).toString(); + } catch (Throwable e) { + e.printStackTrace(); + return ""; + } + } + + private void setReqContent(JSObject jsObject, JSONObject obj, Response response) throws IOException { + switch (obj.optInt("buffer")) { + case 1: + byte[] bytes = response.body().bytes(); + JSArray array = ctx.createNewJSArray(); + for (int i = 0; i < bytes.length; i++) array.set(bytes[i], i); + jsObject.setProperty("content", array); + break; + case 2: + jsObject.setProperty("content", Base64.encodeToString(response.body().bytes(), Base64.DEFAULT)); + break; + default: + jsObject.setProperty("content", response.body().string()); + break; + } + } +} diff --git a/drpy/src/main/java/com/hiker/drpy/method/Local.java b/drpy/src/main/java/com/hiker/drpy/method/Local.java new file mode 100644 index 000000000..42f1b4c38 --- /dev/null +++ b/drpy/src/main/java/com/hiker/drpy/method/Local.java @@ -0,0 +1,26 @@ +package com.hiker.drpy.method; + +import com.github.tvbox.quickjs.JSMethod; + +import java.util.HashMap; +import java.util.Map; + +public class Local { + + private final Map maps = new HashMap<>(); + + @JSMethod + public String get(String R_KEY, String k) { + return maps.get("js_engine_" + R_KEY + "_" + k); + } + + @JSMethod + public void set(String R_KEY, String k, String v) { + maps.put("js_engine_" + R_KEY + "_" + k, v); + } + + @JSMethod + public void delete(String R_KEY, String k) { + maps.remove("js_engine_" + R_KEY + "_" + k); + } +} diff --git a/drpy/src/main/java/com/hiker/drpy/net/OkHttp.java b/drpy/src/main/java/com/hiker/drpy/net/OkHttp.java index 45dacfaa0..86c502d86 100644 --- a/drpy/src/main/java/com/hiker/drpy/net/OkHttp.java +++ b/drpy/src/main/java/com/hiker/drpy/net/OkHttp.java @@ -42,6 +42,15 @@ public class OkHttp { return noRedirect; } + public String module(String url) { + try { + return client().newCall(new Request.Builder().url(url).header("User-Agent", "Mozilla/5.0").build()).execute().body().string(); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + public Call newCall(String url, JSONObject object) { int redirect = object.optInt("redirect", 1); int timeout = object.optInt("timeout", 10000);