更新
This commit is contained in:
35
README.md
35
README.md
@@ -1,43 +1,44 @@
|
||||
# uz影视
|
||||
# uz 影视
|
||||
|
||||
**追剧、直播、无广、投屏、免费**
|
||||
|
||||
**iOS & Android**
|
||||
**iOS & Android & Win & Mac**
|
||||
|
||||
- 频道 [t.me/uzvideoplay](https://t.me/uzvideoplay)
|
||||
- 群组 [t.me/uzVideoApp](https://t.me/uzVideoApp)
|
||||
- 下载 [123 云盘](https://www.123865.com/s/J0HtVv-QUUxA)
|
||||
|
||||
### 扩展仓库
|
||||
|
||||
https://github.com/YYDS678/uzVideo-extensions
|
||||
|
||||
### 视频源扩展
|
||||
|
||||
#### 视频源扩展
|
||||
> 添加方式
|
||||
|
||||
uz影视 -> 设置 -> 数据管理 -> 视频源 -> 小齿轮 -> 添加源列表 -> 输入链接 -> 确定
|
||||
uz 影视 -> 设置 -> 数据管理 -> 视频源 -> 小齿轮 -> 添加源列表 -> 输入链接 -> 确定
|
||||
|
||||
[大佬扩展源](https://ghp.ci/https://raw.githubusercontent.com/Yswag/uzVideo/main/js/spider_sources.json)
|
||||
|
||||
[视频源](https://ghp.ci/https://raw.githubusercontent.com/YYDS678/uzVideo/main/video_sources_default.json)
|
||||
|
||||
[IPTV 大佬 YanG-1989,已经内置](https://github.com/YanG-1989/m3u)
|
||||
|
||||
[IPTV 大佬 YueChan,已经内置](https://github.com/YueChan/Live)
|
||||
|
||||
[色色源](https://ghp.ci/https://raw.githubusercontent.com/YYDS678/uzVideo/main/video_sources_sese.json)
|
||||
|
||||
#### 首页推荐扩展
|
||||
> 添加方式
|
||||
### 直播
|
||||
|
||||
uz影视 -> 设置 -> 数据管理 -> 推荐扩展 -> 小齿轮 -> 添加 -> 输入链接 -> 确定
|
||||
[IPTV 大佬 YanG-1989](https://github.com/YanG-1989/m3u)
|
||||
⚠️ 请注意需要设置 user-agent
|
||||
|
||||
[豆瓣推荐首页](https://ghp.ci/https://raw.githubusercontent.com/YYDS678/uzVideo/refs/heads/main/js/recommendHome.json)
|
||||
[IPTV 大佬 YueChan](https://github.com/YueChan/Live)
|
||||
|
||||
# 编写 uz 可执行的扩展
|
||||
### 编写 uz 可执行的扩展
|
||||
|
||||
<https://github.com/YYDS678/uzVideo/tree/main/js>
|
||||
https://github.com/YYDS678/uzVideo-extensions
|
||||
|
||||
# 如有任何相关问题联系:[机器人](https://t.me/uzVideoAppbot)
|
||||
### 如有任何相关问题联系:[机器人](https://t.me/uzVideoAppbot)
|
||||
|
||||
# 数据源格式说明
|
||||
### 采集站源格式
|
||||
|
||||
## 采集站源格式
|
||||
```
|
||||
{
|
||||
"api": "采集地址",
|
||||
|
||||
164
js/README.md
164
js/README.md
@@ -1,164 +0,0 @@
|
||||
- [扩展说明](#扩展说明)
|
||||
- [uzUtils.js 提供网络、存储 等功能](#uzutilsjs-提供网络存储-等功能)
|
||||
- [uzVideo(视频源) 扩展运行说明](#uzvideo视频源-扩展运行说明)
|
||||
- [uzHome(首页推荐) 扩展运行说明](#uzhome首页推荐-扩展运行说明)
|
||||
- [panTools(网盘工具)扩展运行说明](#pantools网盘工具扩展运行说明)
|
||||
- [加密说明](#加密说明)
|
||||
- [修改记录](#修改记录)
|
||||
- [v1.6.20](#v1620)
|
||||
- [v1.6.00](#v1600)
|
||||
- [v1.5.50](#v1550)
|
||||
- [v1.5.40](#v1540)
|
||||
- [v1.4.00](#v1400)
|
||||
- [v1.3.00](#v1300)
|
||||
|
||||
# 扩展说明
|
||||
|
||||
1. 感谢您的关注,由于作者对 `js` 了解甚少,所以内置的代码不够全面。**如果您要编写 uz 可执行的扩展需要添加代码或其他问题,可以联系[机器人](https://t.me/uzVideoAppbot)**
|
||||
2. `uzVideo.js` 为视频源扩展,提供观看视频的能力。内部定义了一些类和方法,您需要在扩展中实现 `WebApiBase` 的所有方法。
|
||||
3. `uzHome.js` 为首页推荐扩展,提供视频推荐能力。
|
||||
4. uz 内部仅有一个运行时,所有 `js` 代码(包括扩展)都在一起加载执行。所以您的扩展内类名一定要特殊,实例名称也要特殊
|
||||
5. 不支持 `import` `export`
|
||||
6. 集成库可在 `uz3lib.js` 查看,如需添加其他库通用库请联系[机器人](https://t.me/uzVideoAppbot)
|
||||
7. 成对使用 `// ignore` uz 内部会忽略包裹的内容
|
||||
|
||||
## uzUtils.js 提供网络、存储 等功能
|
||||
|
||||
# uzVideo(视频源) 扩展运行说明
|
||||
|
||||
1. 执行每个方法都会为 `webSite` 进行赋值
|
||||
2. json 文件说明
|
||||
|
||||
```
|
||||
{
|
||||
"name": "名称",
|
||||
"codeID": "如果选择了加密请填写,由 uz 生成",
|
||||
"api": "扩展链接",
|
||||
"instance": "实例名称",
|
||||
"webSite": "视频站地址。当加载代码时会赋值给对象的 webSite 属性,用户可自行在 app 内修改",
|
||||
"remark": "备注",
|
||||
"noHistory": false, // *不开启*历史记录 默认false(即开启历史记录),用户可自行在 app 内修改
|
||||
"userAgent": "", // 设置播放ua 优先级低于 getVideoPlayUrl 返回ua,用户可自行在 app 内修改
|
||||
"isLock": false, // 是否上锁 默认false(即不上锁),用户可自行在 app 内修改
|
||||
"blockClassList": ["短剧"] // 屏蔽分类,用户可自行在 app 内修改
|
||||
}
|
||||
```
|
||||
|
||||
3. 流程图
|
||||
|
||||
```mermaid
|
||||
|
||||
graph TD
|
||||
|
||||
A[开始] --> A1[uz 调用 getClassList 获取一级分类] -->|返回数据 rep: RepVideoClassList| B[判断 rep.data 列表内 VideoClass 的 hasSubclass 是否为 true]
|
||||
|
||||
B -->|是,存在二级分类或者筛选列表| C[调用 getSubclassList 获取二级分类或筛选列表]
|
||||
|
||||
B -->|否,不存在二级分类| D[调用 getVideoList 获取视频列表]
|
||||
|
||||
|
||||
C --> C1[调用 getSubclassVideoList 获取二级分类视频列表或者筛选视频列表] -->|点击单个视频| E
|
||||
|
||||
|
||||
|
||||
E[调用 getVideoDetail 获取视频详情]
|
||||
|
||||
D -->|点击单个视频| E
|
||||
|
||||
E -->|点击某一集| F[调用 getVideoPlayUrl 获取播放链接]
|
||||
|
||||
F --> G[结束]
|
||||
|
||||
S[搜索] -->S1[调用 searchVideo 返回视频列表] -->|点击单个视频| E
|
||||
|
||||
```
|
||||
|
||||
# uzHome(首页推荐) 扩展运行说明
|
||||
|
||||
1. 固定实例名称为 `uzHomeJs` (例如 const uzHomeJs = new UZHomeJS();)
|
||||
2. json 文件说明
|
||||
|
||||
```
|
||||
{
|
||||
"name": "名称",
|
||||
"codeID": "如果选择了加密请填写,由 uz 生成",
|
||||
"url": "扩展链接"
|
||||
}
|
||||
```
|
||||
|
||||
3. 如需添加更多 UI 类型,请联系[机器人](https://t.me/uzVideoAppbot)
|
||||
4. 流程图
|
||||
|
||||
```mermaid
|
||||
|
||||
graph TD
|
||||
|
||||
A[开始] --> A1[uz 调用 getHome 获取首页 tab 分类] -->|返回数据 RepHome| B[调用 getTab 获取 tab 页数据]
|
||||
|
||||
B -->|返回 RepTabList.data | C[展示推荐视频数据]
|
||||
|
||||
B -->|返回 RepTabList.filter| D[展示筛选标签,随后调用 getFilterList] -->|返回 RepVideoList| E[展示筛选视频数据]
|
||||
|
||||
C --> F
|
||||
|
||||
E --> F
|
||||
|
||||
F[结束,点击视频 uz 开始搜索]
|
||||
|
||||
```
|
||||
|
||||
# panTools(网盘工具)扩展运行说明
|
||||
|
||||
1. 固定实例名称为 `uzPanToolsInstance`
|
||||
2. uz 运行时仅存在一个网盘工具,请尽量整合所有的解析在 PanTools
|
||||
3. 流程图
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[开始] --> B[uz 调用 getShareVideos 获取视频列表] --> C[uz 调用 getPlayInfo 获取播放信息] --> D[结束]
|
||||
|
||||
```
|
||||
|
||||
# 加密说明
|
||||
|
||||
1. 您的扩展代码由 uz 进行加密,并生成 `codeID` 用于扩展解密。
|
||||
2. `codeID` 每次都是随机生成,不可指定。请将 `codeID` 添加进 `json` 文件内。
|
||||
3. app 设置页 `sid` 为用户标识,卸载、重置等情况下可能会发生变化。
|
||||
4. `VerifyLink` `SaltApp2Backend` `SaltBackend2App` 为选填项,用于控制谁 `sid` 可以添加使用您开发的扩展。(仅在添加时进行验证)。
|
||||
5. 推荐使用 `cloudflare worker` 进行验证。
|
||||
6. 验证相关代码请见 `verifyServer.js` 请注意修改 `salt` 及 `sid` 验证逻辑。
|
||||
|
||||
# 修改记录
|
||||
|
||||
### v1.6.20
|
||||
|
||||
1. 新增 `toast(msg,duration)` 函数,展示提示
|
||||
|
||||
### v1.6.00
|
||||
|
||||
1. `VideoDetail` 去除 `quarkUrl` 新增 `panUrls` 网盘分享链接列表
|
||||
2. `WebApiBase`、`HomeTabModel` 新增 `uzTag` 字段用于存取环境变量,请勿修改值
|
||||
3. 新增 `getEnv(uzTag, key)` 函数用于读取环境变量
|
||||
4. 新增 `setEnv(uzTag, key, value, summary)` 用于新增或更新环境变量
|
||||
5. 新增 `goToVerify` 用于验证视频站,会自动保存 `cookie` 下次请求自动生效。
|
||||
|
||||
### v1.5.50
|
||||
|
||||
1. 支持使用 uz 加密扩展,加密后请将 `codeID` 填写在 `json` 文件内。
|
||||
2. 新增首页推荐扩展类型 `uzHome.js`
|
||||
|
||||
### v1.5.40
|
||||
|
||||
1. `req` 支持设置 `responseType` 值为字符串 `json、arraybuffer、bytes、plain、stream`
|
||||
2. `vod_pic` 支持 data url 格式
|
||||
3. `RepVideoPlayUrl.data` 支持 data url 格式
|
||||
|
||||
### v1.4.00
|
||||
|
||||
1. 增加二级分类和筛选列表功能
|
||||
|
||||
### v1.3.00
|
||||
|
||||
1. 去掉 `cat.js`, 更改为 `cheerio` `Crypto` `Encrypt` `parse(后期可能会移除,推荐优先使用 cheerio)`
|
||||
2. `VideoDetail` 新增 `quarkUrl` 支持夸克网盘
|
||||
3. `RepVideoPlayUrl` 新增 `headers` 支持设置播放 `header`
|
||||
4
js/core/CryptoJS.min.js
vendored
4
js/core/CryptoJS.min.js
vendored
File diff suppressed because one or more lines are too long
2
js/core/JSEncrypt.min.js
vendored
2
js/core/JSEncrypt.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +0,0 @@
|
||||
const cheerio = createCheerio();
|
||||
const Crypto = createCryptoJS();
|
||||
const Encrypt = loadJSEncrypt();
|
||||
// 推荐优先使用 cheerio, parse 后期可能会移除
|
||||
const parse = node_html_parser.parse;
|
||||
@@ -1,153 +0,0 @@
|
||||
/**
|
||||
* @file 首页推荐扩展
|
||||
*/
|
||||
|
||||
//MARK: - 列表展示 UI类型 如需添加更多 UI 类型请联系 https://t.me/uzVideoAppbot
|
||||
const UIType = {
|
||||
/**
|
||||
* 轮播海报
|
||||
*/
|
||||
banner: "banner",
|
||||
|
||||
/**
|
||||
* 横滑小海报
|
||||
*/
|
||||
smallCard: "smallCard",
|
||||
|
||||
/**
|
||||
* 横滑大海报
|
||||
*/
|
||||
largeCard: "largeCard",
|
||||
};
|
||||
|
||||
//MARK: - 单个广告位展示数据
|
||||
/**
|
||||
* UI 展示数据
|
||||
*/
|
||||
class RepAd {
|
||||
constructor() {
|
||||
/**
|
||||
* UI 类型
|
||||
* @type {UIType}
|
||||
*/
|
||||
this.uiType = UIType.smallCard;
|
||||
|
||||
/**
|
||||
* 海报宽高比
|
||||
*/
|
||||
this.ratio = 10.0 / 16.0;
|
||||
|
||||
/**
|
||||
* 标题 为空不展示标题
|
||||
* @type {string}
|
||||
*/
|
||||
this.title = "";
|
||||
|
||||
/**
|
||||
* 展示数据
|
||||
* @type {VideoDetail} 必要字段 vod_name、vod_pic,
|
||||
* 次要 vod_remarks(海报上的小标签),
|
||||
* 后续用于提高匹配度的字段 vod_year vod_director type_name
|
||||
*/
|
||||
this.data = [];
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - tab 列表数据
|
||||
/**
|
||||
* tab列表数据,data 与 filter 二选一
|
||||
* 当有 filter 时展示为筛选列表样式
|
||||
* 当有 data 时展示为普通列表样式
|
||||
*/
|
||||
class RepTabList {
|
||||
constructor() {
|
||||
/**
|
||||
* tab 列表数据
|
||||
* @type {Array <RepAd>}
|
||||
*/
|
||||
this.data = [];
|
||||
|
||||
/**
|
||||
* 筛选列表
|
||||
* @type {FilterTitle[]}
|
||||
*/
|
||||
this.filter = [];
|
||||
|
||||
this.error = "";
|
||||
}
|
||||
}
|
||||
|
||||
class HomeTabModel {
|
||||
constructor() {
|
||||
/**
|
||||
* 当前分类的链接
|
||||
**/
|
||||
this.id = "";
|
||||
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
this.name = "";
|
||||
|
||||
/**
|
||||
* 是否是筛选列表
|
||||
*/
|
||||
this.isFilter = false;
|
||||
|
||||
/**
|
||||
* 扩展运行标识 ** uzApp 运行时自动赋值,请勿修改 **
|
||||
*/
|
||||
this.uzTag = "";
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 首页数据
|
||||
/**
|
||||
* 首页数据
|
||||
*/
|
||||
class RepHome {
|
||||
constructor() {
|
||||
/**
|
||||
* 各 tab 页面名称,用于 getTab() 入参
|
||||
* @type {Array <HomeTabModel>}
|
||||
*/
|
||||
this.data = [];
|
||||
this.error = "";
|
||||
}
|
||||
}
|
||||
|
||||
//MARK:- 首页扩展类
|
||||
/**
|
||||
* 首页扩展,固定实例名称为 uzHomeJs,(例如 const uzHomeJs = new UZHomeJS();)
|
||||
*/
|
||||
class UZHome {
|
||||
/**
|
||||
* 获取首页
|
||||
* @returns {Promise<RepHome>}
|
||||
*/
|
||||
async getHome() {
|
||||
let repData = new RepHome();
|
||||
return JSON.stringify(repData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 tab
|
||||
* @param {UZArgs} args 主要参数 args.url args.page
|
||||
* @returns {Promise<RepTabList>}
|
||||
*/
|
||||
async getTab(args) {
|
||||
let repData = new RepTabList();
|
||||
return JSON.stringify(repData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取筛选列表数据
|
||||
* 当 getTab() 返回 RepTabList.filter 时调用
|
||||
* @param {UZSubclassVideoListArgs} args 主要参数 args.mainClassId(即 VideoClass.id) 、 args.filter( 按 getTab() 返回的 filter 顺序传入)
|
||||
* @returns {Promise<RepVideoList>}返回筛选列表
|
||||
*/
|
||||
async getFilterList(args) {
|
||||
let repData = new RepVideoList();
|
||||
return JSON.stringify(repData);
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/**
|
||||
* @file 工具类
|
||||
*/
|
||||
|
||||
class UZUtils {
|
||||
/**
|
||||
* 从链接中获取域名
|
||||
* @param {string} url
|
||||
* @returns
|
||||
*/
|
||||
static getHostFromURL(url) {
|
||||
const protocolEndIndex = url.indexOf("://");
|
||||
if (protocolEndIndex === -1) {
|
||||
return null;
|
||||
}
|
||||
const hostStartIndex = protocolEndIndex + 3;
|
||||
const hostEndIndex = url.indexOf("/", hostStartIndex);
|
||||
const host =
|
||||
hostEndIndex === -1
|
||||
? url.slice(hostStartIndex)
|
||||
: url.slice(hostStartIndex, hostEndIndex);
|
||||
|
||||
return `${url.slice(0, protocolEndIndex + 3)}${host}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除尾部的斜杠
|
||||
* @param {string} str
|
||||
* @returns
|
||||
*/
|
||||
static removeTrailingSlash(str) {
|
||||
if (str.endsWith("/")) {
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据正则表达式获取字符串
|
||||
* @param {*} pattern
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
static getStrByRegexDefault(pattern, str) {
|
||||
let matcher = pattern.exec(str);
|
||||
if (matcher !== null) {
|
||||
if (matcher.length >= 1) {
|
||||
if (matcher.length >= 1) return matcher[1];
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算最长公共子串
|
||||
* @param {string} s1
|
||||
* @param {string} s2
|
||||
* @returns
|
||||
*/
|
||||
static lcs(s1, s2) {
|
||||
const m = s1.length,
|
||||
n = s2.length;
|
||||
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
||||
let maxLength = 0,
|
||||
endIndex = 0;
|
||||
|
||||
for (let i = 1; i <= m; i++) {
|
||||
for (let j = 1; j <= n; j++) {
|
||||
if (s1[i - 1] === s2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + 1;
|
||||
if (dp[i][j] > maxLength) {
|
||||
maxLength = dp[i][j];
|
||||
endIndex = i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s1.substring(endIndex - maxLength + 1, endIndex + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找元素在数组中的位置
|
||||
* @param {Array} list
|
||||
* @param {string} element
|
||||
* @returns
|
||||
*/
|
||||
static findIndex(list, element) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i] === element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于在 uz 扩展调试模式中展示 log 信息
|
||||
*/
|
||||
static debugLog() {
|
||||
sendMessage("debugLog", JSON.stringify([...arguments]));
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 网络请求返回数据
|
||||
/**
|
||||
* req 返回的数据
|
||||
*/
|
||||
class ProData {
|
||||
constructor() {
|
||||
this.error = "";
|
||||
this.data;
|
||||
|
||||
/**
|
||||
* @type {object} 响应头
|
||||
*/
|
||||
this.headers;
|
||||
|
||||
/**
|
||||
* @type {number} 状态码
|
||||
*/
|
||||
this.code;
|
||||
|
||||
/**
|
||||
* @type {boolean} 是否成功
|
||||
*/
|
||||
this.ok = () => this.code === 200;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求响应类型
|
||||
*/
|
||||
const ReqResponseType = {
|
||||
json: "json",
|
||||
arraybuffer: "arraybuffer",
|
||||
bytes: "bytes",
|
||||
plain: "plain",
|
||||
stream: "stream",
|
||||
};
|
||||
|
||||
//MARK: - 网络请求
|
||||
/**
|
||||
* 网络请求
|
||||
* @param {string} url 请求的URL
|
||||
* @param {object} options 请求参数 {headers:{},method:"POST",data:{},responseType:ReqResponseType}
|
||||
* @returns {Promise<ProData>}
|
||||
*/
|
||||
async function req(url, options) {
|
||||
let pro = await sendMessage(
|
||||
"req",
|
||||
JSON.stringify({ url: url, options: options })
|
||||
);
|
||||
return pro;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取环境变量
|
||||
* @param {string} uzTag 直接传入扩展的 uzTag ,请勿修改
|
||||
* @param {string} key
|
||||
* @returns {@Promise<string>}
|
||||
*/
|
||||
async function getEnv(uzTag, key) {
|
||||
let res = await sendMessage(
|
||||
"getEnv",
|
||||
JSON.stringify({ uzTag: uzTag, key: key })
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入环境变量
|
||||
* @param {string} uzTag 直接传入扩展的 uzTag ,请勿修改
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
* @param {string} summary 描述,新增时建议传入。修改时不必传入
|
||||
*/
|
||||
async function setEnv(uzTag, key, value, summary) {
|
||||
let res = await sendMessage(
|
||||
"setEnv",
|
||||
JSON.stringify({ uzTag: uzTag, key: key, value: value, summary: summary })
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到验证页面,自动保存cookie
|
||||
* @param {string} url
|
||||
**/
|
||||
async function goToVerify(url) {
|
||||
await sendMessage("goToVerify", JSON.stringify({ url: url }));
|
||||
}
|
||||
|
||||
/**
|
||||
* toast 弹窗
|
||||
* @param {string} msg 提示信息
|
||||
* @param {number} duration 持续时间
|
||||
**/
|
||||
function toast(msg, duration = 2) {
|
||||
sendMessage("toast", JSON.stringify({ msg: msg, duration: duration }));
|
||||
}
|
||||
@@ -1,319 +0,0 @@
|
||||
/**
|
||||
* @file 视频源扩展
|
||||
*/
|
||||
|
||||
//MARK: - 筛选标签
|
||||
/**
|
||||
* 筛选标签
|
||||
*/
|
||||
class FilterLabel {
|
||||
constructor() {
|
||||
/**
|
||||
* 筛选名称
|
||||
*/
|
||||
this.name = "";
|
||||
/**
|
||||
* 标识值 根据情况赋值
|
||||
*/
|
||||
this.id = "";
|
||||
/**
|
||||
* 标识key 根据情况赋值
|
||||
*/
|
||||
this.key = "";
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 筛选标题
|
||||
/**
|
||||
* 筛选标题
|
||||
*/
|
||||
class FilterTitle {
|
||||
constructor() {
|
||||
// 筛选标题
|
||||
this.name = "";
|
||||
/**
|
||||
* 筛选标签列表
|
||||
* @type {FilterLabel[]}
|
||||
*/
|
||||
this.list = [];
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 视频分类
|
||||
/**
|
||||
* 视频分类
|
||||
*/
|
||||
class VideoClass {
|
||||
constructor() {
|
||||
// 当前分类的链接
|
||||
this.type_id = "";
|
||||
// 分类名称
|
||||
this.type_name = "";
|
||||
|
||||
/**
|
||||
* 是否存在 筛选列表、子分类。 存在会调用 getSubclassList
|
||||
*/
|
||||
this.hasSubclass = false;
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 视频二级分类
|
||||
/**
|
||||
* 视频二级分类,二级分类可以是 分类,也可以是筛选,都有值优先取筛选
|
||||
*/
|
||||
class VideoSubclass {
|
||||
constructor() {
|
||||
/**
|
||||
* 子分类
|
||||
* @type {VideoClass[]}
|
||||
*/
|
||||
this.class = [];
|
||||
/**
|
||||
* 筛选列表
|
||||
* 请求二级分类列表 getSubclassList 时返回该数据或者 data,
|
||||
* @type {FilterTitle[]}
|
||||
*/
|
||||
this.filter = [];
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 视频详情
|
||||
/**
|
||||
* 视频详情
|
||||
*/
|
||||
class VideoDetail {
|
||||
constructor() {
|
||||
// 当前视频详情链接
|
||||
this.vod_id = "";
|
||||
// 视频名称
|
||||
this.vod_name = "";
|
||||
/**
|
||||
* 线路列表 (没什么特殊区别可为空) 线路1$$$线路2$$$
|
||||
*/
|
||||
this.vod_play_from = "";
|
||||
/**
|
||||
* 所有剧集 使用 $$$ 分割线路,# 分割剧集,$ 分割剧集名称和剧集链接
|
||||
* 第一集$第一集的视频详情链接#第二集$第二集的视频详情链接$$$第一集$第一集的视频详情链接#第二集$第二集的视频详情链接
|
||||
*/
|
||||
this.vod_play_url = "";
|
||||
// 封面 支持 data:image/xxx;base64,
|
||||
this.vod_pic = "";
|
||||
// 视频分类
|
||||
this.type_name = "";
|
||||
// 更新到
|
||||
this.vod_remarks = "";
|
||||
// 豆瓣
|
||||
this.vod_douban_score = "";
|
||||
// 语言
|
||||
this.vod_lang = "";
|
||||
// 年份
|
||||
this.vod_year = "";
|
||||
// 演员
|
||||
this.vod_actor = "";
|
||||
// 导演
|
||||
this.vod_director = "";
|
||||
// 描述
|
||||
this.vod_content = "";
|
||||
// 地区
|
||||
this.vod_area = "";
|
||||
/**
|
||||
* 网盘分享链接列表
|
||||
* @type {string[]}
|
||||
*/
|
||||
this.panUrls = [];
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 分类列表数据
|
||||
/**
|
||||
* 返回分类列表
|
||||
*/
|
||||
class RepVideoClassList {
|
||||
constructor() {
|
||||
/**
|
||||
* @type {VideoClass[]}
|
||||
*/
|
||||
this.data = [];
|
||||
this.error = "";
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 二级分类列表/筛选列表数据
|
||||
/**
|
||||
* 返回二级分类列表(包括筛选列表)
|
||||
*/
|
||||
class RepVideoSubclassList {
|
||||
constructor() {
|
||||
/**
|
||||
* 二级分类数据
|
||||
* @type {VideoSubclass}
|
||||
*/
|
||||
this.data = new VideoSubclass();
|
||||
this.error = "";
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 视频列表数据
|
||||
/**
|
||||
* 返回视频列表
|
||||
*/
|
||||
class RepVideoList {
|
||||
constructor() {
|
||||
/**
|
||||
* @type {VideoDetail[]}
|
||||
*/
|
||||
this.data = [];
|
||||
this.error = "";
|
||||
this.total = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 视频详情数据
|
||||
/**
|
||||
* 返回视频详情
|
||||
*/
|
||||
class RepVideoDetail {
|
||||
constructor() {
|
||||
/**
|
||||
* @type {VideoDetail}
|
||||
*/
|
||||
this.data = null;
|
||||
this.error = "";
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 视频播放地址数据
|
||||
/**
|
||||
* 返回播放地址
|
||||
*/
|
||||
class RepVideoPlayUrl {
|
||||
constructor() {
|
||||
/**
|
||||
* 播放视频的URL 支持 data:xxx/xxx;base64,
|
||||
**/
|
||||
this.data = "";
|
||||
/**
|
||||
* 播放视频的请求header
|
||||
**/
|
||||
this.headers;
|
||||
this.error = "";
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 传入参数
|
||||
/**
|
||||
* UZArgs 封装一组参数,用于构建请求URL或进行数据查询。
|
||||
*/
|
||||
class UZArgs {
|
||||
constructor() {
|
||||
// 请求的URL
|
||||
this.url = "";
|
||||
// 当前页码
|
||||
this.page = 1;
|
||||
//搜索关键词
|
||||
this.searchWord = "";
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 二级分类传入参数
|
||||
/**
|
||||
* getSubclassVideoList 方法传入的参数
|
||||
*/
|
||||
class UZSubclassVideoListArgs extends UZArgs {
|
||||
constructor() {
|
||||
/**
|
||||
* 主分类ID 即扩展返回的 @type {RepVideoClassList}.data[0].type_id
|
||||
*/
|
||||
this.mainClassId = "";
|
||||
|
||||
/**
|
||||
* 二级分类ID 即扩展返回的 @type {RepVideoSubclassList}.data.class.type_id
|
||||
*/
|
||||
this.subclassId = "";
|
||||
|
||||
/**
|
||||
* 筛选标签,按返回的顺序传入 即扩展返回的 {RepVideoSubclassList}.data.filter.
|
||||
* @type {FilterLabel[]}
|
||||
*/
|
||||
this.filter = [];
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 视频源扩展基类
|
||||
/**
|
||||
* 扩展基类
|
||||
*/
|
||||
class WebApiBase {
|
||||
constructor() {
|
||||
/**
|
||||
* 网站主页
|
||||
**/
|
||||
this.webSite = "";
|
||||
|
||||
/**
|
||||
* 扩展运行标识 ** uzApp 运行时自动赋值,请勿修改 **
|
||||
*/
|
||||
this.uzTag = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步获取分类列表的方法。
|
||||
* @param {UZArgs} args
|
||||
* @returns {@Promise<JSON.stringify(new RepVideoClassList())>}
|
||||
*/
|
||||
async getClassList(args) {
|
||||
return JSON.stringify(new RepVideoClassList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二级分类列表筛选列表的方法。
|
||||
* @param {UZArgs} args
|
||||
* @returns {@Promise<JSON.stringify(new RepVideoSubclassList())>}
|
||||
*/
|
||||
async getSubclassList(args) {
|
||||
return JSON.stringify(new RepVideoSubclassList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类视频列表
|
||||
* @param {UZArgs} args
|
||||
* @returns {@Promise<JSON.stringify(new RepVideoList())>}
|
||||
*/
|
||||
async getVideoList(args) {
|
||||
return JSON.stringify(new RepVideoList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二级分类视频列表 或 筛选视频列表
|
||||
* @param {UZSubclassVideoListArgs} args
|
||||
* @returns {@Promise<JSON.stringify(new RepVideoList())>}
|
||||
*/
|
||||
async getSubclassVideoList(args) {
|
||||
return JSON.stringify(new RepVideoList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频详情
|
||||
* @param {UZArgs} args
|
||||
* @returns {@Promise<JSON.stringify(new RepVideoDetail())>}
|
||||
*/
|
||||
async getVideoDetail(args) {
|
||||
return JSON.stringify(new RepVideoDetail());
|
||||
}
|
||||
/**
|
||||
* 获取视频的播放地址
|
||||
* @param {UZArgs} args
|
||||
* @returns {@Promise<JSON.stringify(new RepVideoPlayUrl())>}
|
||||
*/
|
||||
async getVideoPlayUrl(args) {
|
||||
return JSON.stringify(new RepVideoPlayUrl());
|
||||
}
|
||||
/**
|
||||
* 搜索视频
|
||||
* @param {UZArgs} args
|
||||
* @returns {@Promise<JSON.stringify(new RepVideoList())>}
|
||||
*/
|
||||
async searchVideo(args) {
|
||||
return JSON.stringify(new RepVideoList());
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// TODO: 替换为自己的盐
|
||||
// 服务器接收来自 app 的请求的盐
|
||||
const saltApp2End = "123321";
|
||||
// 服务器发送数据到 app 的盐
|
||||
const saltEnd2App = "123321";
|
||||
|
||||
addEventListener("fetch", (event) => {
|
||||
event.respondWith(handleRequest(event.request));
|
||||
});
|
||||
|
||||
async function handleRequest(request) {
|
||||
let params;
|
||||
|
||||
try {
|
||||
params = await request.json();
|
||||
} catch (error) {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
if (!params || Object.keys(params).length === 0) {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
if (!(await _verifySignFromApp(params))) {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
if (!_verifyTimestamp(params.date)) {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
let responseData = {};
|
||||
let sid = params.sid;
|
||||
//TODO: 判断 sid 是否是允许的用户
|
||||
// 如果使用 cloudflare worker,可以使用 KV 存储 key: sid value:与用户定义的其它标识 例如邮箱
|
||||
if (sid) {
|
||||
responseData.pass = true;
|
||||
} else {
|
||||
responseData.pass = false;
|
||||
}
|
||||
|
||||
const signedResponse = await _signResponse2App(
|
||||
responseData,
|
||||
params.sessionId
|
||||
);
|
||||
|
||||
return new Response(JSON.stringify(signedResponse), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
async function _verifySignFromApp(params) {
|
||||
const { sign, ...otherParams } = params;
|
||||
if (
|
||||
!sign ||
|
||||
!otherParams.random ||
|
||||
!otherParams.date ||
|
||||
!otherParams.sessionId ||
|
||||
!otherParams.sid
|
||||
)
|
||||
return false;
|
||||
|
||||
const sortedParams = Object.entries(otherParams)
|
||||
.sort((a, b) => b[0].localeCompare(a[0]))
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join("&");
|
||||
|
||||
const salt =
|
||||
saltApp2End + otherParams.random.slice(-4) + otherParams.date.slice(-4);
|
||||
const signStr = salt + sortedParams;
|
||||
const calculatedSign = await _keyToSha256(signStr);
|
||||
|
||||
return calculatedSign === sign;
|
||||
}
|
||||
|
||||
async function _signResponse2App(data, sessionId) {
|
||||
const signMap = { ...data };
|
||||
// 64位随机数
|
||||
const random = crypto.randomUUID().slice(-64);
|
||||
const date = Date.now().toString();
|
||||
|
||||
signMap.random = random;
|
||||
signMap.date = date;
|
||||
signMap.sessionId = sessionId;
|
||||
|
||||
const sortedParams = Object.entries(signMap)
|
||||
.sort((a, b) => b[0].localeCompare(a[0]))
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join("&");
|
||||
|
||||
const salt = saltEnd2App + random.slice(-4) + date.slice(-4);
|
||||
const signStr = salt + sortedParams;
|
||||
signMap.sign = await _keyToSha256(signStr);
|
||||
|
||||
return signMap;
|
||||
}
|
||||
|
||||
async function _keyToSha256(input) {
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(input);
|
||||
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
||||
return Array.from(new Uint8Array(hashBuffer))
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
// 验证时间戳
|
||||
function _verifyTimestamp(timestamp) {
|
||||
// 时间为了保证任意时区都一致 所以使用格林威治时间
|
||||
const currentTimeString = new Date().toISOString();
|
||||
const currentTime = new Date(currentTimeString).getTime();
|
||||
const requestTime = parseInt(timestamp);
|
||||
const timeDifference = Math.abs(currentTime - requestTime);
|
||||
// 检查时间差是否小于2分钟(120000毫秒)
|
||||
return timeDifference < 120000;
|
||||
}
|
||||
133
js/package-lock.json
generated
133
js/package-lock.json
generated
@@ -1,133 +0,0 @@
|
||||
{
|
||||
"name": "jsbox",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"node-html-parser": "^6.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||
"dependencies": {
|
||||
"boolbase": "^1.0.0",
|
||||
"css-what": "^6.1.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"domutils": "^3.0.1",
|
||||
"nth-check": "^2.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/css-what": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
||||
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domelementtype": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||
"dependencies": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"bin": {
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/node-html-parser": {
|
||||
"version": "6.1.13",
|
||||
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz",
|
||||
"integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==",
|
||||
"dependencies": {
|
||||
"css-select": "^5.1.0",
|
||||
"he": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nth-check": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
||||
"dependencies": {
|
||||
"boolbase": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"node-html-parser": "^6.1.13"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "UC、夸克 网盘解析工具",
|
||||
"url": "https://ghp.ci/https://raw.githubusercontent.com/YYDS678/uzVideo/refs/heads/main/js/spider/panTools.js",
|
||||
"env": "UCCookie##用于播放UC网盘视频,请在网页获取UC网盘的Cookie&&夸克Cookie##用于播放Quark网盘视频,请在网页获取Quark网盘的Cookie"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "豆瓣推荐 影视推荐",
|
||||
"codeID": "f2GRQWVkMifmWtoE",
|
||||
"url": "https://ghp.ci/https://raw.githubusercontent.com/YYDS678/uzVideo/main/js/spider/recommendHome.txt"
|
||||
}
|
||||
@@ -1,362 +0,0 @@
|
||||
// ignore
|
||||
import { WebApiBase, VideoClass } from "../core/uzCode.js";
|
||||
import { parse } from "node-html-parser";
|
||||
// ignore
|
||||
|
||||
// 类名要特殊
|
||||
class ChangZhang20240614 extends WebApiBase {
|
||||
webSite = "https://www.czzy77.com";
|
||||
/**
|
||||
* 异步获取分类列表的方法。
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoClassList>}
|
||||
*/
|
||||
async getClassList(args) {
|
||||
let webUrl = args.url;
|
||||
// 如果通过首页获取分类的话,可以将对象内部的首页更新
|
||||
this.webSite = UZUtils.removeTrailingSlash(webUrl);
|
||||
var backData = new RepVideoClassList();
|
||||
try {
|
||||
const pro = await req(webUrl);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
if (proData) {
|
||||
var document = parse(proData);
|
||||
var allClass = document.querySelectorAll("ul.submenu_mi > li > a");
|
||||
var list = [];
|
||||
for (let index = 0; index < allClass.length; index++) {
|
||||
const element = allClass[index];
|
||||
var isIgnore = this.isIgnoreClassName(element.text);
|
||||
if (isIgnore) {
|
||||
continue;
|
||||
}
|
||||
var type_name = element.text;
|
||||
var url = element.attributes["href"];
|
||||
url = this.combineUrl(url);
|
||||
|
||||
if (url.length > 0 && type_name.length > 0) {
|
||||
var videoClass = new VideoClass();
|
||||
videoClass.type_id = url;
|
||||
videoClass.type_name = type_name;
|
||||
list.push(videoClass);
|
||||
}
|
||||
}
|
||||
backData.data = list;
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取分类失败~";
|
||||
}
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类视频列表
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoList>}
|
||||
*/
|
||||
async getVideoList(args) {
|
||||
var listUrl = UZUtils.removeTrailingSlash(args.url) + "/page/" + args.page;
|
||||
var backData = new RepVideoList();
|
||||
try {
|
||||
let pro = await req(listUrl, null);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
if (proData) {
|
||||
var document = parse(proData);
|
||||
var allVideo = document
|
||||
.querySelector(".bt_img.mi_ne_kd.mrb")
|
||||
.querySelectorAll("ul > li");
|
||||
var videos = [];
|
||||
for (let index = 0; index < allVideo.length; index++) {
|
||||
const element = allVideo[index];
|
||||
var vodUrl = element.querySelector("a")?.attributes["href"] ?? "";
|
||||
var vodPic =
|
||||
element.querySelector("a > img")?.attributes["data-original"] ?? "";
|
||||
var vodName =
|
||||
element.querySelector("a > img")?.attributes["alt"] ?? "";
|
||||
var vodDiJiJi = element.querySelector("div.jidi > span")?.text;
|
||||
var vodHD =
|
||||
element.querySelector("div.hdinfo > span.qb")?.text ??
|
||||
element.querySelector("div.hdinfo > span.furk")?.text;
|
||||
|
||||
var vodDouBan = element.querySelector("div.rating")?.text ?? "";
|
||||
vodUrl = this.combineUrl(vodUrl);
|
||||
|
||||
let videoDet = new VideoDetail();
|
||||
videoDet.vod_id = vodUrl;
|
||||
videoDet.vod_pic = vodPic;
|
||||
videoDet.vod_name = vodName;
|
||||
videoDet.vod_remarks = vodDiJiJi ?? vodHD;
|
||||
videoDet.vod_douban_score = vodDouBan;
|
||||
videos.push(videoDet);
|
||||
}
|
||||
backData.data = videos;
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取列表失败~";
|
||||
}
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频详情
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoDetail>}
|
||||
*/
|
||||
async getVideoDetail(args) {
|
||||
var backData = new RepVideoDetail();
|
||||
try {
|
||||
var webUrl = args.url;
|
||||
let pro = await req(webUrl, null);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
if (proData) {
|
||||
var document = parse(proData);
|
||||
var vod_pic =
|
||||
document.querySelector(".dyimg.fl > img")?.attributes["src"] ?? "";
|
||||
var vod_name =
|
||||
document.querySelector("div.moviedteail_tt > h1")?.text ?? "";
|
||||
var detList =
|
||||
document.querySelector(".moviedteail_list")?.querySelectorAll("li") ??
|
||||
[];
|
||||
var vod_year = "";
|
||||
var vod_director = "";
|
||||
var vod_actor = "";
|
||||
var vod_area = "";
|
||||
var vod_lang = "";
|
||||
var vod_douban_score = "";
|
||||
var type_name = "";
|
||||
|
||||
for (let index = 0; index < detList.length; index++) {
|
||||
const element = detList[index];
|
||||
if (element.text.includes("年份")) {
|
||||
vod_year = element.text.replace("年份:", "");
|
||||
} else if (element.text.includes("导演")) {
|
||||
vod_director = element.text.replace("导演:", "");
|
||||
} else if (element.text.includes("主演")) {
|
||||
vod_actor = element.text.replace("主演:", "");
|
||||
} else if (element.text.includes("地区")) {
|
||||
vod_area = element.text.replace("地区:", "");
|
||||
} else if (element.text.includes("语言")) {
|
||||
vod_lang = element.text.replace("语言:", "");
|
||||
} else if (element.text.includes("类型")) {
|
||||
type_name = element.text.replace("类型:", "");
|
||||
} else if (element.text.includes("豆瓣")) {
|
||||
vod_douban_score = element.text.replace("豆瓣:", "");
|
||||
}
|
||||
}
|
||||
|
||||
var vod_content = "";
|
||||
var vodBlurbDocument = document.querySelector(".yp_context");
|
||||
|
||||
if (vodBlurbDocument) {
|
||||
vod_content = vodBlurbDocument.text;
|
||||
|
||||
var allP = vodBlurbDocument.querySelectorAll("p");
|
||||
|
||||
for (let index = 0; index < allP.length; index++) {
|
||||
const element = allP[index];
|
||||
vod_content = vod_content + element.text;
|
||||
}
|
||||
}
|
||||
|
||||
var juJiDocment =
|
||||
document.querySelector(".paly_list_btn")?.querySelectorAll("a") ?? [];
|
||||
|
||||
var vod_play_url = "";
|
||||
for (let index = 0; index < juJiDocment.length; index++) {
|
||||
const element = juJiDocment[index];
|
||||
|
||||
vod_play_url += element.text;
|
||||
vod_play_url += "$";
|
||||
vod_play_url += element.attributes["href"];
|
||||
vod_play_url += "#";
|
||||
}
|
||||
|
||||
let detModel = new VideoDetail();
|
||||
detModel.vod_year = vod_year;
|
||||
detModel.type_name = type_name;
|
||||
detModel.vod_director = vod_director;
|
||||
detModel.vod_actor = vod_actor;
|
||||
detModel.vod_area = vod_area;
|
||||
detModel.vod_lang = vod_lang;
|
||||
detModel.vod_douban_score = vod_douban_score;
|
||||
detModel.vod_content = vod_content;
|
||||
detModel.vod_pic = vod_pic;
|
||||
detModel.vod_name = vod_name;
|
||||
detModel.vod_play_url = vod_play_url;
|
||||
detModel.vod_id = webUrl;
|
||||
backData.data = detModel;
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取视频详情失败";
|
||||
}
|
||||
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频的播放地址
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoPlayUrl>}
|
||||
*/
|
||||
async getVideoPlayUrl(args) {
|
||||
var backData = new RepVideoPlayUrl();
|
||||
try {
|
||||
const pro = await req(args.url);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
|
||||
if (proData) {
|
||||
var document = parse(proData);
|
||||
|
||||
let jsUrl = document.querySelector("iframe")?.attributes["src"] ?? "";
|
||||
if (jsUrl.length > 0) {
|
||||
let pro2 = await req(jsUrl, {
|
||||
headers: {
|
||||
Referer: this.webSite,
|
||||
"Sec-Fetch-Dest": "iframe",
|
||||
"Sec-Fetch-Mode": "navigate",
|
||||
},
|
||||
});
|
||||
pro2.error += pro.error;
|
||||
if (pro2.data) {
|
||||
let root = parse(pro2.data);
|
||||
let scripts = root.querySelectorAll("script");
|
||||
var code1 = "";
|
||||
if (scripts.length - 2 > 0) {
|
||||
code1 = scripts[scripts.length - 2].text;
|
||||
// console.log(code1);
|
||||
if (code1.indexOf("var player") > -1) {
|
||||
let player = code1.match(/var player = "(.*?)"/);
|
||||
let rand = code1.match(/var rand = "(.*?)"/);
|
||||
// console.log(player[1]);
|
||||
// console.log(rand[1]);
|
||||
let content = JSON.parse(
|
||||
this.cryptJs(player[1], "VFBTzdujpR9FWBhe", rand[1])
|
||||
);
|
||||
backData.data = content["url"];
|
||||
} else {
|
||||
// let path = scripts[scripts.length - 1].attributes["src"];
|
||||
// let host = UZUtils.getHostFromURL(jsUrl);
|
||||
// let pro = await req(host + path, {
|
||||
// headers: {
|
||||
// Referer: this.webSite,
|
||||
// "Sec-Fetch-Dest": "iframe",
|
||||
// "Sec-Fetch-Mode": "navigate",
|
||||
// },
|
||||
// });
|
||||
|
||||
// 浏览器里这样执行可以。。。
|
||||
// let c =
|
||||
// 'document[_0x2911("43", "EL@a")](_0x82e421)[_0x2911("44", "vU#R")] =';
|
||||
// var videoHtml = "";
|
||||
// let code2 = pro.data.replace(c, "videoHtml =");
|
||||
|
||||
// console.log(code2);
|
||||
// var res = eval('var videoHtml = "";' + code1 + code2);
|
||||
// console.log(res);
|
||||
backData.data = "";
|
||||
backData.error = "这个加密不知道怎么解~";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let x =
|
||||
document.querySelectorAll("script:contains(window.wp_nonce)") ?? [];
|
||||
if (x.length > 0) {
|
||||
let code = x[0].text;
|
||||
let group = code.match(/(var.*)eval\((\w*\(\w*\))\)/);
|
||||
const md5 = Crypto;
|
||||
const result = eval(group[1] + group[2]);
|
||||
let url = result.match(/url:.*?['"](.*?)['"]/)[1];
|
||||
backData.data = url;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取视频播放地址失败";
|
||||
}
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索视频
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoList>}
|
||||
*/
|
||||
async searchVideo(args) {
|
||||
var backData = new RepVideoList();
|
||||
// let url =
|
||||
// this.removeTrailingSlash(this.webSite) +
|
||||
// "/daoyongjiekoshibushiyoubing?q=" +
|
||||
// args.searchWord +
|
||||
// "&f=_all&p=" +
|
||||
// args.page;
|
||||
// let pro = await req(url, {
|
||||
// headers: {
|
||||
// "Cookie":
|
||||
// "cf_clearance=FNTfIrcfhaIjgq31GXM.lheyLTqcDDdOmUG6ci8xZo0-1718372164-1.0.1.1-3Sd9Aat3W4QbdrO8l4t6UF2dCLFjuHFeRVeH6VIXDAsYpitprJkKkESjPbPpendwyIuQMfYHzTqj_EXPeDselw; Hm_lvt_07305e6f6305a01dd93218c7fe6bc9c3=1717259553; Hm_lvt_06341c948291d8e90aac72f9d64905b3=1717259553; Hm_lvt_0653ba1ead8a9aabff96252e70492497=1717259553; myannoun=1",
|
||||
// "User-Agent":
|
||||
// "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
|
||||
// },
|
||||
// });
|
||||
// backData.error = pro.error;
|
||||
// let proData = pro.data;
|
||||
// if (proData) {
|
||||
// var document = parse(proData);
|
||||
|
||||
// let allVideo =
|
||||
// document
|
||||
// .querySelector(".bt_img.mi_ne_kd.search_list")
|
||||
// ?.querySelectorAll("ul > li") ?? [];
|
||||
// }
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
ignoreClassName = ["关于", "公告", "官方", "备用", "群", "地址"];
|
||||
|
||||
cryptJs(text, key, iv, type) {
|
||||
let key_value = Crypto.enc.Utf8.parse(key || "PBfAUnTdMjNDe6pL");
|
||||
let iv_value = Crypto.enc.Utf8.parse(iv || "sENS6bVbwSfvnXrj");
|
||||
let content;
|
||||
if (type) {
|
||||
content = Crypto.AES.encrypt(text, key_value, {
|
||||
iv: iv_value,
|
||||
mode: Crypto.mode.CBC,
|
||||
padding: Crypto.pad.Pkcs7,
|
||||
});
|
||||
} else {
|
||||
content = Crypto.AES.decrypt(text, key_value, {
|
||||
iv: iv_value,
|
||||
padding: Crypto.pad.Pkcs7,
|
||||
}).toString(Crypto.enc.Utf8);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
combineUrl(url) {
|
||||
if (url === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (url.indexOf(this.webSite) !== -1) {
|
||||
return url;
|
||||
}
|
||||
if (url.startsWith("/")) {
|
||||
return this.webSite + url;
|
||||
}
|
||||
return this.webSite + "/" + url;
|
||||
}
|
||||
|
||||
isIgnoreClassName(className) {
|
||||
for (let index = 0; index < this.ignoreClassName.length; index++) {
|
||||
const element = this.ignoreClassName[index];
|
||||
if (className.indexOf(element) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// json 中 instance 的值,这个名称一定要特殊
|
||||
var changZhang20240614 = new ChangZhang20240614();
|
||||
@@ -1,342 +0,0 @@
|
||||
// ignore
|
||||
import { WebApiBase, VideoClass } from "../core/uzCode.js";
|
||||
import { parse } from "node-html-parser";
|
||||
// ignore
|
||||
|
||||
// 类名要特殊
|
||||
class Keke20240712 extends WebApiBase {
|
||||
webSite = "https://www.keke12.com:51111";
|
||||
/**
|
||||
* 异步获取分类列表的方法。
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoClassList>}
|
||||
*/
|
||||
async getClassList(args) {
|
||||
let webUrl = args.url;
|
||||
var backData = new RepVideoClassList();
|
||||
backData.data = [];
|
||||
try {
|
||||
const pro = await req(webUrl);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
if (proData) {
|
||||
var document = parse(proData);
|
||||
var ulList = document.querySelectorAll("div.main > ul") ?? [];
|
||||
|
||||
if (ulList.length >= 1) {
|
||||
var li = ulList[1].querySelectorAll("li") ?? [];
|
||||
for (let i = 0; i < li.length; i++) {
|
||||
const element = li[i];
|
||||
const title = element.querySelector(".menu-item-label").text;
|
||||
const path = element.querySelector("a").attributes["href"];
|
||||
const id = UZUtils.getStrByRegexDefault(/\/(\d+)\.html/, path);
|
||||
var videoClass = new VideoClass();
|
||||
videoClass.hasSubclass = true;
|
||||
videoClass.type_id = id;
|
||||
videoClass.type_name = title;
|
||||
backData.data.push(videoClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取分类失败~";
|
||||
}
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
async getSubclassList(args) {
|
||||
var backData = new RepVideoSubclassList();
|
||||
backData.data = new VideoSubclass();
|
||||
const id = args.url;
|
||||
try {
|
||||
var url =
|
||||
UZUtils.removeTrailingSlash(this.webSite) +
|
||||
"/show/" +
|
||||
id +
|
||||
"------1.html";
|
||||
const pro = await req(url);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
if (proData) {
|
||||
var document = parse(proData);
|
||||
var filterTitleList = document.querySelectorAll("div.filter-row") ?? [];
|
||||
for (let i = 0; i < filterTitleList.length; i++) {
|
||||
const element = filterTitleList[i];
|
||||
const title = element.querySelector(".filter-row-side > strong").text;
|
||||
const items = element.querySelectorAll(".filter-item") ?? [];
|
||||
// 2-惊悚-中国香港-英语-2022-1-1
|
||||
// 分类-类型-地区-语言-年代-排序-页码
|
||||
var filterTitle = new FilterTitle();
|
||||
filterTitle.name = title.replace(":", "");
|
||||
filterTitle.list = [];
|
||||
for (let j = 0; j < items.length; j++) {
|
||||
const item = items[j];
|
||||
const name = item.text;
|
||||
const path = item.attributes["href"] ?? "";
|
||||
const regex = /\/show\/(.*?)\.html/;
|
||||
const match = path.match(regex);
|
||||
const parsStr = match ? match[1] : null;
|
||||
if (parsStr) {
|
||||
const parList = parsStr.split("-");
|
||||
const id = parList[i + 1];
|
||||
var filterLab = new FilterLabel();
|
||||
filterLab.name = name;
|
||||
filterLab.id = id;
|
||||
filterTitle.list.push(filterLab);
|
||||
}
|
||||
}
|
||||
|
||||
backData.data.filter.push(filterTitle);
|
||||
}
|
||||
if (id === 6 || id === "6") {
|
||||
// 短剧
|
||||
if (backData.data.filter.length > 0) {
|
||||
const list = backData.data.filter[0].list;
|
||||
var classList = [];
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
var subclass = new VideoClass();
|
||||
subclass.type_id = element.id;
|
||||
subclass.type_name = element.name;
|
||||
classList.push(subclass);
|
||||
}
|
||||
backData.data.filter = [];
|
||||
backData.data.class = classList;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取分类失败~ " + error;
|
||||
}
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
/**
|
||||
* 获取二级分类视频列表 或 筛选视频列表
|
||||
* @param {UZSubclassVideoListArgs} args
|
||||
* @returns {@Promise<JSON.stringify(new RepVideoList())>}
|
||||
*/
|
||||
async getSubclassVideoList(args) {
|
||||
var backData = new RepVideoList();
|
||||
backData.data = [];
|
||||
try {
|
||||
var pList = [args.mainClassId];
|
||||
if (args.filter.length > 0) {
|
||||
// 筛选
|
||||
for (let index = 0; index < args.filter.length; index++) {
|
||||
const element = args.filter[index];
|
||||
pList.push(element.id);
|
||||
}
|
||||
} else {
|
||||
pList.push(args.subclassId);
|
||||
for (let index = 0; index < 4; index++) {
|
||||
pList.push("");
|
||||
}
|
||||
}
|
||||
pList.push(args.page);
|
||||
var path = pList.join("-");
|
||||
const url =
|
||||
UZUtils.removeTrailingSlash(this.webSite) + "/show/" + path + ".html";
|
||||
|
||||
const pro = await req(url);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
if (proData) {
|
||||
var document = parse(proData);
|
||||
var allVideo = document.querySelectorAll("div.module-item") ?? [];
|
||||
var videos = [];
|
||||
for (let index = 0; index < allVideo.length; index++) {
|
||||
const element = allVideo[index];
|
||||
var vodUrl = element.querySelector("a")?.attributes["href"] ?? "";
|
||||
var avaImg =
|
||||
document.querySelector("img.user-avatar-img")?.attributes["src"] ??
|
||||
"";
|
||||
var path =
|
||||
element.querySelector("img")?.attributes["data-original"] ?? "";
|
||||
var vodPic = UZUtils.getHostFromURL(avaImg) + path;
|
||||
var vodName = element.querySelector("img")?.attributes["title"] ?? "";
|
||||
var vod_remarks =
|
||||
element.querySelector("div.v-item-bottom > span")?.text ??
|
||||
element.querySelector("div.v-item-top-left > span")?.text;
|
||||
if (vodUrl && vodPic && vodName) {
|
||||
var video = new VideoDetail();
|
||||
video.vod_id = vodUrl;
|
||||
video.vod_pic = vodPic;
|
||||
video.vod_name = vodName;
|
||||
video.vod_remarks = vod_remarks;
|
||||
videos.push(video);
|
||||
}
|
||||
}
|
||||
backData.data = videos;
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取视频列表失败~ " + error;
|
||||
}
|
||||
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类视频列表
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoList>}
|
||||
*/
|
||||
async getVideoList(args) {
|
||||
var backData = new RepVideoList();
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频详情
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoDetail>}
|
||||
*/
|
||||
async getVideoDetail(args) {
|
||||
var backData = new RepVideoDetail();
|
||||
try {
|
||||
var webUrl = UZUtils.removeTrailingSlash(this.webSite) + args.url;
|
||||
let pro = await req(webUrl, null);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
if (proData) {
|
||||
var document = parse(proData);
|
||||
|
||||
var detList = document.querySelectorAll("div.detail-info-row") ?? [];
|
||||
var vod_year = "";
|
||||
var vod_director = "";
|
||||
var vod_actor = "";
|
||||
|
||||
for (let index = 0; index < detList.length; index++) {
|
||||
const element = detList[index];
|
||||
var title =
|
||||
element.querySelector("div.detail-info-row-side")?.text ?? "";
|
||||
var contentE = element.querySelector("div.detail-info-row-main");
|
||||
if (title.includes("首映")) {
|
||||
vod_year = contentE.text ?? "";
|
||||
} else if (title.includes("导演")) {
|
||||
vod_director = contentE?.querySelector("a")?.text ?? "";
|
||||
} else if (title.includes("演员")) {
|
||||
contentE.querySelectorAll("a").forEach((element) => {
|
||||
vod_actor += element.text + "、";
|
||||
});
|
||||
}
|
||||
}
|
||||
var vod_content =
|
||||
document.querySelector("div.detail-desc > p").text?.trim() ?? "";
|
||||
|
||||
var fromListE =
|
||||
document.querySelectorAll("span.source-item-label") ?? [];
|
||||
var fromListStr = [];
|
||||
for (let index = 0; index < fromListE.length; index++) {
|
||||
fromListStr.push(fromListE[index].text);
|
||||
}
|
||||
var vod_play_from = fromListStr.join("$$$");
|
||||
|
||||
var playListE = document.querySelectorAll("div.episode-list") ?? [];
|
||||
var vod_play_url = "";
|
||||
for (let index = 0; index < playListE.length; index++) {
|
||||
const epList = playListE[index].querySelectorAll("a") ?? [];
|
||||
|
||||
for (let index = 0; index < epList.length; index++) {
|
||||
const element = epList[index];
|
||||
vod_play_url += element.text;
|
||||
vod_play_url += "$";
|
||||
vod_play_url += element.attributes["href"];
|
||||
vod_play_url += "#";
|
||||
}
|
||||
vod_play_url += "$$$";
|
||||
}
|
||||
|
||||
let detModel = new VideoDetail();
|
||||
detModel.vod_year = vod_year;
|
||||
detModel.vod_director = vod_director;
|
||||
detModel.vod_actor = vod_actor;
|
||||
detModel.vod_content = vod_content;
|
||||
detModel.vod_play_url = vod_play_url;
|
||||
detModel.vod_play_from = vod_play_from;
|
||||
detModel.vod_id = args.url;
|
||||
backData.data = detModel;
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取视频详情失败 ~ " + error;
|
||||
}
|
||||
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频的播放地址
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoPlayUrl>}
|
||||
*/
|
||||
async getVideoPlayUrl(args) {
|
||||
var backData = new RepVideoPlayUrl();
|
||||
backData.error = "获取播放链接失败~";
|
||||
try {
|
||||
const pro = await req(
|
||||
UZUtils.removeTrailingSlash(this.webSite) + args.url
|
||||
);
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
const regex = /playSource\s*=\s*{[^}]*src:\s*"([^"]*)"/;
|
||||
const match = proData.match(regex);
|
||||
const result = match ? match[1] : "";
|
||||
backData.data = result;
|
||||
} catch (error) {
|
||||
backData.error = "获取视频播放地址失败~ " + error;
|
||||
}
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索视频
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoList>}
|
||||
*/
|
||||
async searchVideo(args) {
|
||||
var backData = new RepVideoList();
|
||||
backData.data = [];
|
||||
try {
|
||||
const rep = await req(
|
||||
UZUtils.removeTrailingSlash(this.webSite) +
|
||||
"/search?k=" +
|
||||
args.searchWord +
|
||||
"&page=" +
|
||||
args.page
|
||||
);
|
||||
backData.error = rep.error;
|
||||
let repData = rep.data;
|
||||
var document = parse(repData);
|
||||
var count = document.querySelectorAll("span.highlight-text") ?? [];
|
||||
if (count.length > 1) {
|
||||
backData.total = count[1].text.trim();
|
||||
}
|
||||
const allVideoE = document.querySelectorAll("a.search-result-item") ?? [];
|
||||
|
||||
for (let index = 0; index < allVideoE.length; index++) {
|
||||
const element = allVideoE[index];
|
||||
var video = new VideoDetail();
|
||||
video.vod_id = element.attributes["href"];
|
||||
|
||||
var avaImg =
|
||||
document.querySelector("img.user-avatar-img")?.attributes["src"] ??
|
||||
"";
|
||||
var path =
|
||||
element.querySelector("img")?.attributes["data-original"] ?? "";
|
||||
var vodPic = UZUtils.getHostFromURL(avaImg) + path;
|
||||
video.vod_pic = vodPic;
|
||||
|
||||
video.vod_name = element.querySelector("div.title")?.text ?? "";
|
||||
video.vod_remarks =
|
||||
element.querySelector("div.search-result-item-header > div")?.text ??
|
||||
"";
|
||||
|
||||
backData.data.push(video);
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "搜索视频失败 ~ " + error;
|
||||
}
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
}
|
||||
// json 中 instance 的值,这个名称一定要特殊
|
||||
var keke20240712 = new Keke20240712();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,175 +0,0 @@
|
||||
// ignore
|
||||
import {} from "../core/uzVideo.js";
|
||||
import {} from "../core/uzHome.js";
|
||||
import {} from "../core/uz3lib.js";
|
||||
import {} from "../core/uzUtils.js";
|
||||
// ignore
|
||||
|
||||
// 类名要特殊
|
||||
class Wogg20240929 extends WebApiBase {
|
||||
constructor() {
|
||||
super();
|
||||
this.webSite = "https://www.wogg.net/";
|
||||
}
|
||||
/**
|
||||
* 异步获取分类列表的方法。
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoClassList>}
|
||||
*/
|
||||
async getClassList(args) {
|
||||
var backData = new RepVideoClassList();
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类视频列表
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoList>}
|
||||
*/
|
||||
async getVideoList(args) {
|
||||
var backData = new RepVideoList();
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频详情
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoDetail>}
|
||||
*/
|
||||
async getVideoDetail(args) {
|
||||
var backData = new RepVideoDetail();
|
||||
try {
|
||||
let webUrl = UZUtils.removeTrailingSlash(this.webSite) + args.url;
|
||||
let pro = await req(webUrl);
|
||||
|
||||
backData.error = pro.error;
|
||||
let proData = pro.data;
|
||||
this.checkVerify(webUrl, proData);
|
||||
if (proData?.includes("js=slider")) {
|
||||
await goToVerify(webUrl);
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
if (proData) {
|
||||
const $ = cheerio.load(proData);
|
||||
let vodDetail = new VideoDetail();
|
||||
vodDetail.vod_name = $(".page-title")[0].children[0].data;
|
||||
vodDetail.vod_pic = $($(".mobile-play")).find(".lazyload")[0].attribs[
|
||||
"data-src"
|
||||
];
|
||||
|
||||
let video_items = $(".video-info-itemtitle");
|
||||
|
||||
for (const item of video_items) {
|
||||
let key = $(item).text();
|
||||
|
||||
let vItems = $(item).next().find("a");
|
||||
let value = vItems
|
||||
.map((i, el) => {
|
||||
let text = $(el).text().trim(); // 获取并去除空白字符
|
||||
return text ? text : null; // 只有非空的文本才返回
|
||||
})
|
||||
.get() // 将 jQuery 对象转换为普通数组
|
||||
.filter(Boolean) // 过滤掉 null 和空字符串
|
||||
.join(", "); // 用逗号和空格分割
|
||||
|
||||
if (key.includes("年代")) {
|
||||
vodDetail.vod_year = value.trim();
|
||||
} else if (key.includes("导演")) {
|
||||
vodDetail.vod_director = value.trim();
|
||||
} else if (key.includes("主演")) {
|
||||
vodDetail.vod_actor = value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
const panUrls = [];
|
||||
let items = $(".module-row-info");
|
||||
for (const item of items) {
|
||||
let shareUrl = $(item).find("p")[0].children[0].data;
|
||||
panUrls.push(shareUrl);
|
||||
}
|
||||
vodDetail.panUrls = panUrls;
|
||||
console.log(panUrls);
|
||||
|
||||
backData.data = vodDetail;
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = "获取视频详情失败" + error;
|
||||
}
|
||||
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频的播放地址
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoPlayUrl>}
|
||||
*/
|
||||
async getVideoPlayUrl(args) {
|
||||
var backData = new RepVideoPlayUrl();
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索视频
|
||||
* @param {UZArgs} args
|
||||
* @returns {Promise<RepVideoList>}
|
||||
*/
|
||||
async searchVideo(args) {
|
||||
var backData = new RepVideoList();
|
||||
try {
|
||||
let searchUrl = this.combineUrl(
|
||||
this.webSite +
|
||||
"vodsearch/" +
|
||||
args.searchWord +
|
||||
"----------" +
|
||||
args.page +
|
||||
"---.html"
|
||||
);
|
||||
let repData = await req(searchUrl);
|
||||
this.checkVerify(searchUrl, repData.data);
|
||||
const $ = cheerio.load(repData.data);
|
||||
let items = $(".module-search-item");
|
||||
|
||||
for (const item of items) {
|
||||
let video = new VideoDetail();
|
||||
video.vod_id = $(item).find(".video-serial")[0].attribs.href;
|
||||
video.vod_name = $(item).find(".video-serial")[0].attribs.title;
|
||||
video.vod_pic = $(item).find(".module-item-pic > img")[0].attribs[
|
||||
"data-src"
|
||||
];
|
||||
video.vod_remarks = $($(item).find(".video-serial")[0]).text();
|
||||
backData.data.push(video);
|
||||
}
|
||||
} catch (error) {
|
||||
backData.error = error;
|
||||
}
|
||||
return JSON.stringify(backData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否需要验证码
|
||||
* @param {string} webUrl
|
||||
* @param {any} data
|
||||
**/
|
||||
async checkVerify(webUrl, data) {
|
||||
if (typeof data === "string" && data.includes("js=slider")) {
|
||||
await goToVerify(webUrl);
|
||||
}
|
||||
}
|
||||
|
||||
combineUrl(url) {
|
||||
if (url === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (url.indexOf(this.webSite) !== -1) {
|
||||
return url;
|
||||
}
|
||||
if (url.startsWith("/")) {
|
||||
return this.webSite + url;
|
||||
}
|
||||
return this.webSite + "/" + url;
|
||||
}
|
||||
}
|
||||
|
||||
// json 中 instance 的值,这个名称一定要特殊
|
||||
var wogg20240929 = new Wogg20240929();
|
||||
@@ -1,9 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "玩偶哥哥",
|
||||
"api": "https://ghp.ci/https://raw.githubusercontent.com/YYDS678/uzVideo/main/js/spider/wogg.js",
|
||||
"instance": "wogg20240929",
|
||||
"webSite": "https://www.wogg.net/",
|
||||
"remark": "网盘,搜索"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user