TVBox官网
TVBox项目索引:https://github.com/o0HalfLife0o/TVBoxOSC/
完整代码参考,参见CSDN本地:https://download.csdn.net/download/zhiyuan411/89648187
源内容的结构
参见:catvod、TVBox源的格式解析及合并多个源的内容(Python脚本)
Spider参数覆盖问题
由下文的核心代码分析可知:
- 在站点类型为
type: 3
时,为Spider模式,无论是Jar或Js的子类型,都会依据Jar包来通过反射方式生成解析类。 - 在寻找Jar包时,站点的
jar
参数的优先级会大于源的spider
参数。当不存在站点的jar
参数时,才会使用源的spider
参数进行默认解析。 - 对不同源进行合并时,覆盖源的
spider
参数,对于类型为type: 3
且不带jar
参数的站点会造成解析错误! - 在
type: 3
之外的其他类型场景中,未发现对站点的jar
参数的使用和依赖。修改站点的jar
参数应该对其他类型常见并无影响。
Spider参数覆盖问题的解决脚本
解决思路:当源内容存在spider
参数时,则对所有的不自带jar
参数的站点添加jar
参数,并将其值设置为spider
参数值。
具体脚本参见:Python简记#5. 支持多种常见预处理的嵌套合并Json内容的脚本
附录:核心代码分析
首页
// https://github.com/takagen99/Box/blob/main/app/src/main/java/com/github/tvbox/osc/ui/activity/HomeActivity.java
// 订阅刷新事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void refresh(RefreshEvent event) {
// 检查事件类型是否为TYPE_PUSH_URL。
if (event.type == RefreshEvent.TYPE_PUSH_URL) {
// 检查是否存在名为"push_agent"的源。
if (ApiConfig.get().getSource("push_agent") != null) {
// 创建新的Intent以启动DetailActivity。
Intent newIntent = new Intent(mContext, DetailActivity.class);
// 将ID添加到Intent的额外数据中。
newIntent.putExtra("id", (String) event.obj);
// 将sourceKey添加到Intent的额外数据中。
newIntent.putExtra("sourceKey", "push_agent");
// 设置Intent的标志位。
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 启动DetailActivity。
// 注:这只是DetailActivity的一处启动来源,还有多处启动来源,比如:CollectActivity.java、FastSearchActivity.java、HistoryActivity.java、PushActivity.java、SearchActivity.java
HomeActivity.this.startActivity(newIntent);
}
} else if (event.type == RefreshEvent.TYPE_FILTER_CHANGE) {
// 检查当前视图是否非空。
if (currentView != null) {
// TODO: 实现过滤器图标显示的逻辑。
// showFilterIcon((int) event.obj);
}
}
}
详情页面
// https://github.com/takagen99/Box/blob/main/app/src/main/java/com/github/tvbox/osc/ui/activity/DetailActivity.java
// 跳转到播放界面
private void jumpToPlay() {
// 检查视频信息是否有效并且至少有一个可播放的剧集。
if (vodInfo != null && vodInfo.seriesMap.get(vodInfo.playFlag).size() > 0) {
// 保存当前播放标志供将来参考。
preFlag = vodInfo.playFlag;
// 创建一个Bundle对象用于传递数据。
Bundle bundle = new Bundle();
// 保存历史记录。
insertVod(firstsourceKey, vodInfo);
// 将sourceKey添加到Bundle中。
// sourceKey是成员变量。
// 它是初始化时从bundle获取的,如果没有则默认置为空字符串。
// 初始化之后使用mVideo.sourceKey 进行了设置。
// mVideo = absXml.movie.videoList.get(0)
// absXml是AbsXml类型,是刷新变化时解析好后传下来的实例
// @XStreamAlias("rss")
// public class AbsXml implements Serializable {
// @XStreamAlias("list")
// public Movie movie;
// @XStreamAlias("msg")
// public String msg;
// }
// @XStreamAlias("list")
// public class Movie implements Serializable {
// @XStreamAsAttribute
// public int page;
// @XStreamAsAttribute
// public int pagecount;//总页数
// @XStreamAsAttribute
// public int pagesize;
// @XStreamAsAttribute
// public int recordcount;//总条数
// @XStreamImplicit(itemFieldName = "video")
// public List<Video> videoList;
// @XStreamAlias("video")
// public static class Video implements Serializable {
// @XStreamAlias("last")//时间
// public String last;
// @XStreamAlias("id")//内容id
// public String id;
// @XStreamAlias("tid")//父级id
// public int tid;
// @XStreamAlias("name")//影片名称 <![CDATA[老爸当家]]>
// public String name;
// @XStreamAlias("type")//类型名称
// public String type;
// /*@XStreamAlias("dt")//视频分类 zuidam3u8,zuidall
// public String dt;*/
// @XStreamAlias("pic")//图片
// public String pic;
// @XStreamAlias("lang")//语言
// public String lang;
// @XStreamAlias("area")//地区
// public String area;
// @XStreamAlias("year")//年份
// public int year;
// @XStreamAlias("state")
// public String state;
// @XStreamAlias("note")//描述集数或者影片信息<![CDATA[共40集]]>
// public String note;
// @XStreamAlias("actor")//演员<![CDATA[张国立,蒋欣,高鑫,曹艳艳,王维维,韩丹彤,孟秀,王新]]>
// public String actor;
// @XStreamAlias("director")//导演<![CDATA[陈国星]]>
// public String director;
// @XStreamAlias("dl")
// public UrlBean urlBean;
// @XStreamAlias("des")
// public String des;// <![CDATA[权来]
// public String sourceKey;
// @XStreamAlias("tag")
// public String tag;
bundle.putString("sourceKey", sourceKey);
// 将视频信息序列化并添加到Bundle中。
// vodInfo是VodInfo类型,成员变量。
// 它是初始化时新建的对象实例,填充了:setVideo(mVideo)、sourceKey = mVideo.sourceKey
bundle.putSerializable("VodInfo", vodInfo);
// 如果需要预览模式,则进行预览准备。
if (showPreview) {
// 如果预览信息尚未创建,则创建一个预览信息的副本。
if (previewVodInfo == null) {
try {
// 创建字节输出流和对象输出流。
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 将vodInfo对象写入字节流。
oos.writeObject(vodInfo);
oos.flush();
oos.close();
// 创建字节输入流和对象输入流。
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
// 从字节流中读取并反序列化得到vodInfo对象。
previewVodInfo = (VodInfo) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
// 如果预览信息已经创建,则复制播放配置和播放信息。
if (previewVodInfo != null) {
previewVodInfo.playerCfg = vodInfo.playerCfg;
previewVodInfo.playFlag = vodInfo.playFlag;
previewVodInfo.playIndex = vodInfo.playIndex;
previewVodInfo.playGroup = vodInfo.playGroup;
previewVodInfo.reverseSort = vodInfo.reverseSort;
previewVodInfo.playGroupCount = vodInfo.playGroupCount;
previewVodInfo.seriesMap = vodInfo.seriesMap;
// 将预览信息序列化并添加到Bundle中。
bundle.putSerializable("VodInfo", previewVodInfo);
}
// 设置预览Fragment的数据。
playFragment.setData(bundle);
} else {
// 如果不需要预览模式,则跳转到播放活动。
// 在 bundle 中初始化了sourceKey、VodInfo信息,会传递给PlayActivity
jumpActivity(PlayActivity.class, bundle);
}
}
}
播放界面
// https://github.com/takagen99/Box/blob/main/app/src/main/java/com/github/tvbox/osc/ui/activity/PlayActivity.java
// 启动视频播放过程,并包含了处理不同URL格式、头部信息以及迅雷链接回调等功能
public void play(boolean reset) {
// 从系列映射中获取当前正在播放的剧集,使用播放标志和播放索引。
VodInfo.VodSeries vs = mVodInfo.seriesMap.get(mVodInfo.playFlag).get(mVodInfo.getplayIndex());
// 发布一个事件以刷新UI并更新当前播放索引。
EventBus.getDefault().post(new RefreshEvent(RefreshEvent.TYPE_REFRESH, mVodInfo.getplayIndex()));
// 发布另一个事件来通知刷新,并附带视频名称和剧集名称。
EventBus.getDefault().post(new RefreshEvent(RefreshEvent.TYPE_REFRESH_NOTIFY, mVodInfo.name + "&&" + vs.name));
// 创建一个包含视频名称和剧集名称的字符串。
String playTitleInfo = mVodInfo.name + " : " + vs.name;
// 设置提示信息,指示正在获取播放信息。
setTip("正在获取播放信息", true, false);
// 设置控制器的标题为播放标题信息。
mController.setTitle(playTitleInfo);
// 停止解析器。
stopParse();
// 初始化解析加载器。
initParseLoadFound();
// 如果视频视图不为空,则释放它。
if (mVideoView != null) mVideoView.release();
// 创建字幕缓存键。
subtitleCacheKey = mVodInfo.sourceKey + "-" + mVodInfo.id + "-" + mVodInfo.playFlag + "-" + mVodInfo.getplayIndex() + "-" + vs.name + "-subt";
// 创建进度键。
progressKey = mVodInfo.sourceKey + mVodInfo.id + mVodInfo.playFlag + mVodInfo.getplayIndex();
// 如果是重播,则清除现有的进度。
if (reset) {
CacheManager.delete(MD5.string2MD5(progressKey), 0);
CacheManager.delete(MD5.string2MD5(subtitleCacheKey), "");
}
// 处理 tvbox-drive:// 开头的 URL。
if (vs.url.startsWith("tvbox-drive://")) {
progressKey = vs.url.replace("tvbox-drive://", "");
// takagen99: 快速修复在 tvbox-drive 播放中的媒体选项问题
initPlayerDrive();
mController.showParse(false);
// 如果存在播放配置,则根据配置创建头部信息。
HashMap<String, String> headers = null;
if (mVodInfo.playerCfg != null && mVodInfo.playerCfg.length() > 0) {
JsonObject playerConfig = JsonParser.parseString(mVodInfo.playerCfg).getAsJsonObject();
if (playerConfig.has("headers")) {
headers = new HashMap<>();
for (JsonElement headerEl : playerConfig.getAsJsonArray("headers")) {
JsonObject headerJson = headerEl.getAsJsonObject();
headers.put(headerJson.get("name").getAsString(), headerJson.get("value").getAsString());
}
}
}
// 播放 tvbox-drive:// URL 替换后的地址。
playUrl(vs.url.replace("tvbox-drive://", ""), headers);
return;
}
// 处理 tvbox-xg: 开头的 URL。
if (vs.url.startsWith("tvbox-xg:") && !TextUtils.isEmpty(vs.url.substring(9))) {
this.mController.showParse(false);
// 解码 tvbox-xg: 后面的内容并播放。
playUrl(Jianpian.JPUrlDec(vs.url.substring(9)), null);
return;
}
// 处理迅雷链接。
if (Thunder.play(vs.url, new Thunder.ThunderCallback() {
@Override
public void status(int code, String info) {
if (code < 0) {
// 如果状态码小于0,设置提示信息为错误信息,并显示错误提示。
setTip(info, false, true);
} else {
// 如果状态码大于等于0,设置提示信息为信息内容,并隐藏错误提示。
setTip(info, true, false);
}
}
@Override
public void list(Map<Integer, String> urlMap) {
// 这个方法未实现,可能用于处理返回的URL列表。
}
@Override
public void play(String url) {
// 当迅雷链接成功解析后,播放URL。
playUrl(url, null);
}
})) {
mController.showParse(false);
return;
}
// 获取播放信息,使用提供的源键、进度键、播放URL 和字幕缓存键。
// sourceKey是成员变量,其值是初始化时从详情页面传递过来的
// mVodInfo是成员变量,VodInfo类型,其值是初始化时从详情页面传递过来的
// progressKey在本函数内初始化
// vs在本函数内初始化
// subtitleCacheKey在本函数内初始化
// public class VodInfo implements Serializable {
// public String last;//时间
// //内容id
// public String id;
// //父级id
// public int tid;
// //影片名称 <![CDATA[老爸当家]]>
// public String name;
// //类型名称
// public String type;
// //视频分类zuidam3u8,zuidall
// public String dt;
// //图片
// public String pic;
// //语言
// public String lang;
// //地区
// public String area;
// //年份
// public int year;
// public String state;
// //描述集数或者影片信息<![CDATA[共40集]]>
// public String note;
// //演员<![CDATA[张国立,蒋欣,高鑫,曹艳艳,王维维,韩丹彤,孟秀,王新]]>
// public String actor;
// //导演<![CDATA[陈国星]]>
// public String director;
// public ArrayList<VodSeriesFlag> seriesFlags;
// public LinkedHashMap<String, List<VodSeries>> seriesMap;
// public String des;// <![CDATA[权来]
// public String playFlag = null;
// public int playIndex = 0;
// public int playGroup = 0;
// public int playGroupCount = 0;
// public String playNote = "";
// public String sourceKey;
// public String playerCfg = "";
// public boolean reverseSort = false;
// public static class VodSeries implements Serializable {
// public String name;
// public String url;
// public boolean selected;
sourceViewModel.getPlay(sourceKey, mVodInfo.playFlag, progressKey, vs.url, subtitleCacheKey);
}
获取播放信息
// https://github.com/takagen99/Box/blob/main/app/src/main/java/com/github/tvbox/osc/viewmodel/SourceViewModel.java
// 获取指定视频源的播放信息
public void getPlay(String sourceKey, String playFlag, String progressKey, String url, String subtitleKey) {
// 如果已有线程池,立即关闭它。
if (threadPoolGetPlay != null) threadPoolGetPlay.shutdownNow();
// 创建一个新的固定大小的线程池,大小为2。
threadPoolGetPlay = Executors.newFixedThreadPool(2);
// 创建一个Callable任务,用于异步获取播放信息。
Callable<JSONObject> callable = () -> {
// 如果当前线程被中断,则返回null。
if (Thread.currentThread().isInterrupted()) return null;
// 获取指定sourceKey的视频源信息。根据sourceKey来从ApiConfig的sourceBeanList中获取SourceBean
SourceBean sourceBean = ApiConfig.get().getSource(sourceKey);
// 获取视频源的类型。
int type = sourceBean.getType();
// 结果JSON对象,用于封装播放信息。
JSONObject result = null;
// 根据视频源的类型进行不同的处理。
if (type == 3) { // Spider
// 获取爬虫实例。
Spider sp = ApiConfig.get().getCSP(sourceBean);
// 使用爬虫获取播放内容。
String json = sp.playerContent(playFlag, url, ApiConfig.get().getVipParseFlags());
// 将结果转换为JSON对象。
result = new JSONObject(json);
} else if (type == 0 || type == 1) { // 0 xml 1 json
// 创建一个空的JSON对象。
result = new JSONObject();
// 获取源内容里的站点的播放URL。
String playUrl = sourceBean.getPlayerUrl().trim();
// url是入参,表示视频的实际url。
// isVideoFormat()函数是判断url是否匹配视频格式的URL(比如结尾后缀等)
// 判断是否需要解析。仅当实际url是视频格式URL,且播放URL为空时,才不需要解析(使用实际url)
boolean parse = DefaultConfig.isVideoFormat(url) && playUrl.isEmpty();
// 设置是否需要解析的标志。
result.put("parse", BooleanUtils.toInteger(!parse));
// 设置播放URL。
result.put("url", url);
} else if (type == 4) {
// 对站点的api网址发起HTTP GET请求获取播放信息。
okhttp3.Response response = OkGo.<String>get(sourceBean.getApi())
.params("play", url)
.params("flag", playFlag)
.tag("play")
.execute();
// 读取响应体并转换为JSON对象。
String json = response.body().string();
result = new JSONObject(json);
}
// 如果结果不为空,则添加额外的键值对。
if (result != null) {
result.put("key", url); // 播放URL键值
result.put("proKey", progressKey); // 进度键值
result.put("subtKey", subtitleKey); // 字幕键值
if (!result.has("flag")) // 如果结果中没有播放标志,则添加播放标志。
result.put("flag", playFlag);
}
// 返回结果JSON对象。
return result;
};
// 提交Callable任务到线程池执行。
threadPoolGetPlay.execute(() -> {
// 提交Callable任务并获取Future对象。
Future<JSONObject> future = threadPoolGetPlay.submit(callable);
try {
// 等待最多15秒获取结果。
JSONObject jsonObject = future.get(15, TimeUnit.SECONDS);
// 将结果发布给LiveData观察者。
playResult.postValue(jsonObject);
} catch (Throwable e) {
// 如果发生异常,打印堆栈跟踪,并发布null结果。
e.printStackTrace();
playResult.postValue(null);
}
});
}
源内容配置类
// https://github.com/takagen99/Box/blob/main/app/src/main/java/com/github/tvbox/osc/api/ApiConfig.java
// 获取与给定的SourceBean相关的爬虫(Spider)实例
public Spider getCSP(SourceBean sourceBean) {
// 检查视频源的API是否以".js"结尾或包含".js?"。
if (sourceBean.getApi().endsWith(".js") || sourceBean.getApi().contains(".js?")) {
// 如果API与JavaScript相关,则从jsLoader中获取爬虫实例。
return jsLoader.getSpider(sourceBean.getKey(), sourceBean.getApi(), sourceBean.getExt(), sourceBean.getJar());
}
// 如果API不是JavaScript相关,则从jarLoader中获取爬虫实例。
return jarLoader.getSpider(sourceBean.getKey(), sourceBean.getApi(), sourceBean.getExt(), sourceBean.getJar());
}
Jar包解析类
// https://github.com/takagen99/Box/blob/main/app/src/main/java/com/github/catvod/crawler/JarLoader.java
// 根据给定的参数获取或创建一个Spider实例
public Spider getSpider(String key, String cls, String ext, String jar) {
// 移除爬虫类名前缀"csp_"。
String clsKey = cls.replace("csp_", "");
// 初始化jar包相关信息。
String jarUrl = "";
String jarMd5 = "";
String jarKey = "";
// 如果未提供jar包路径,则使用默认的主jar包。
if (jar.isEmpty()) {
jarKey = "main";
} else {
// 分割jar包路径和MD5校验码。
String[] urls = jar.split(";md5;");
jarUrl = urls[0];
// 生成jar包路径的MD5作为唯一标识。
jarKey = MD5.string2MD5(jarUrl);
// 获取MD5校验码。
jarMd5 = urls.length > 1 ? urls[1].trim() : "";
}
// 更新最近使用的jar包标识。
recentJarKey = jarKey;
// 如果已经存在与key关联的Spider实例,则直接返回。
if (spiders.containsKey(key))
return spiders.get(key);
// 加载类加载器。
DexClassLoader classLoader = null;
// 如果使用的是主jar包,则从缓存中获取类加载器。
if (jarKey.equals("main"))
classLoader = classLoaders.get("main");
else {
// 如果使用的是其他jar包,则加载新的类加载器。
// loadJarInternal是成员函数,它会根据提供的jar文件URL下载并加载jar文件到DexClassLoader中
// ,如果缓存中已有对应的加载器则直接返回
// ,否则下载文件、保存到缓存并加载
// ,最终返回对应的DexClassLoader实例。
classLoader = loadJarInternal(jarUrl, jarMd5, jarKey);
}
// 如果类加载器为空,则返回一个空的Spider实例。
if (classLoader == null)
return new SpiderNull();
try {
// 对Jar包内的clsKey类使用反射机制创建爬虫类的实例。
Spider sp = (Spider) classLoader.loadClass("com.github.catvod.spider." + clsKey).newInstance();
// 调用clsKey类的init()函数来初始化爬虫。
sp.init(App.getInstance(), ext);
// 如果提供了jar包路径,则调用homeContent方法。
if (!jar.isEmpty()) {
sp.homeContent(false); // 增加此行 应该可以解决部分写的有问题源的历史记录问题 但会增加这个源的首次加载时间 不需要可以已删掉
}
// 将爬虫实例缓存起来。
spiders.put(key, sp);
// 返回爬虫实例。
return sp;
} catch (Throwable th) {
// 如果发生异常,则打印堆栈跟踪。
th.printStackTrace();
}
// 如果出现异常或其他问题,则返回一个空的Spider实例。
return new SpiderNull();
}
Js解析类
// https://github.com/takagen99/Box/blob/main/app/src/main/java/com/github/catvod/crawler/JsLoader.java
// 根据给定的参数获取或创建一个Spider实例
public Spider getSpider(String key, String api, String ext, String jar) {
// 如果提供了jar包路径,则加载类加载器。
Class<?> classLoader = null;
if (!jar.isEmpty()) {
// 分割jar包路径和MD5校验码。
String[] urls = jar.split(";md5;");
String jarUrl = urls[0];
// 生成jar包路径的MD5作为唯一标识。
String jarKey = MD5.string2MD5(jarUrl);
// 获取MD5校验码。
String jarMd5 = urls.length > 1 ? urls[1].trim() : "";
// 优先使用缓存中已有对应的加载器
// 否则,根据提供的jar文件URL下载、缓存并加载jar文件到DexClassLoader中
classLoader = loadJarInternal(jarUrl, jarMd5, jarKey);
}
// 更新最近使用的jar包标识。
recentJarKey = key;
// 如果已经存在与key关联的Spider实例,则直接返回。
if (spiders.containsKey(key))
return spiders.get(key);
try {
// 创建爬虫类的实例。
// public class JsSpider extends Spider
Spider sp = new JsSpider(key, api, classLoader);
// 初始化爬虫。
sp.init(App.getInstance(), ext);
// 将爬虫实例缓存起来。
spiders.put(key, sp);
// 返回爬虫实例。
return sp;
} catch (Throwable th) {
// 如果发生异常,则打印堆栈跟踪。
th.printStackTrace();
// 记录错误日志。
LOG.e("QuJS", th);
}
// 如果出现异常或其他问题,则返回一个空的Spider实例。
return new SpiderNull();
}
Spider
// https://github.com/takagen99/Box/blob/main/app/src/main/java/com/github/tvbox/osc/util/js/JsSpider.java
public class JsSpider extends Spider {
...
// 提交一个Runnable任务到线程池中执行
private void submit(Runnable runnable) {
executor.submit(runnable);
}
// 提交一个Callable任务到线程池中执行,并返回Future对象
private <T> Future<T> submit(Callable<T> callable) {
return executor.submit(callable);
}
// 使用CompletableFuture来异步调用JavaScript对象上的方法,并返回结果
private Object call(String func, Object... args) throws Exception {
//return executor.submit((FunCall.call(jsObject, func, args))).get();
return CompletableFuture.supplyAsync(() -> Async.run(jsObject, func, args), executor).join().get();
}
// 构造函数
public JsSpider(String key, String api, Class<?> cls) throws Exception {
this.key = "J" + MD5.encode(key);
this.executor = Executors.newSingleThreadExecutor();
this.api = api;
this.dex = cls;
initializeJS();
}
// 初始化JavaScript环境
private void initializeJS() throws Exception {
// 提交任务到线程池执行。
submit(() -> {
// 如果JavaScript上下文不存在,则创建。
if (ctx == null) createCtx();
// 如果DexClassLoader不存在,则创建。
if (dex != null) createDex();
// 加载API地址的内容。
String content = FileUtils.loadModule(api);
// 如果内容为空,则直接返回。
if (TextUtils.isEmpty(content)) {
return null;
}
// 处理以 "//bb" 开头的内容。
if (content.startsWith("//bb")) {
// 设置标志位。
cat = true;
// 解码Base64编码的内容。
byte[] b = Base64.decode(content.replace("//bb", ""), 0);
// 在JavaScript上下文中执行解码后的字节流。
ctx.execute(byteFF(b), key + ".js");
// 评估并执行模块代码,将全局上下文中的变量绑定到指定名称。
ctx.evaluateModule(String.format(SPIDER_STRING_CODE, key + ".js") + "globalThis." + key + " = __JS_SPIDER__;", "tv_box_root.js");
// 注释掉的代码,用于备选处理方式。
//ctx.execute(byteFF(b), key + ".js", "__jsEvalReturn");
//ctx.evaluate("globalThis." + key + " = __JS_SPIDER__;");
} else {
// 替换 "__JS_SPIDER__ =" 为 "export default"。
if (content.contains("__JS_SPIDER__")) {
content = content.replaceAll("__JS_SPIDER__\\s*=", "export default ");
}
// 初始化模块扩展名。
String moduleExtName = "default";
// 如果内容包含 "__jsEvalReturn" 并且没有 "export default",则设置模块扩展名并标记。
if (content.contains("__jsEvalReturn") && !content.contains("export default")) {
moduleExtName = "__jsEvalReturn";
cat = true;
}
// 评估并执行模块代码。
ctx.evaluateModule(content, api);
// 评估并执行模块代码,将全局上下文中的变量绑定到指定名称。
ctx.evaluateModule(String.format(SPIDER_STRING_CODE, api) + "globalThis." + key + " = __JS_SPIDER__;", "tv_box_root.js");
// 注释掉的代码,用于备选处理方式。
//ctx.evaluateModule(content, api, moduleExtName);
//ctx.evaluate("globalThis." + key + " = __JS_SPIDER__;");
}
// 获取JavaScript对象。
jsObject = (JSObject) ctx.get(ctx.getGlobalObject(), key);
// 返回 null。
return null;
}).get();
}
// 根据提供的参数获取播放内容
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
// 将VIP标志列表转换为JSArray。
JSArray array = submit(() -> new JSUtils<String>().toArray(ctx, vipFlags)).get();
// 调用JavaScript中的 "play" 方法,并传递参数。
// 返回方法调用的结果,即播放内容的字符串表示形式。
return (String) call("play", flag, id, array);
}
...
}
注:Jar包类型Spider在在Jar包内有具体的实现类。