添加猫影视测试
136
cat/tjs/18_open_config.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"video": {
|
||||
"sites": [
|
||||
{
|
||||
"key": "sehuatang",
|
||||
"name": "🔞┃色花堂BT┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/sehuatang.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "hscangku",
|
||||
"name": "🔞┃黄色仓库┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/hscangku.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "feifan18",
|
||||
"name": "🔞┃非凡资源18+┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/feifan18.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "asianx",
|
||||
"name": "🔞┃海外宅┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/asianx.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "liangzi18",
|
||||
"name": "🔞┃量子资源18+┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/liangzi18.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "jable",
|
||||
"name": "🔞┃Jable┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/jable.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "ciliduo",
|
||||
"name": "🔞┃磁力多BT┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/ciliduo.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "doll",
|
||||
"name": "🔞┃玩偶姐姐┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/doll.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "nivod_18",
|
||||
"name": "🔞┃泥视频18+┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/nivod_18.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "push_agent",
|
||||
"name": "┃推送┃",
|
||||
"type": 3,
|
||||
"api": "./js/push_agent.js",
|
||||
"ext": "{\"box\": \"CatOpen\", \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"read": {
|
||||
"sites": [
|
||||
{
|
||||
"key": "audiomack",
|
||||
"name": "🎵┃音声┃🎵",
|
||||
"type": 3,
|
||||
"api": "./js/audiomack.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
},
|
||||
{
|
||||
"key": "copymanga",
|
||||
"name": "🧑🎨|拷贝漫画|🧑🎨",
|
||||
"type": 3,
|
||||
"api": "./js/copymanga.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
},
|
||||
{
|
||||
"key": "bqg_open",
|
||||
"name": "📚︎┃笔趣阁┃📚︎",
|
||||
"type": 10,
|
||||
"api": "./js/bqg_open.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
},
|
||||
{
|
||||
"key": "dj0898_book_open",
|
||||
"name": "🎵┃世纪DJ音乐网┃🎵",
|
||||
"type": 3,
|
||||
"api": "./js/dj0898_book_open.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
},
|
||||
{
|
||||
"key": "bookan",
|
||||
"name": "🎵┃看书┃🎵",
|
||||
"type": 3,
|
||||
"api": "./js/bookan.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pan": {
|
||||
"sites": []
|
||||
}
|
||||
}
|
||||
460
cat/tjs/18_tv_config.json
Normal file
@ -0,0 +1,460 @@
|
||||
{
|
||||
"sites": [
|
||||
{
|
||||
"key": "sehuatang",
|
||||
"name": "🔞┃色花堂BT┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/sehuatang.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "hscangku",
|
||||
"name": "🔞┃黄色仓库┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/hscangku.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "feifan18",
|
||||
"name": "🔞┃非凡资源18+┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/feifan18.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "asianx",
|
||||
"name": "🔞┃海外宅┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/asianx.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "liangzi18",
|
||||
"name": "🔞┃量子资源18+┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/liangzi18.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "jable",
|
||||
"name": "🔞┃Jable┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/jable.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "ciliduo",
|
||||
"name": "🔞┃磁力多BT┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/ciliduo.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "doll",
|
||||
"name": "🔞┃玩偶姐姐┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/doll.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "nivod_18",
|
||||
"name": "🔞┃泥视频18+┃🔞",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/nivod_18.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"TVBox\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "push_agent",
|
||||
"name": "┃推送┃",
|
||||
"type": 3,
|
||||
"api": "./js/push_agent.js",
|
||||
"ext": "{\"box\": \"TVBox\", \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
}
|
||||
],
|
||||
"ijk": [
|
||||
{
|
||||
"group": "软解码",
|
||||
"options": [
|
||||
{
|
||||
"category": 4,
|
||||
"name": "opensles",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "overlay-format",
|
||||
"value": "842225234"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "framedrop",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "soundtouch",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "start-on-prepared",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 1,
|
||||
"name": "http-detect-range-support",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 1,
|
||||
"name": "fflags",
|
||||
"value": "fastseek"
|
||||
},
|
||||
{
|
||||
"category": 2,
|
||||
"name": "skip_loop_filter",
|
||||
"value": "48"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "reconnect",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "max-buffer-size",
|
||||
"value": "5242880"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "enable-accurate-seek",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "mediacodec",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "mediacodec-auto-rotate",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "mediacodec-handle-resolution-change",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "mediacodec-hevc",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 1,
|
||||
"name": "dns_cache_timeout",
|
||||
"value": "600000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "硬解码",
|
||||
"options": [
|
||||
{
|
||||
"category": 4,
|
||||
"name": "opensles",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "overlay-format",
|
||||
"value": "842225234"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "framedrop",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "soundtouch",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "start-on-prepared",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 1,
|
||||
"name": "http-detect-range-support",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 1,
|
||||
"name": "fflags",
|
||||
"value": "fastseek"
|
||||
},
|
||||
{
|
||||
"category": 2,
|
||||
"name": "skip_loop_filter",
|
||||
"value": "48"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "reconnect",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "max-buffer-size",
|
||||
"value": "5242880"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "enable-accurate-seek",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "mediacodec",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "mediacodec-auto-rotate",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "mediacodec-handle-resolution-change",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 4,
|
||||
"name": "mediacodec-hevc",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"category": 1,
|
||||
"name": "dns_cache_timeout",
|
||||
"value": "600000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"ads": [
|
||||
"wan.51img1.com",
|
||||
"iqiyi.hbuioo.com",
|
||||
"vip.ffzyad.com",
|
||||
"https://lf1-cdn-tos.bytegoofy.com/obj/tos-cn-i-dy/455ccf9e8ae744378118e4bd289288dd"
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"name": "星星",
|
||||
"hosts": [
|
||||
"aws.ulivetv.net"
|
||||
],
|
||||
"regex": [
|
||||
"#EXT-X-DISCONTINUITY\\r*\\n*#EXTINF:8,[\\s\\S]*?#EXT-X-DISCONTINUITY"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "量子廣告",
|
||||
"hosts": [
|
||||
"vip.lz",
|
||||
"hd.lz"
|
||||
],
|
||||
"regex": [
|
||||
"#EXT-X-DISCONTINUITY\\r*\\n*#EXTINF:6.433333,[\\s\\S]*?#EXT-X-DISCONTINUITY",
|
||||
"#EXTINF.*?\\s+.*?1o.*?\\.ts\\s+"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "非凡廣告",
|
||||
"hosts": [
|
||||
"vip.ffzy",
|
||||
"hd.ffzy"
|
||||
],
|
||||
"regex": [
|
||||
"#EXT-X-DISCONTINUITY\\r*\\n*#EXTINF:6.666667,[\\s\\S]*?#EXT-X-DISCONTINUITY",
|
||||
"#EXTINF.*?\\s+.*?1o.*?\\.ts\\s+"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "火山嗅探",
|
||||
"hosts": [
|
||||
"huoshan.com"
|
||||
],
|
||||
"regex": [
|
||||
"item_id="
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "抖音嗅探",
|
||||
"hosts": [
|
||||
"douyin.com"
|
||||
],
|
||||
"regex": [
|
||||
"is_play_url="
|
||||
]
|
||||
}
|
||||
],
|
||||
"doh": [
|
||||
{
|
||||
"name": "Google",
|
||||
"url": "https://dns.google/dns-query",
|
||||
"ips": [
|
||||
"8.8.4.4",
|
||||
"8.8.8.8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Cloudflare",
|
||||
"url": "https://cloudflare-dns.com/dns-query",
|
||||
"ips": [
|
||||
"1.1.1.1",
|
||||
"1.0.0.1",
|
||||
"2606:4700:4700::1111",
|
||||
"2606:4700:4700::1001"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AdGuard",
|
||||
"url": "https://dns.adguard.com/dns-query",
|
||||
"ips": [
|
||||
"94.140.14.140",
|
||||
"94.140.14.141"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DNSWatch",
|
||||
"url": "https://resolver2.dns.watch/dns-query",
|
||||
"ips": [
|
||||
"84.200.69.80",
|
||||
"84.200.70.40"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Quad9",
|
||||
"url": "https://dns.quad9.net/dns-quer",
|
||||
"ips": [
|
||||
"9.9.9.9",
|
||||
"149.112.112.112"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parses": [
|
||||
{
|
||||
"name": "聚合",
|
||||
"type": 3,
|
||||
"url": "Demo"
|
||||
},
|
||||
{
|
||||
"name": "线路1",
|
||||
"type": 0,
|
||||
"url": "https://jx.xmflv.com/?url=",
|
||||
"ext": {
|
||||
"header": {
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "线路2",
|
||||
"type": 0,
|
||||
"url": "https://jx.quankan.app/?url=",
|
||||
"ext": {
|
||||
"header": {
|
||||
"user-agent": "Mozilla/5.0 (Linux; Android 13; V2049A Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "线路3",
|
||||
"type": 0,
|
||||
"url": "https://jx.yparse.com/index.php?url=",
|
||||
"ext": {
|
||||
"header": {
|
||||
"user-agent": "Mozilla/5.0 (Linux; Android 13; V2049A Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "线路4",
|
||||
"type": 0,
|
||||
"url": "https://jx.jsonplayer.com/player/?url=",
|
||||
"ext": {
|
||||
"header": {
|
||||
"user-agent": "Mozilla/5.0 (Linux; Android 13; V2049A Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "线路5",
|
||||
"type": 0,
|
||||
"url": "https://jx.aidouer.net/?url=",
|
||||
"ext": {
|
||||
"header": {
|
||||
"user-agent": "Mozilla/5.0 (Linux; Android 13; V2049A Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36",
|
||||
"referer": "https://jiejie.uk/"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "线路6",
|
||||
"type": 0,
|
||||
"url": "https://jx.777jiexi.com/player/?url="
|
||||
},
|
||||
{
|
||||
"name": "线路7",
|
||||
"type": 0,
|
||||
"url": "https://www.8090g.cn/?url="
|
||||
},
|
||||
{
|
||||
"name": "线路8",
|
||||
"type": 0,
|
||||
"url": "https://jx.yangtu.top?url="
|
||||
},
|
||||
{
|
||||
"name": "线路9",
|
||||
"type": 0,
|
||||
"url": "https://jx.m3u8.tv/jiexi/?url="
|
||||
},
|
||||
{
|
||||
"name": "线路10",
|
||||
"type": 0,
|
||||
"url": "https://www.ckplayer.vip/jiexi/?url="
|
||||
}
|
||||
],
|
||||
"lives": [
|
||||
{
|
||||
"name": "live",
|
||||
"type": 0,
|
||||
"url": "https://agit.ai/fantaiying/0/raw/branch/main/tvlive.txt",
|
||||
"playerType": 1,
|
||||
"ua": "okhttp/3.15",
|
||||
"epg": "http://epg.112114.xyz/?ch={name}&date={date}",
|
||||
"logo": "https://epg.112114.xyz/logo/{name}.png"
|
||||
}
|
||||
],
|
||||
"wallpaper": "http://饭太硬.top/深色壁纸/api.php"
|
||||
}
|
||||
67
cat/tjs/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
# CatVodOpen和TvBox
|
||||
|
||||
## 食用前阅读
|
||||
  以下资源不能保证内容的合法性、准确性、完整性和有效性,请根据情况自行判断。
|
||||
仅用于测试和学习研究,禁止用于商业用途,不得将其用于违反国家、地区、组织等的法律法规或相关规定的其他用途。
|
||||
使用任何第三方硬件、软件、所造成的一切后果由使用的个人或组织承担,与本文内容无关。
|
||||
所有直接或间接使用本内容的个人和组织,应 24 小时内完成学习和研究,并及时删除本文内容。
|
||||
所有基于本内容的源代码,进行的任何修改,为其他个人或组织的自发行为,与本内容没有任何直接或间接的关系,所造成的一切后果亦与本内容和本管理者无关。
|
||||
本管理者保留随时更改或补充此免责声明的权利。一旦使用、复制、转载、发布、修改了本文内容,则视为您已接受此免责声明。否则后果自负。
|
||||
本接口无公众号、无盈利、无引流、免费分享给小白使用,请勿轻信他人,谨防上当受骗。
|
||||
|
||||
源项目地址:https://github.com/jadehh/TVSpider
|
||||
|
||||
## TVBox互联网发布地址
|
||||
[TV发布地址](https://github.com/FongMi/Release/tree/main/apk/release)
|
||||
```bash
|
||||
https://gh.con.sh/https://raw.githubusercontent.com/qist/tvbox/cat/tjs/tv_config.json
|
||||
```
|
||||
> 配置信息见js分支分支
|
||||
|
||||
|
||||
## CatVodOpen
|
||||
[CatVodOpen发布地址](https://github.com/catvod/CatVodOpen/releases)
|
||||
|
||||
> 注意使用Gitee或Github导入,并设置为私有仓库,<font color="red">CatVodOpen仅支持私有仓库</font>
|
||||
|
||||
V1.1.3版本以上
|
||||
```bash
|
||||
github://Token@github.com/qist/tvbox/cat/dist/index.js.md5
|
||||
```
|
||||
<font color="red">**改动**</font>
|
||||
|
||||
* quickjs改为nodejs,proxy设置修改
|
||||
* 在ios上无法使用local,使用db替换local所有方法
|
||||
* nodejs 的优势在于更加灵活
|
||||
> 配置信息见dist分支
|
||||
|
||||
V1.1.2版本以下
|
||||
```bash
|
||||
github://Token@github.com/qist/tvbox/cat/tjs/open_config.json
|
||||
```
|
||||
> 配置信息见js分支分支
|
||||
|
||||
|
||||
## config文件生成
|
||||
```bash
|
||||
python build.py --aliToken aliToken
|
||||
```
|
||||
> Token如果失效,需要重新获取阿里Token
|
||||
>
|
||||
> [阿里Token获取](https://alist.nn.ci/zh/guide/drivers/aliyundrive.html)
|
||||
>
|
||||
> nodejs 部分只生成代码,需要手动build,区分18+
|
||||
|
||||
## 遇到的问题
|
||||
* 玩偶姐姐播放不了,需要切换VPN节点
|
||||
* m3u8遇到跨域的问题可以尝试使用代理来进行加载,如果没有跨域使用代理会引起死循环
|
||||
* 虎牙弹幕功能无法实现,现在并不支持WebSocket来监听弹幕
|
||||
* SP360启用嗅探解析,CatVodOpen目前还不支持嗅探
|
||||
|
||||
|
||||
## 特别说明
|
||||
* 近期CatVodOpen更新移除了quickjs,导致无法使用,请尝试使用旧版本
|
||||
* 或切换至nodejs目录下编译生成dist目录,dist目录发布到dist分支下
|
||||
* main分支用于代码测试(不包含任何配置信息),js分支发布支持quickjs爬虫配置信息,dist分支发布支持nodejs爬虫配置信息
|
||||
* 所有的配置信息都通过Github Actions发布,通过创建tag来生成新的配置信息并自动发布
|
||||
* fork仓库时去掉仅复制main分支的✔,这样就可以fork所有的分支了
|
||||
238
cat/tjs/js/4kysxz.js
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* @File : 4kysxz.js.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/24 16:47
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 4k高清网 (已失效)
|
||||
*/
|
||||
import {_} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
class YSXZSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://4kysxz.top"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return `4K高清网`
|
||||
}
|
||||
|
||||
getName() {
|
||||
return `🚄┃4K高清网┃🚄`
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "4kysxz"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
const cards = $('div.row.posts-wrapper >div > article > div.entry-media > div > a')
|
||||
return _.map(cards, (n) => {
|
||||
let id = n.attribs['href'];
|
||||
let name = $($(n).find('img')[0]).attr('alt').replaceAll('<strong>', '').replaceAll('</strong>', '').split(' ')[0];
|
||||
let pic = $($(n).find('img')[0]).attr('data-src');
|
||||
return {
|
||||
vod_id: id, vod_name: name, vod_pic: pic, vod_remarks: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail();
|
||||
let vodElement = $("[class=\"entry-content u-text-format u-clearfix\"]")
|
||||
let text = "";
|
||||
for (const vodEle of vodElement) {
|
||||
text = text + $(vodEle).text().replaceAll(":", ":") + "\n"
|
||||
}
|
||||
vodDetail.vod_name = $($("[class=\"article-title\"]")).text()
|
||||
vodDetail.vod_pic = $($("[class=\"entry-content u-text-format u-clearfix\"]")).find("img")[0].attribs["src"]
|
||||
vodDetail.vod_area = Utils.getStrByRegex(/上映地区(.*?)\n/, text).replaceAll(":", "")
|
||||
vodDetail.vod_director = Utils.getStrByRegex(/导演(.*?)\n/, text).replaceAll(":", "")
|
||||
vodDetail.vod_actor = Utils.getStrByRegex(/主演(.*?)\n/, text).replaceAll(":", "")
|
||||
vodDetail.vod_content = Utils.getStrByRegex(/剧情简介(.*?)\n/, text).replaceAll(":", "").replaceAll("·", "")
|
||||
let actors = _.map($('div.entry-content.u-text-format.u-clearfix > div:nth-child(10) > div > span > span'), (n) => {
|
||||
return $(n).text().split(' ')[0];
|
||||
});
|
||||
vodDetail.vod_actor = actors.join('/');
|
||||
let directors = _.map($('div.entry-content.u-text-format.u-clearfix > div:nth-child(6) > div > span'), (n) => {
|
||||
return $(n).text().split(' ')[0];
|
||||
});
|
||||
vodDetail.vod_director = directors.join('/');
|
||||
vodDetail.vod_name = $('div.site-content > section > div > header > h1').text().trim();
|
||||
let playUrlStr = '';
|
||||
let playFromStr = '';
|
||||
//高清直播
|
||||
const cards = $('div.entry-content.u-text-format.u-clearfix > custag > ul > li > a');
|
||||
if (cards.length > 0) {
|
||||
let playUrls = _.map(cards, (n) => {
|
||||
let playUrl = n.attribs['href'];
|
||||
if (playUrl.indexOf('url=') > 0) {
|
||||
playUrl = playUrl.split('url=')[1].split('&name')[0];
|
||||
}
|
||||
return $(n).text() + '$' + playUrl;
|
||||
});
|
||||
playUrlStr = playUrls.join('#');
|
||||
playFromStr = '高清直播';
|
||||
}
|
||||
if (!this.catOpenStatus) {
|
||||
//磁力链接
|
||||
const tbs = $('loginshow > table');
|
||||
let playFrom = '';
|
||||
let nameUrls = [];
|
||||
for (let i = 0; i < tbs.length; i++) {
|
||||
if (i % 2 == 0) {
|
||||
playFrom = $(tbs[i]).find('tbody > tr >td').text().replaceAll('WEB', '磁力');
|
||||
} else if (i % 2 == 1) {
|
||||
const tds = $(tbs[i]).find('tbody > tr >td');
|
||||
let nameUrl = '';
|
||||
for (let j = 0; j < tds.length; j++) {
|
||||
if (j % 2 == 0) {
|
||||
nameUrl = $(tds[j]).text().split('.')[0].split(' ')[0];
|
||||
} else if (j % 2 == 1) {
|
||||
nameUrl = nameUrl + '$' + $(tds[j]).text().split('【')[0];
|
||||
nameUrls.push(nameUrl);
|
||||
}
|
||||
}
|
||||
if (playFromStr.length > 0) {
|
||||
playFromStr += '$$$';
|
||||
playUrlStr += '$$$';
|
||||
}
|
||||
playFromStr += playFrom;
|
||||
playUrlStr += nameUrls.join('#');
|
||||
}
|
||||
}
|
||||
}
|
||||
vodDetail.vod_play_from = playFromStr
|
||||
vodDetail.vod_play_url = playUrlStr
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
|
||||
async setClasses() {
|
||||
this.classes = []
|
||||
this.classes.push(this.getTypeDic("首页", "/#"))
|
||||
}
|
||||
|
||||
async getFilter(typeElements) {
|
||||
let value_list = []
|
||||
value_list.push({
|
||||
"n": "全部", "v": "全部",
|
||||
})
|
||||
for (const typeElement of typeElements) {
|
||||
value_list.push({
|
||||
"n": typeElement.attribs["title"],
|
||||
"v": typeElement.attribs["href"].split("/").slice(-1)[0].split(".")[0],
|
||||
})
|
||||
}
|
||||
return [{"key": "1", "name": "类型", "value": value_list}]
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
let $ = await this.getHtml()
|
||||
let navElements = $("[class=\"navbar-item menu-item-has-children\"]")
|
||||
let extent_list = []
|
||||
for (const navElement of navElements) {
|
||||
let type_name = $($(navElement).find("a")[0]).text()
|
||||
if (type_name.indexOf("影视") > -1) {
|
||||
let extend_dic = {"key": "1", "name": type_name, "value": []}
|
||||
let type_elements = $($(navElement).find("ul")).find("a")
|
||||
for (const type_element of type_elements) {
|
||||
extend_dic["value"].push({"n": $(type_element).text(), "v": type_element.attribs["href"]})
|
||||
}
|
||||
extent_list.push(extend_dic)
|
||||
}
|
||||
}
|
||||
this.filterObj["/#"] = extent_list
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let url;
|
||||
if (extend["1"] === undefined) {
|
||||
url = this.siteUrl + tid
|
||||
} else {
|
||||
if (extend["1"].indexOf("category") > -1) {
|
||||
url = this.siteUrl + extend["1"].split(".")[0] + "_" + pg + ".html"
|
||||
} else {
|
||||
url = this.siteUrl + extend["1"]
|
||||
}
|
||||
}
|
||||
let $ = await this.getHtml(url)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
const $ = await this.getHtml(id);
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let $ = await this.getHtml(this.siteUrl + '/search.php?q=' + wd)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new YSXZSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
299
cat/tjs/js/aiyingshi.js
Normal file
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* @Author: samples jadehh@live.com
|
||||
* @Date: 2023-12-14 11:03:04
|
||||
* @LastEditors: samples jadehh@live.com
|
||||
* @LastEditTime: 2023-12-14 11:03:04
|
||||
* @FilePath: js/aiyingshi.js
|
||||
* @Description: 爱影视爬虫类
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
|
||||
class AiYingShiSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = 'https://aiyingshis.com';
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🚀┃爱影视┃🚀"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "爱影视"
|
||||
}
|
||||
getJSName() {
|
||||
return "aiyingshi"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let items = $('.module-item');
|
||||
let vod_list = [];
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort()
|
||||
let oneA = $(item).find('.module-item-cover .module-item-pic a').first();
|
||||
vodShort.vod_id = oneA.attr('href');
|
||||
vodShort.vod_name = oneA.attr('title');
|
||||
let pic = $(item).find('.module-item-cover .module-item-pic img').first().attr('data-src')
|
||||
if (pic.indexOf("img.php?url=") > 0) {
|
||||
pic = pic.split("img.php?url=")[1]
|
||||
}else if (pic.indexOf("https:") === -1 && pic.indexOf("http:") === -1){
|
||||
pic = "https:" + pic
|
||||
}
|
||||
vodShort.vod_pic = pic
|
||||
vodShort.vod_remarks = $(item).find('.module-item-text').first().text();
|
||||
if (vodShort.vod_name !== undefined){
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = $('.page-title')[0].children[0].data
|
||||
vodDetail.vod_pic = $($("[class=\"video-cover\"]")).find(".lazyload")[0].attribs["data-src"]
|
||||
let video_info_list = $($(".video-info-aux")).text().replaceAll("\t","").split("\n")
|
||||
let type_list = []
|
||||
for (const video_info of video_info_list){
|
||||
if (!_.isEmpty(video_info.replaceAll(" ","").replaceAll("/",""))){
|
||||
type_list.push(video_info.replaceAll(" ","").replaceAll("/",""))
|
||||
}
|
||||
}
|
||||
vodDetail.type_name = type_list.slice(0,2).join("*")
|
||||
let video_items = $("[class=\"video-info-items\"]")
|
||||
vodDetail.vod_director = $(video_items[0]).find("a")[0].children[0].data
|
||||
let vidoe_info_actor_list = $(video_items[1]).find("a")
|
||||
let actor_list = []
|
||||
for (const vidoe_info_actor of vidoe_info_actor_list) {
|
||||
actor_list.push(vidoe_info_actor.children[0].data)
|
||||
}
|
||||
vodDetail.vod_actor = actor_list.join(" * ")
|
||||
vodDetail.vod_year = type_list[2]
|
||||
vodDetail.vod_remarks = $($(video_items[3]).find(".video-info-item")).text()
|
||||
vodDetail.vod_content = $($(video_items[5]).find(".video-info-item")).text()
|
||||
vodDetail.vod_area = type_list[3]
|
||||
vodDetail.vod_content = vodDetail.vod_content.replace("[收起部分]", "").replace("[展开全部]", "").replaceAll("\t","").replaceAll("\n","")
|
||||
|
||||
let playElements = $($("[class=\"module-tab-content\"]")[0]).find("span")
|
||||
let urlElements = $("[class=\"module-list module-player-list tab-list sort-list \"]")
|
||||
let form_list = []
|
||||
for (const playerElement of playElements){
|
||||
form_list.push($(playerElement).text())
|
||||
}
|
||||
let play_url_list = []
|
||||
for (const urlElement of urlElements){
|
||||
let playUrlElements = $($(urlElement).find("[class=\"sort-item\"]")).find("a")
|
||||
let vodItems = []
|
||||
for (const playUrlElement of playUrlElements){
|
||||
let name = $(playUrlElement).text()
|
||||
let url = playUrlElement.attribs["href"]
|
||||
let play_url = name + "$" + url
|
||||
vodItems.push(play_url)
|
||||
}
|
||||
play_url_list.push(vodItems.join("#"))
|
||||
}
|
||||
vodDetail.vod_play_from = form_list.join('$$$');
|
||||
vodDetail.vod_play_url = _.values(play_url_list).join('$$$');
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let items = $('.module-search-item');
|
||||
let vod_list = [];
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = $(item).find(".video-serial")[0].attribs.href;
|
||||
vodShort.vod_name = $(item).find(".video-serial")[0].attribs.title;
|
||||
vodShort.vod_pic = $(item).find(".module-item-pic > img")[0].attribs['data-src']
|
||||
vodShort.vod_remarks = '';
|
||||
vod_list.push(vodShort);
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let con = await this.fetch(this.siteUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(con)) {
|
||||
const $ = load(con);
|
||||
let elements = $($("[class=\"nav-menu-items\"]")[0]).find("li")
|
||||
for (const element of elements.slice(0,6)) {
|
||||
let type_name = $($(element).find("span")).text()
|
||||
if (type_name !== "首页"){
|
||||
let type_id = $(element).find("a")[0].attribs["href"].split("/").slice(-1)[0].split(".")[0]
|
||||
let type_dic = {"type_id": type_id, "type_name": type_name}
|
||||
this.classes.push(type_dic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let elements = $("[class=\"scroll-content\"]").slice(1)
|
||||
let extend_list = []
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let extend_dic = {"key": (i + 1).toString(), "name": "", "value": []}
|
||||
if (i < elements.length - 1) {
|
||||
extend_dic["name"] = $($(elements[i]).find("a")[0]).text()
|
||||
extend_dic["value"].push({"n": "全部", "v": "0"})
|
||||
for (const ele of $(elements[i]).find("a").slice(1)) {
|
||||
if ($($(elements[i]).find("a")[0]).text() === "全部类型"){
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v":ele.attribs["href"].split("/").slice(-1)[0].split(".")[0]})
|
||||
}else{
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v":$(ele).text()})
|
||||
|
||||
}
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
} else {
|
||||
extend_dic["name"] = $($(elements[i]).find("a")[0]).text()
|
||||
extend_dic["value"] = [{"n": "全部", "v": "0"}, {
|
||||
"n": $($(elements[i]).find("a")[1]).text(),
|
||||
"v": "hits"
|
||||
}, {"n": $($(elements[i]).find("a")[2]).text(), "v": "score"}]
|
||||
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const type_dic of this.classes) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "/" && type_id !== "最近更新") {
|
||||
let url = this.siteUrl + `/vodshow/id/${type_id}.html`
|
||||
let html = await this.fetch(url, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async setHomeVod() {
|
||||
let con = await this.fetch(this.siteUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(con)) {
|
||||
const $ = load(con);
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
getExtend(value,defaultvalue,key = ""){
|
||||
if (value !== undefined && value !== "0"){
|
||||
return key + value
|
||||
}else{
|
||||
return defaultvalue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async getCateUrl(tid,pg,extend){
|
||||
tid = this.getExtend(extend["1"],tid)
|
||||
let area = this.getExtend(extend["2"],"","/area/")
|
||||
let lanuage = this.getExtend(extend["3"],"","/lang/")
|
||||
let year = this.getExtend(extend["4"],"","/year/")
|
||||
let letter = this.getExtend(extend["5"],"","/letter/")
|
||||
let time = this.getExtend(extend['6'],"","/by/")
|
||||
return this.siteUrl + `/vodshow${time}${area}/id/${tid}${lanuage}${letter}${year}/page/${pg}.html`
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let reqUrl = await this.getCateUrl(tid,pg,extend)
|
||||
let html = await this.fetch(reqUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
let total = Utils.getStrByRegex(/\$\("\.mac_total"\)\.text\('(\d+)'\)/, html)
|
||||
this.limit = 72;
|
||||
if (total.length > 0) {
|
||||
this.total = parseInt(total)
|
||||
}
|
||||
if (this.total <= this.limit) {
|
||||
this.count = 1
|
||||
} else {
|
||||
this.count = Math.ceil(this.total / this.limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let detailUrl = this.siteUrl + id;
|
||||
let html = await this.fetch(detailUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let html = await this.fetch(this.siteUrl + id,null,this.getHeader())
|
||||
if (!_.isEmpty(html)){
|
||||
let player_str = Utils.getStrByRegex(/<script type="text\/javascript">var player_aaaa=(.*?)<\/script>/,html)
|
||||
let play_dic = JSON.parse(player_str)
|
||||
this.playUrl = play_dic["url"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let searchUrl = this.siteUrl + `/vodsearch/wd/${wd}.html`
|
||||
let html = await this.fetch(searchUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new AiYingShiSpider()
|
||||
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,proxy:proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
230
cat/tjs/js/alipansou.js
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* @File : alipansou.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/18 13:20
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 猫狸盘搜
|
||||
*/
|
||||
|
||||
import {_, load} from "../lib/cat.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {detailContent, initAli, playContent} from "../lib/ali.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
class GitCafeSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.alipansou.com"
|
||||
}
|
||||
getSearchHeader(id) {
|
||||
let headers = this.getHeader()
|
||||
headers["Referer"] = id
|
||||
headers["Postman-Token"] = "5f1bb291-ce30-44c7-8885-6db1f3a50785"
|
||||
headers["Host"] = "www.alipansou.com"
|
||||
return headers
|
||||
}
|
||||
|
||||
|
||||
getName() {
|
||||
return "😸┃阿里猫狸┃😸"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "阿里猫狸"
|
||||
}
|
||||
getJSName() {
|
||||
return "alipansou"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
|
||||
return {
|
||||
"User-Agent":Utils.CHROME,
|
||||
"Connection": "keep-alive",
|
||||
"Cookie":"_ga=GA1.1.1506025676.1708225506;FCNEC=%5B%5B%22AKsRol9sCpH4JteOAAMprJLQxCHddrtkOFinxqt1cs8x3fKzbBZ5Ll76VvjATz1Ejf6NoayGSONFl2gfn6PbVAG97MlHjhp6cY5NFLQtLIUy0TuzI1_ThHnANe8fW03fHdU2-cx5yM3MftaHt4awEGBWhgtE9H_P5w%3D%3D%22%5D%5D;_cc_id=cc82bd83ea8936df45fe63c887a6f221;mysession=MTcwOTYyMjMxMHxEdi1CQkFFQ180SUFBUkFCRUFBQU1fLUNBQUVHYzNSeWFXNW5EQXdBQ25ObFlYSmphRjlyWlhrR2MzUnlhVzVuREJFQUQtV1JxT1draE9tWnBPUzRpZVd1c3c9PXyjHmLCdFvUlsW_gilBojjCq1ak-ffOud6aZKm3kxzJ4w==;Hm_lvt_02f69e0ba673e328ef49b5fb98dd4601=1708225506,1709622301,1710414091;_bid=28d3966abb8cf873ea912b715552f587;cf_clearance=6LuYs83fWIZlcwwzZkgRyYyFrP6Hndxe_CgByMe.pMs-1710414092-1.0.1.1-V44M.u7MNIozBytYixxp4Qe1OVr.CBH78.IEK2QJTWGQ7.HQBR0DoUgiSfpa23U.nxtOfhkrASpqogvz53knnw;cto_bundle=-WbYyl9VWGZjQkhzZ0gyQjE4VXNlcTJnYTNaV3dMaTdVV0xST3p5RkVnUTNxVWpxYVElMkZtNnVsaWtQSzdQU3JJY0slMkYxc3R5SXdyQlRzbkp1clVNZk84OElTR2MlMkJPeGx0bGtsUHk2VzhGdk1yYyUyRnB5eUNNblhKbWpzcjY1SVI1ODlWRGZXemgzUU51bGF5UWxFNVljcUZpd252bnVZZ1R1d0VXRmJ3S1FXQ1RCMXhVNCUzRA;Hm_lpvt_02f69e0ba673e328ef49b5fb98dd4601=1710416656;_ga_NYNC791BP2=GS1.1.1710414091.2.1.1710416656.0.0.0;_ga_0B2NFC7Z09=GS1.1.1710414091.2.1.1710416656.60.0.0;_egg=16a87a4666714be885e814217b225d50e"}
|
||||
}
|
||||
|
||||
async getContentHtml() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
return load(html)
|
||||
}
|
||||
}
|
||||
|
||||
async spiderInit() {
|
||||
this.content_html = await this.getContentHtml()
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await this.spiderInit()
|
||||
await super.init(cfg);
|
||||
await initAli(this.cfgObj["token"]);
|
||||
}
|
||||
|
||||
async parseClassFromDoc($) {
|
||||
let tap_elemets = $($("[id=\"app\"]")[0]).find("van-tab")
|
||||
let index = 0
|
||||
for (const tap_element of tap_elemets) {
|
||||
let type_name = tap_element.attribs["title"]
|
||||
if (type_name.indexOf("热搜") === -1 && type_name !== "游戏" && type_name !== "小说") {
|
||||
this.classes.push({"type_name": type_name, "type_id": index})
|
||||
}
|
||||
index = index + 1
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc(doc) {
|
||||
let vod_list = []
|
||||
let elements = this.content_html(doc).find("a")
|
||||
for (const element of elements) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = element.attribs["href"]
|
||||
vodShort.vod_name = this.content_html(element).text().split(".").slice(-1)[0]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async getAliUrl(id) {
|
||||
let url = this.siteUrl + id.replace("/s/", "/cv/")
|
||||
let headers = this.getSearchHeader(url)
|
||||
let content = await req(url,{postType:"get",headers:headers,redirect:2})
|
||||
await this.jadeLog.debug(`回复内容为:${JSON.stringify(content)}`)
|
||||
// let url = await this.fetch(this.siteUrl + id.replace("/s/", "/cv/"), null, headers, true)
|
||||
return content.headers.location
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let vodDetail = new VodDetail();
|
||||
vodDetail.vod_name = obj["name"]
|
||||
vodDetail.vod_remarks = obj["remarks"]
|
||||
let ali_url = await this.getAliUrl(obj["id"])
|
||||
await this.jadeLog.debug(`阿里分享链接为:${ali_url}`)
|
||||
if (!_.isEmpty(ali_url)) {
|
||||
let aliVodDetail = await detailContent([ali_url])
|
||||
vodDetail.vod_play_url = aliVodDetail.vod_play_url
|
||||
vodDetail.vod_play_from = aliVodDetail.vod_play_from
|
||||
}
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let elements = $($($("[id=\"app\"]")[0]).find("van-row")).find("a")
|
||||
let vod_list = []
|
||||
for (const element of elements) {
|
||||
let id = element.attribs["href"]
|
||||
let matches = id.match(/(\/s\/[^"])/);
|
||||
if (!_.isEmpty(matches) && id.indexOf("https") === -1) {
|
||||
let text = $(element).text().replaceAll("\n", "").replaceAll(" ", "")
|
||||
if (text.indexOf("时间") > -1 && text.indexOf("文件夹") > -1) {
|
||||
let textList = text.split("时间")
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_name = textList[0]
|
||||
vodShort.vod_remarks = textList[1].split("格式")[0].replaceAll(":", "").replaceAll(" ", "").replaceAll("", "").replaceAll(" ", "")
|
||||
vodShort.vod_id = JSON.stringify({
|
||||
"name": vodShort.vod_name, "remarks": vodShort.vod_remarks, "id": id
|
||||
})
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
await this.parseClassFromDoc(this.content_html)
|
||||
}
|
||||
|
||||
|
||||
async setHomeVod() {
|
||||
let tap_elemets = this.content_html(this.content_html("[id=\"app\"]")[0]).find("van-tab")
|
||||
this.homeVodList = await this.parseVodShortListFromDoc(tap_elemets[0])
|
||||
}
|
||||
|
||||
|
||||
async setDetail(id) {
|
||||
if (id.indexOf("search") > -1) {
|
||||
let url = this.siteUrl + "/search"
|
||||
let params = {"k":decodeURIComponent(id.split("search?k=").slice(-1)[0]) }
|
||||
let html = await this.fetch(url, params, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
let vod_list = await this.parseVodShortListFromDocBySearch($)
|
||||
if (vod_list.length > 0) {
|
||||
id = vod_list[0]["vod_id"]
|
||||
} else {
|
||||
id = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_.isEmpty(id)) {
|
||||
let json_content = JSON.parse(id)
|
||||
this.vodDetail = await this.parseVodDetailfromJson(json_content)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let tap_elemets = this.content_html(this.content_html("[id=\"app\"]")[0]).find("van-tab")
|
||||
this.vodList = await this.parseVodShortListFromDoc(tap_elemets[parseInt(tid)])
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = this.siteUrl + "/search"
|
||||
let params = {"k": wd}
|
||||
let html = await this.fetch(url, params, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let playObjStr = await playContent(flag, id, flags);
|
||||
this.playUrl = JSON.parse(playObjStr)["url"]
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new GitCafeSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
226
cat/tjs/js/aliyunpanshare.js
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* @File : aliyunpanshare.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/26 13:06
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 阿里云盘分享
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import {detailContent, initAli, playContent} from "../lib/ali.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {patternAli} from "../lib/utils.js";
|
||||
|
||||
let remark_list = ["4k", "4K"]
|
||||
|
||||
class AliyunpanShare extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = 'https://www.alypw.com';
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await initAli(this.cfgObj["token"]);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🥏┃阿里云盘分享┃🥏"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "阿里云盘分享"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "aliyunpanshare"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
getRemarks(name, title) {
|
||||
if (_.isEmpty(name)) {
|
||||
for (const remark of remark_list) {
|
||||
if (title.indexOf(remark) > -1) {
|
||||
return remark
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
parseVodName(name) {
|
||||
let vod_name = Utils.getStrByRegex(/\[阿里云盘](.*?) /, name)
|
||||
if (name.indexOf("合集") > -1) {
|
||||
return ""
|
||||
}
|
||||
if (_.isEmpty(vod_name)) {
|
||||
vod_name = Utils.getStrByRegex(/\[阿里云盘](.*?)(/, name)
|
||||
}
|
||||
if (vod_name.indexOf("[") > -1) {
|
||||
vod_name = vod_name.split("[")[0]
|
||||
}
|
||||
if (vod_name.indexOf("【") > -1) {
|
||||
vod_name = vod_name.split("【")[0]
|
||||
}
|
||||
if (vod_name === "4K") {
|
||||
return ""
|
||||
}
|
||||
return vod_name
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $($("[class=\"hometab\"]").find("[class=\"box\"]")).find("li")
|
||||
for (const vodElement of vodElements) {
|
||||
let ele = $(vodElement).find("[class=\"imgr\"]")[0]
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = $(ele).find("a")[0].attribs["href"]
|
||||
let name = $(ele).find("a")[0].attribs["title"]
|
||||
vodShort.vod_name = this.parseVodName(name)
|
||||
vodShort.vod_pic = $(vodElement).find("img")[0].attribs["src"]
|
||||
vodShort.vod_remarks = this.getRemarks(Utils.getStrByRegex(/【(.*?)】/, name), name)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let mainElement = $("[class=\"main container\"]")
|
||||
let vodElements = $($(mainElement).find("[class=\"list\"]")).find("li")
|
||||
if (vodElements.length === 0) {
|
||||
vodElements = $(mainElement).find("li")
|
||||
}
|
||||
for (const vodElement of vodElements) {
|
||||
let name = $(vodElement).find("img")[0].attribs["alt"].replaceAll("<strong>", "").replaceAll("</strong>", "")
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = $(vodElement).find("a")[0].attribs["href"]
|
||||
vodShort.vod_name = this.parseVodName(name)
|
||||
vodShort.vod_pic = $(vodElement).find("img")[0].attribs["src"]
|
||||
vodShort.vod_remarks = this.getRemarks(Utils.getStrByRegex(/【(.*?)】/, name), name)
|
||||
if (!_.isEmpty(vodShort.vod_name)) {
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let mainElements = $("[class=\"mainl\"]")
|
||||
let name = $($(mainElements).find("[class=\"title\"]")[0]).text()
|
||||
let vodDetail = new VodDetail();
|
||||
vodDetail.vod_name = Utils.getStrByRegex(/\[阿里云盘](.*?) /, name)
|
||||
vodDetail.vod_remarks = this.getRemarks(Utils.getStrByRegex(/【(.*?)】/, name), name)
|
||||
let articleElement = $(mainElements).find("[class=\"article_content\"]")
|
||||
vodDetail.vod_pic = $(articleElement).find("p>img")[0].attribs["src"]
|
||||
let articleElements = $(articleElement).find("p")
|
||||
let articleContent = ""
|
||||
for (const articleEle of articleElements) {
|
||||
articleContent = articleContent + $(articleEle).text() + "\n"
|
||||
}
|
||||
let share_ali_url_list = []
|
||||
let share_url_list = Utils.getStrByRegex(Utils.patternAli, articleContent).split("\n")
|
||||
for (const share_url of share_url_list) {
|
||||
let matches = share_url.match(Utils.patternAli);
|
||||
if (!_.isEmpty(matches)) share_ali_url_list.push(matches[1])
|
||||
}
|
||||
let aliVodDetail = await detailContent(share_ali_url_list)
|
||||
vodDetail.vod_play_url = aliVodDetail.vod_play_url
|
||||
vodDetail.vod_play_from = aliVodDetail.vod_play_from
|
||||
vodDetail.type_name = Utils.getStrByRegex(/标签(.*?)\n/, articleContent).replaceAll(":", "")
|
||||
vodDetail.vod_content = Utils.getStrByRegex(/描述(.*?)\n/, articleContent).replaceAll(":", "")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let typeElements = $("[id^='navbar-category']").find("a")
|
||||
let key_list = ["影", "剧", "4K", "视", "音", "演", "动漫"]
|
||||
for (const typeElement of typeElements) {
|
||||
let type_name = $(typeElement).text()
|
||||
let type_id = typeElement.attribs["href"]
|
||||
let is_show = false
|
||||
for (const key of key_list) {
|
||||
if (type_name.indexOf(key) > -1) {
|
||||
is_show = true
|
||||
}
|
||||
}
|
||||
if (is_show) {
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let cateUrl = tid.split(".html")[0] + "_" + pg + ".html"
|
||||
let $ = await this.getHtml(cateUrl)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let playObjStr = await playContent(flag, id, flags);
|
||||
this.playUrl = JSON.parse(playObjStr)["url"]
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = this.siteUrl + `/search.php?q=${wd}`
|
||||
let $ = await this.getHtml(url)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new AliyunpanShare()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
183
cat/tjs/js/asianx.js
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* @Author: samples jadehh@live.com
|
||||
* @Date: 2023-12-14 11:03:04
|
||||
* @LastEditors: samples jadehh@live.com
|
||||
* @LastEditTime: 2023-12-14 11:03:04
|
||||
* @FilePath: js/asianx.js
|
||||
* @Description: asianx
|
||||
*/
|
||||
|
||||
|
||||
import {Spider} from "./spider.js";
|
||||
import {Crypto, _, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
class AsianXSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://cn.asianx.tube/"
|
||||
}
|
||||
|
||||
|
||||
getName() {
|
||||
return "🔞┃海外宅┃🔞"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "海外宅"
|
||||
}
|
||||
getJSName() {
|
||||
return "asianx"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let navElements = $($("[class=\"menu m-0 mb-2 mb-lg-0\"]")).find("a").slice(6)
|
||||
let extend_dic = {"key": "1", "name": "分类", "value": [{"n": "全部", "v": "全部"}]}
|
||||
for (const navElement of navElements) {
|
||||
let type_name = $($(navElement).find("span")).text()
|
||||
let type_id = navElement.attribs["href"]
|
||||
extend_dic["value"].push({"n": type_name, "v": type_id})
|
||||
}
|
||||
return [extend_dic]
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($,is_home=false) {
|
||||
let vod_list = []
|
||||
let vodShortElements;
|
||||
if (is_home){
|
||||
vodShortElements = $($("[class=\"gal-box\"]")).slice(12)
|
||||
}else{
|
||||
vodShortElements = $($("[class=\"gal-box\"]"))
|
||||
}
|
||||
for (const vodShortElement of vodShortElements) {
|
||||
let vodShort = new VodShort()
|
||||
let vodElements = $(vodShortElement).find("a")
|
||||
vodShort.vod_id = vodElements[0].attribs["href"]
|
||||
vodShort.vod_pic = $(vodElements[0]).find("img")[0].attribs["data-src"]
|
||||
vodShort.vod_name = vodElements[1].attribs["title"]
|
||||
vodShort.vod_remarks = $($(vodShortElement).find("[class=\"meta text-muted text-truncate\"]")).text()
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc(html) {
|
||||
let vodDetail = new VodDetail();
|
||||
let content = Utils.getStrByRegex(/<script type="application\/ld\+json">(.*?)<\/script>/,html)
|
||||
let content_json = JSON.parse(content)
|
||||
let textList = content_json["name"].split(" ")
|
||||
vodDetail.vod_name = textList[0]
|
||||
vodDetail.vod_play_from = ["未加密线路","加密线路"].join("$$$")
|
||||
let playUrl1 = content_json["contentUrl"]
|
||||
let playUrl2 = content_json["embedUrl"]
|
||||
vodDetail.vod_play_url = [`${textList[0]}$${playUrl1}`,`${textList[0]}$${playUrl2}`].join("$$$")
|
||||
vodDetail.vod_remarks = content_json["uploadDate"]
|
||||
vodDetail.vod_content = content_json["description"]
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
this.classes = []
|
||||
this.classes.push({"type_name": "首页", "type_id": "/"})
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
let navElements = $($("[class=\"menu m-0 mb-2 mb-lg-0\"]")).find("a").slice(0, 5)
|
||||
for (const navElement of navElements) {
|
||||
let type_name = $($(navElement).find("span")).text()
|
||||
let type_id = navElement.attribs["href"]
|
||||
this.classes.push({"type_name": type_name, "type_id": type_id})
|
||||
}
|
||||
this.filterObj[this.classes[0].type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($,true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getExtend(pg,extend){
|
||||
if (extend["1"] !== undefined){
|
||||
if (extend["1"] === "全部"){
|
||||
return this.siteUrl
|
||||
}else{
|
||||
return this.siteUrl + extend["1"] + "/" + pg
|
||||
}
|
||||
}else{
|
||||
return this.siteUrl
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let html = await this.fetch(id,null,this.getHeader())
|
||||
if (!_.isEmpty(html)){
|
||||
this.vodDetail = await this.parseVodDetailFromDoc(html)
|
||||
}
|
||||
}
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let url;
|
||||
if (tid === "/") {
|
||||
url = this.getExtend(pg,extend)
|
||||
} else {
|
||||
url = this.siteUrl + tid + "/" + pg
|
||||
}
|
||||
let html = await this.fetch(url, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($,false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new AsianXSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
258
cat/tjs/js/audiomack.js
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* @File : audiomack.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/31 15:56
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 音乐之声
|
||||
*/
|
||||
import {Spider} from "./spider.js";
|
||||
import {BookShort} from "../lib/book.js";
|
||||
import {Crypto} from "../lib/cat.js";
|
||||
|
||||
function u(e) {
|
||||
(this._parameters = {}), this._loadParameters(e || {});
|
||||
}
|
||||
|
||||
u.prototype = {
|
||||
_loadParameters: function (e) {
|
||||
e instanceof Array ? this._loadParametersFromArray(e) : "object" == typeof e && this._loadParametersFromObject(e);
|
||||
}, _loadParametersFromArray: function (e) {
|
||||
var n;
|
||||
for (n = 0; n < e.length; n++) this._loadParametersFromObject(e[n]);
|
||||
}, _loadParametersFromObject: function (e) {
|
||||
var n;
|
||||
for (n in e) if (e.hasOwnProperty(n)) {
|
||||
var r = this._getStringFromParameter(e[n]);
|
||||
this._loadParameterValue(n, r);
|
||||
}
|
||||
}, _loadParameterValue: function (e, n) {
|
||||
var r;
|
||||
if (n instanceof Array) {
|
||||
for (r = 0; r < n.length; r++) {
|
||||
var i = this._getStringFromParameter(n[r]);
|
||||
this._addParameter(e, i);
|
||||
}
|
||||
0 == n.length && this._addParameter(e, "");
|
||||
} else this._addParameter(e, n);
|
||||
}, _getStringFromParameter: function (e) {
|
||||
var n = e || "";
|
||||
try {
|
||||
("number" == typeof e || "boolean" == typeof e) && (n = e.toString());
|
||||
} catch (e) {
|
||||
}
|
||||
return n;
|
||||
}, _addParameter: function (e, n) {
|
||||
this._parameters[e] || (this._parameters[e] = []), this._parameters[e].push(n);
|
||||
}, get: function () {
|
||||
return this._parameters;
|
||||
},
|
||||
};
|
||||
|
||||
function _decode(e) {
|
||||
return e ? decodeURIComponent(e) : "";
|
||||
}
|
||||
|
||||
function getNormalizedParams(parameters) {
|
||||
const sortedKeys = [];
|
||||
const normalizedParameters = [];
|
||||
for (let e in parameters) {
|
||||
sortedKeys.push(_encode(e));
|
||||
}
|
||||
sortedKeys.sort();
|
||||
for (let idx = 0; idx < sortedKeys.length; idx++) {
|
||||
const e = sortedKeys[idx];
|
||||
var n, r, i = _decode(e), a = parameters[i];
|
||||
for (a.sort(), n = 0; n < a.length; n++) (r = _encode(a[n])), normalizedParameters.push(e + "=" + r);
|
||||
}
|
||||
return normalizedParameters.join("&");
|
||||
}
|
||||
|
||||
function nonce(e = 10) {
|
||||
let n = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", r = "";
|
||||
for (let i = 0; i < e; i++) r += n.charAt(Math.floor(Math.random() * n.length));
|
||||
return r;
|
||||
}
|
||||
|
||||
function _encode(e) {
|
||||
return e ? encodeURIComponent(e)
|
||||
.replace(/[!'()]/g, escape)
|
||||
.replace(/\*/g, "%2A") : "";
|
||||
}
|
||||
|
||||
function getSignature(method, urlPath, params, secret = "f3ac5b086f3eab260520d8e3049561e6") {
|
||||
urlPath = urlPath.split("?")[0];
|
||||
urlPath = urlPath.startsWith("http") ? urlPath : "https://api.audiomack.com/v1" + urlPath;
|
||||
const r = new u(params).get();
|
||||
const httpMethod = method.toUpperCase();
|
||||
const normdParams = getNormalizedParams(r);
|
||||
const l = _encode(httpMethod) + "&" + _encode(urlPath) + "&" + _encode(normdParams);
|
||||
return Crypto.HmacSHA1(l, secret + "&").toString(Crypto.enc.Base64);
|
||||
}
|
||||
|
||||
class AudioMackSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://api.audiomack.com/v1";
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🎵┃音声┃🎵"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "音声"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "audiomack"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 10
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let books = []
|
||||
for (const data of obj["results"]["playlists"]) {
|
||||
let bookShort = new BookShort()
|
||||
bookShort.book_id = data["id"]
|
||||
bookShort.book_pic = data["image"]
|
||||
bookShort.book_name = data["title"]
|
||||
bookShort.book_remarks = data["description"]
|
||||
books.push(bookShort)
|
||||
}
|
||||
return books
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
this.classes = [{"type_name": "推荐榜单", "type_id": "最近更新"}]
|
||||
const genres = [{
|
||||
title: "All Genres", url_slug: "null",
|
||||
}, {
|
||||
title: "Afrosounds", url_slug: "afrobeats",
|
||||
}, {
|
||||
title: "Hip-Hop/Rap", url_slug: "rap",
|
||||
}, {
|
||||
title: "Latin", url_slug: "latin",
|
||||
}, {
|
||||
title: "Caribbean", url_slug: "caribbean",
|
||||
}, {
|
||||
title: "Pop", url_slug: "pop",
|
||||
}, {
|
||||
title: "R&B", url_slug: "rb",
|
||||
}, {
|
||||
title: "Gospel", url_slug: "gospel",
|
||||
}, {
|
||||
title: "Electronic", url_slug: "electronic",
|
||||
}, {
|
||||
title: "Rock", url_slug: "rock",
|
||||
}, {
|
||||
title: "Punjabi", url_slug: "punjabi",
|
||||
}, {
|
||||
title: "Country", url_slug: "country",
|
||||
}, {
|
||||
title: "Instrumental", url_slug: "instrumental",
|
||||
}, {
|
||||
title: "Podcast", url_slug: "podcast",
|
||||
},];
|
||||
for (const genre of genres) {
|
||||
this.classes.push(this.getTypeDic(genre["title"], genre["url_slug"]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 推荐歌单
|
||||
* */
|
||||
async setHomeVod() {
|
||||
let tag = {id: "34", title: "What's New", url_slug: "whats-new"};
|
||||
const params = {
|
||||
featured: "yes",
|
||||
limit: 20,
|
||||
oauth_consumer_key: "audiomack-js",
|
||||
oauth_nonce: nonce(32),
|
||||
oauth_signature_method: "HMAC-SHA1",
|
||||
oauth_timestamp: Math.round(Date.now() / 1e3),
|
||||
oauth_version: "1.0",
|
||||
page: 1,
|
||||
slug: tag.url_slug,
|
||||
};
|
||||
const oauth_signature = getSignature("GET", "/playlist/categories", params);
|
||||
let url = this.siteUrl + "/playlist/categories"
|
||||
let content = await this.fetch(url, Object.assign(Object.assign({}, params), {oauth_signature}), this.getHeader());
|
||||
this.homeVodList = await this.parseVodShortListFromJson(JSON.parse(content))
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let partialUrl;
|
||||
if (tid === "null"){
|
||||
partialUrl = `/music/page/${pg}`;
|
||||
}else{
|
||||
partialUrl = `/music/${tid}/page/${pg}`;
|
||||
}
|
||||
|
||||
const url = `https://api.audiomack.com/v1${partialUrl}`;
|
||||
const params = {
|
||||
oauth_consumer_key: "audiomack-js",
|
||||
oauth_nonce: nonce(32),
|
||||
oauth_signature_method: "HMAC-SHA1",
|
||||
oauth_timestamp: Math.round(Date.now() / 1e3),
|
||||
oauth_version: "1.0",
|
||||
type: "song",
|
||||
};
|
||||
const oauth_signature = getSignature("GET", partialUrl, params);
|
||||
const results = await this.fetch(url, Object.assign(Object.assign({}, params), {oauth_signature}),this.getHeader())
|
||||
|
||||
let x = 0
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
let spider = new AudioMackSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
85
cat/tjs/js/base_spider.js
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* @File : base_spider.js.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/4 14:13
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {JadeLogging} from "../lib/log.js";
|
||||
import {Result, SpiderInit} from "../lib/spider_object.js";
|
||||
const JadeLog = new JadeLogging(getAppName(), "DEBUG")
|
||||
let result = new Result()
|
||||
let CatOpenStatus = false
|
||||
|
||||
function getName() {
|
||||
return `🍥┃基础┃🍥`
|
||||
}
|
||||
|
||||
function getAppName() {
|
||||
return `基础`
|
||||
}
|
||||
|
||||
async function init(cfg) {
|
||||
let obj = await SpiderInit(cfg)
|
||||
CatOpenStatus = obj.CatOpenStatus
|
||||
// 读取缓存
|
||||
}
|
||||
|
||||
|
||||
async function home(filter) {
|
||||
await JadeLog.info("正在解析首页类别", true)
|
||||
try{
|
||||
await JadeLog.debug(`首页类别内容为:${result.home()}`)
|
||||
await JadeLog.info("首页类别解析完成", true)
|
||||
return result.homeVod()
|
||||
}catch (e){
|
||||
await this.jadeLog.error(`首页内容解析失败,失败原因为:{e}`)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function homeVod() {
|
||||
let vod_list = []
|
||||
if (!CatOpenStatus) {
|
||||
await JadeLog.info("正在解析首页内容")
|
||||
}
|
||||
await JadeLog.debug(`首页内容为:${JSON.stringify({"list": vod_list})}`)
|
||||
return JSON.stringify({"list": vod_list})
|
||||
}
|
||||
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
let url = ""
|
||||
await JadeLog.info(`正在解析分类页面,tid = ${tid},pg = ${pg},filter = ${filter},extend = ${JSON.stringify(extend)},url = ${url}`)
|
||||
}
|
||||
|
||||
|
||||
async function detail(id) {
|
||||
return JSON.stringify({})
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
|
||||
return JSON.stringify({});
|
||||
}
|
||||
|
||||
|
||||
async function search(wd, quick) {
|
||||
let url = ""
|
||||
await JadeLog.info(`正在解析搜索页面,关键词为 = ${wd},quick = ${quick},url = ${url}`)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
550
cat/tjs/js/bilibili.js
Normal file
@ -0,0 +1,550 @@
|
||||
/*
|
||||
* @File : bilibili.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/4/3 9:27
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 哔哩哔哩
|
||||
*/
|
||||
import {Spider} from "./spider.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Crypto, _, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
|
||||
class BilibiliSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.bilibili.com"
|
||||
this.apiUrl = "https://api.bilibili.com"
|
||||
this.cookie = ""
|
||||
this.bili_jct = '';
|
||||
this.is_login = false
|
||||
this.is_vip = false
|
||||
this.vod_audio_id = {
|
||||
30280: 192000,
|
||||
30232: 132000,
|
||||
30216: 64000,
|
||||
};
|
||||
this.vod_codec = {
|
||||
// 13: 'AV1',
|
||||
12: 'HEVC',
|
||||
7: 'AVC',
|
||||
};
|
||||
|
||||
this.play_url_obj = {
|
||||
80: "1080P 高清",
|
||||
64: "720P 高清",
|
||||
32: "420P 清晰",
|
||||
16: "360P 流畅"
|
||||
}
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
const headers = super.getHeader();
|
||||
if (!_.isEmpty(this.cookie)) {
|
||||
headers["cookie"] = this.cookie;
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
initCookie(cookie) {
|
||||
this.cookie = cookie
|
||||
if (cookie.includes('bili_jct')) {
|
||||
this.bili_jct = cookie.split('bili_jct=')[1].split(";")[0];
|
||||
}
|
||||
}
|
||||
|
||||
async spiderInit(Req) {
|
||||
this.is_login = await this.checkLogin()
|
||||
if (this.is_login) {
|
||||
await this.jadeLog.info("哔哩哔哩登录成功", true)
|
||||
} else {
|
||||
await this.jadeLog.error("哔哩哔哩登录失败", true)
|
||||
}
|
||||
if (Req === null) {
|
||||
// dash mpd 代理
|
||||
this.js2Base = await js2Proxy(true, this.siteType, this.siteKey, 'dash/', this.getHeader());
|
||||
} else {
|
||||
this.js2Base = await js2Proxy(Req, "dash", this.getHeader());
|
||||
}
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await this.initCookie(cfg["ext"]["cookie"])
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🏰┃哔哩哔哩┃🏰"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "哔哩哔哩"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "bilibili"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml(this.siteUrl)
|
||||
let navElements = $("[class=\"channel-items__left\"]").find("a")
|
||||
for (const navElement of navElements) {
|
||||
this.classes.push(this.getTypeDic($(navElement).text(), $(navElement).text()))
|
||||
}
|
||||
if (!_.isEmpty(this.bili_jct)) {
|
||||
this.classes.push(this.getTypeDic("历史记录", "历史记录"))
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
return [
|
||||
{
|
||||
key: 'order',
|
||||
name: '排序',
|
||||
value: [
|
||||
{n: '综合排序', v: '0'},
|
||||
{n: '最多点击', v: 'click'},
|
||||
{n: '最新发布', v: 'pubdate'},
|
||||
{n: '最多弹幕', v: 'dm'},
|
||||
{n: '最多收藏', v: 'stow'},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'duration',
|
||||
name: '时长',
|
||||
value: [
|
||||
{n: '全部时长', v: '0'},
|
||||
{n: '60分钟以上', v: '4'},
|
||||
{n: '30~60分钟', v: '3'},
|
||||
{n: '10~30分钟', v: '2'},
|
||||
{n: '10分钟以下', v: '1'},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const typeDic of this.classes) {
|
||||
let type_id = typeDic["type_name"]
|
||||
if (type_id !== "最近更新" && type_id !== "历史记录") {
|
||||
this.filterObj[type_id] = await this.getFilter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getFullTime(numberSec) {
|
||||
let totalSeconds = '';
|
||||
try {
|
||||
let timeParts = numberSec.split(":");
|
||||
let min = parseInt(timeParts[0]);
|
||||
let sec = parseInt(timeParts[1]);
|
||||
totalSeconds = min * 60 + sec;
|
||||
} catch (e) {
|
||||
totalSeconds = parseInt(numberSec);
|
||||
}
|
||||
if (isNaN(totalSeconds)) {
|
||||
return '无效输入';
|
||||
}
|
||||
if (totalSeconds >= 3600) {
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const remainingSecondsAfterHours = totalSeconds % 3600;
|
||||
const minutes = Math.floor(remainingSecondsAfterHours / 60);
|
||||
const seconds = remainingSecondsAfterHours % 60;
|
||||
return `${hours}小时 ${minutes}分钟 ${seconds}秒`;
|
||||
} else {
|
||||
const minutes = Math.floor(totalSeconds / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
return `${minutes}分钟 ${seconds}秒`;
|
||||
}
|
||||
}
|
||||
|
||||
removeTags(input) {
|
||||
return input.replace(/<[^>]*>/g, '');
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(objList) {
|
||||
let vod_list = []
|
||||
for (const vodData of objList) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = vodData["bvid"]
|
||||
if (vodData.hasOwnProperty("rcmd_reason")) {
|
||||
vodShort.vod_remarks = vodData["rcmd_reason"]["content"]
|
||||
} else {
|
||||
vodShort.vod_remarks = this.getFullTime(vodData["duration"])
|
||||
}
|
||||
|
||||
vodShort.vod_name = this.removeTags(vodData["title"])
|
||||
let imageUrl = vodData["pic"];
|
||||
if (imageUrl.startsWith('//')) {
|
||||
imageUrl = 'https:' + imageUrl;
|
||||
}
|
||||
vodShort.vod_pic = imageUrl
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj, bvid) {
|
||||
let cd = this.getFullTime(obj["duration"]);
|
||||
const aid = obj.aid;
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = obj["title"]
|
||||
vodDetail.vod_pic = obj["pic"]
|
||||
vodDetail.type_name = obj["tname"]
|
||||
vodDetail.vod_remarks = cd
|
||||
vodDetail.vod_content = obj["desc"]
|
||||
|
||||
let params = {"avid": aid, "cid": obj["cid"], "qn": "127", "fnval": 4048, "fourk": 1}
|
||||
let playUrlDatas = JSON.parse(await this.fetch(this.apiUrl + "/x/player/playurl", params, this.getHeader()));
|
||||
let playUrldDataList = playUrlDatas["data"];
|
||||
const accept_quality = playUrldDataList["accept_quality"];
|
||||
const accept_description = playUrldDataList["accept_description"];
|
||||
const qualityList = [];
|
||||
const descriptionList = [];
|
||||
|
||||
for (let i = 0; i < accept_quality.length; i++) {
|
||||
if (!this.is_vip) {
|
||||
if (this.is_login) {
|
||||
if (accept_quality[i] > 80) continue;
|
||||
} else {
|
||||
if (accept_quality[i] > 32) continue;
|
||||
}
|
||||
|
||||
}
|
||||
descriptionList.push(Utils.base64Encode(accept_description[i]));
|
||||
qualityList.push(accept_quality[i]);
|
||||
}
|
||||
let treeMap = {};
|
||||
const jSONArray = obj["pages"];
|
||||
let playList = [];
|
||||
for (let j = 0; j < jSONArray.length; j++) {
|
||||
const jSONObject6 = jSONArray[j];
|
||||
const cid = jSONObject6.cid;
|
||||
const playUrl = j + '$' + aid + '+' + cid + '+' + qualityList.join(':') + '+' + descriptionList.join(':');
|
||||
playList.push(playUrl);
|
||||
}
|
||||
for (let quality of qualityList) {
|
||||
treeMap[`dash - ${this.play_url_obj[quality]}`] = playList.join("#")
|
||||
}
|
||||
for (let quality of qualityList) {
|
||||
treeMap[`mp4 - ${this.play_url_obj[quality]}`] = playList.join("#")
|
||||
}
|
||||
let relatedParams = {"bvid": bvid}
|
||||
const relatedData = JSON.parse(await this.fetch(this.apiUrl + "/x/web-interface/archive/related", relatedParams, this.getHeader())).data;
|
||||
playList = [];
|
||||
for (let j = 0; j < relatedData.length; j++) {
|
||||
const jSONObject6 = relatedData[j];
|
||||
const cid = jSONObject6.cid;
|
||||
const title = jSONObject6.title;
|
||||
const aaid = jSONObject6.aid;
|
||||
const playUrl = title + '$' + aaid + '+' + cid + '+' + qualityList.join(':') + '+' + descriptionList.join(':');
|
||||
playList.push(playUrl);
|
||||
}
|
||||
for (let quality of qualityList) {
|
||||
treeMap["相关" + ` - ${this.play_url_obj[quality]}`] = playList.join("#")
|
||||
}
|
||||
vodDetail.vod_play_from = Object.keys(treeMap).join("$$$");
|
||||
vodDetail.vod_play_url = Object.values(treeMap).join("$$$");
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let params = {"ps": 20}
|
||||
let content = await this.fetch(this.apiUrl + "/x/web-interface/popular", params, this.getHeader())
|
||||
this.homeVodList = await this.parseVodShortListFromJson(JSON.parse(content)["data"]["list"])
|
||||
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
const detailUrl = this.apiUrl + "/x/web-interface/view";
|
||||
let params = {"bvid": id}
|
||||
|
||||
const detailData = JSON.parse(await this.fetch(detailUrl, params, this.getHeader())).data
|
||||
// 记录历史
|
||||
if (!_.isEmpty(this.bili_jct)) {
|
||||
const historyReport = this.apiUrl + '/x/v2/history/report';
|
||||
let dataPost = {
|
||||
aid: detailData.aid,
|
||||
cid: detailData.cid,
|
||||
csrf: this.bili_jct,
|
||||
}
|
||||
await this.post(historyReport, dataPost, this.getHeader(), "form");
|
||||
}
|
||||
this.vodDetail = await this.parseVodDetailfromJson(detailData, id)
|
||||
|
||||
}
|
||||
|
||||
findKeyByValue(obj, value) {
|
||||
for (const key in obj) {
|
||||
if (obj[key] === value) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
const ids = id.split('+');
|
||||
const aid = ids[0];
|
||||
const cid = ids[1];
|
||||
let quality_name = flag.split(" - ")[1]
|
||||
let quality_id = this.findKeyByValue(this.play_url_obj, quality_name)
|
||||
this.danmuUrl = this.apiUrl + '/x/v1/dm/list.so?oid=' + cid;
|
||||
this.result.header = this.getHeader()
|
||||
if (flag.indexOf("dash") > -1 || flag.indexOf('相关') > -1) {
|
||||
// dash mpd 代理
|
||||
this.playUrl = this.js2Base + Utils.base64Encode(aid + '+' + cid + '+' + quality_id)
|
||||
} else if (flag.indexOf('mp4') > -1) {
|
||||
// 直链
|
||||
const url = this.apiUrl + `/x/player/playurl`;
|
||||
let params = {"avid": aid, "cid": cid, "qn": parseInt(quality_id), "fourk": "1"}
|
||||
const resp = JSON.parse(await this.fetch(url, params, this.getHeader()));
|
||||
const data = resp.data;
|
||||
this.playUrl = data["durl"][0].url;
|
||||
} else {
|
||||
// 音频外挂
|
||||
let urls = [];
|
||||
let audios = [];
|
||||
const url = this.siteUrl + "/x/player/playurl"
|
||||
let params = {"avid": aid, "cid": cid, "qn": quality_id, "fnval": 4048, "fourk": 1};
|
||||
let resp = JSON.parse(await this.fetch(url, params, this.getHeader()));
|
||||
const dash = resp.data.dash;
|
||||
const video = dash.video;
|
||||
const audio = dash.audio;
|
||||
for (let j = 0; j < video.length; j++) {
|
||||
const dashjson = video[j];
|
||||
if (dashjson.id === quality_id) {
|
||||
for (const key in this.vod_codec) {
|
||||
if (dashjson["codecid"] === key) {
|
||||
urls.push(Utils.base64Decode(quality_id) + ' ' + this.vod_codec[key], dashjson["baseUrl"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (audios.length === 0) {
|
||||
for (let j = 0; j < audio.length; j++) {
|
||||
const dashjson = audio[j];
|
||||
for (const key in this.vod_audio_id) {
|
||||
if (dashjson.id === key) {
|
||||
audios.push({
|
||||
title: _.floor(parseInt(this.vod_audio_id[key]) / 1024) + 'Kbps',
|
||||
bit: this.vod_audio_id[key],
|
||||
url: dashjson["baseUrl"],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
audios = _.sortBy(audios, 'bit');
|
||||
}
|
||||
this.playUrl = urls
|
||||
this.extra = {"audio": audios}
|
||||
}
|
||||
}
|
||||
|
||||
async checkLogin() {
|
||||
let result = JSON.parse(await this.fetch('https://api.bilibili.com/x/web-interface/nav', null, this.getHeader()));
|
||||
this.is_vip = result["data"]["vipStatus"]
|
||||
return result["data"]["isLogin"]
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let page;
|
||||
if (parseInt(pg) < 1) {
|
||||
page = 1;
|
||||
} else {
|
||||
page = parseInt(pg)
|
||||
}
|
||||
if (Object.keys(extend).length > 0 && extend.hasOwnProperty('tid') && extend['tid'].length > 0) {
|
||||
tid = extend['tid'];
|
||||
}
|
||||
let url = '';
|
||||
url = this.apiUrl + `/x/web-interface/search/type?search_type=video&keyword=${encodeURIComponent(tid)}`;
|
||||
|
||||
if (Object.keys(extend).length > 0) {
|
||||
for (const k in extend) {
|
||||
if (k === 'tid') {
|
||||
continue;
|
||||
}
|
||||
url += `&${encodeURIComponent(k)}=${encodeURIComponent(extend[k])}`;
|
||||
}
|
||||
}
|
||||
url += `&page=${encodeURIComponent(page)}`;
|
||||
if (tid === "历史记录") {
|
||||
url = this.apiUrl + "/x/v2/history?pn=" + page;
|
||||
}
|
||||
const data = JSON.parse(await this.fetch(url, null, this.getHeader())).data;
|
||||
let items = data.result;
|
||||
if (tid === "历史记录") {
|
||||
items = data;
|
||||
}
|
||||
this.vodList = await this.parseVodShortListFromJson(items)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
const ext = {
|
||||
duration: '0',
|
||||
};
|
||||
let resp = JSON.parse(await this.category(wd, 1, true, ext));
|
||||
this.vodList = resp["list"]
|
||||
}
|
||||
|
||||
getDashMedia(dash) {
|
||||
try {
|
||||
let qnid = dash.id;
|
||||
const codecid = dash["codecid"];
|
||||
const media_codecs = dash["codecs"];
|
||||
const media_bandwidth = dash["bandwidth"];
|
||||
const media_startWithSAP = dash["startWithSap"];
|
||||
const media_mimeType = dash.mimeType;
|
||||
const media_BaseURL = dash["baseUrl"].replace(/&/g, '&');
|
||||
const media_SegmentBase_indexRange = dash["SegmentBase"]["indexRange"];
|
||||
const media_SegmentBase_Initialization = dash["SegmentBase"]["Initialization"];
|
||||
const mediaType = media_mimeType.split('/')[0];
|
||||
let media_type_params = '';
|
||||
|
||||
if (mediaType === 'video') {
|
||||
const media_frameRate = dash.frameRate;
|
||||
const media_sar = dash["sar"];
|
||||
const media_width = dash.width;
|
||||
const media_height = dash.height;
|
||||
media_type_params = `height='${media_height}' width='${media_width}' frameRate='${media_frameRate}' sar='${media_sar}'`;
|
||||
} else if (mediaType === 'audio') {
|
||||
for (const key in this.vod_audio_id) {
|
||||
if (qnid === key) {
|
||||
const audioSamplingRate = this.vod_audio_id[key];
|
||||
media_type_params = `numChannels='2' sampleRate='${audioSamplingRate}'`;
|
||||
}
|
||||
}
|
||||
}
|
||||
qnid += '_' + codecid;
|
||||
return `<AdaptationSet lang="chi">
|
||||
<ContentComponent contentType="${mediaType}"/>
|
||||
<Representation id="${qnid}" bandwidth="${media_bandwidth}" codecs="${media_codecs}" mimeType="${media_mimeType}" ${media_type_params} startWithSAP="${media_startWithSAP}">
|
||||
<BaseURL>${media_BaseURL}</BaseURL>
|
||||
<SegmentBase indexRange="${media_SegmentBase_indexRange}">
|
||||
<Initialization range="${media_SegmentBase_Initialization}"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>`;
|
||||
} catch (e) {
|
||||
// Handle exceptions here
|
||||
}
|
||||
}
|
||||
|
||||
getDash(ja, videoList, audioList) {
|
||||
const duration = ja.data.dash["duration"];
|
||||
const minBufferTime = ja.data.dash["minBufferTime"];
|
||||
return `<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" type="static" mediaPresentationDuration="PT${duration}S" minBufferTime="PT${minBufferTime}S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011">
|
||||
<Period duration="PT${duration}S" start="PT0S">
|
||||
${videoList}
|
||||
${audioList}
|
||||
</Period>
|
||||
</MPD>`;
|
||||
}
|
||||
|
||||
async proxy(segments, headers) {
|
||||
let what = segments[0];
|
||||
let url = Utils.base64Decode(segments[1]);
|
||||
if (what === 'dash') {
|
||||
const ids = url.split('+');
|
||||
const aid = ids[0];
|
||||
const cid = ids[1];
|
||||
const str5 = ids[2];
|
||||
const urls = this.apiUrl + `/x/player/playurl?avid=${aid}&cid=${cid}&qn=${str5}&fnval=4048&fourk=1`;
|
||||
let videoList = '';
|
||||
let audioList = '';
|
||||
let content = await this.fetch(urls, null, headers);
|
||||
let resp = JSON.parse(content)
|
||||
const dash = resp.data.dash;
|
||||
const video = dash.video;
|
||||
const audio = dash.audio;
|
||||
for (let i = 0; i < video.length; i++) {
|
||||
// if (i > 0) continue; // 只取一个
|
||||
const dashjson = video[i];
|
||||
if (dashjson.id.toString() === str5) {
|
||||
videoList += this.getDashMedia(dashjson);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < audio.length; i++) {
|
||||
// if (i > 0) continue;
|
||||
const ajson = audio[i];
|
||||
for (const key in this.vod_audio_id) {
|
||||
if (ajson.id.toString() === key) {
|
||||
audioList += this.getDashMedia(ajson);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mpd = this.getDash(resp, videoList, audioList);
|
||||
return JSON.stringify({
|
||||
code: 200,
|
||||
content: mpd,
|
||||
headers: {
|
||||
'Content-Type': 'application/dash+xml',
|
||||
},
|
||||
});
|
||||
}
|
||||
return JSON.stringify({
|
||||
code: 500,
|
||||
content: '',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new BilibiliSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
152
cat/tjs/js/bookan.js
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* @File : bookan.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/31 13:44
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {_} from '../lib/cat.js';
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
function formatPlayUrl(name) {
|
||||
return name
|
||||
.trim()
|
||||
.replace(/<|>|《|》/g, '')
|
||||
.replace(/\$|#/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
class BooKanSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://api.bookan.com.cn";
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🎵┃看书┃🎵"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "看书"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "bookan"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 10
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let books = [];
|
||||
for (const book of obj.list) {
|
||||
books.push({
|
||||
book_id: book.id, book_name: book.name, book_pic: book.cover, book_remarks: book.extra.author,
|
||||
});
|
||||
}
|
||||
return books
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
{
|
||||
this.classes = [{type_id: '1305', type_name: '少年读物'}, {
|
||||
type_id: '1304', type_name: '儿童文学'
|
||||
}, {type_id: '1320', type_name: '国学经典'}, {type_id: '1306', type_name: '文艺少年'}, {
|
||||
type_id: '1309', type_name: '育儿心经'
|
||||
}, {type_id: '1310', type_name: '心理哲学'}, {type_id: '1307', type_name: '青春励志'}, {
|
||||
type_id: '1312', type_name: '历史小说'
|
||||
}, {type_id: '1303', type_name: '故事会'}, {type_id: '1317', type_name: '音乐戏剧'}, {
|
||||
type_id: '1319', type_name: '相声评书'
|
||||
},]
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let content = await this.fetch(`${this.siteUrl}/voice/book/list?instance_id=25304&page=${pg}&category_id=${tid}&num=24`, null, this.getHeader());
|
||||
let data = JSON.parse(content).data;
|
||||
this.vodList = await this.parseVodShortListFromJson(data)
|
||||
}
|
||||
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let book = {
|
||||
audio: 1,
|
||||
type_name: '',
|
||||
book_year: '',
|
||||
book_area: '',
|
||||
book_remarks: '',
|
||||
book_actor: '',
|
||||
book_director: '',
|
||||
book_content: '',
|
||||
};
|
||||
let us = _.map(obj.list, function (b) {
|
||||
return formatPlayUrl(b.title) + '$' + b.file;
|
||||
}).join('#');
|
||||
book.volumes = '书卷';
|
||||
book.urls = us;
|
||||
return book
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let content = await this.fetch(`${this.siteUrl}/voice/album/units?album_id=${id}&page=1&num=200&order=1`, null, this.getHeader());
|
||||
let data = JSON.parse(content).data;
|
||||
this.vodDetail = await this.parseVodDetailfromJson(data)
|
||||
this.vodDetail.book_id = id
|
||||
}
|
||||
|
||||
async play(flag, id, flags) {
|
||||
return JSON.stringify({
|
||||
parse: 0, url: id,
|
||||
});
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let content = await this.fetch(`https://es.bookan.com.cn/api/v3/voice/book?instanceId=25304&keyword=${wd}&pageNum=1&limitNum=20`,null,this.getHeader());
|
||||
let data = JSON.parse(content).data;
|
||||
this.vodList = await this.parseVodShortListFromJson(data)
|
||||
}
|
||||
|
||||
}
|
||||
let spider = new BooKanSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
263
cat/tjs/js/bqg_open.js
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* @File : bqg_open.js.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/30 15:38
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {_} from '../lib/cat.js';
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {BookDetail, BookShort} from "../lib/book.js";
|
||||
|
||||
class BQQSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://m.13bqg.com"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "笔趣阁"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "bqg_open"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 10
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "📚︎┃笔趣阁┃📚︎"
|
||||
}
|
||||
|
||||
async spiderInit(inReq = null) {
|
||||
if (inReq !== null) {
|
||||
this.jsBase = await js2Proxy(inReq, "img", this.getHeader());
|
||||
} else {
|
||||
this.jsBase = await js2Proxy(true, this.siteType, this.siteKey, 'img/', this.getHeader());
|
||||
}
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let books = []
|
||||
let bookElements = $("[class=\"block\"]")
|
||||
for (const bookElement of $(bookElements[0]).find("li")) {
|
||||
let bookShort = new BookShort()
|
||||
let bookShortElements = $(bookElement).find("span")
|
||||
bookShort.book_remarks = $(bookShortElements[0]).text()
|
||||
bookShort.book_name = $(bookShortElements[1]).text()
|
||||
bookShort.book_id = $(bookShortElements[1]).find("a")[0].attribs.href
|
||||
bookShort.book_pic = this.jsBase + Utils.base64Encode(bookShort.book_id)
|
||||
books.push(bookShort)
|
||||
}
|
||||
return books
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let books = [];
|
||||
for (const item of $('div.item')) {
|
||||
let bookShort = new BookShort()
|
||||
bookShort.book_id = $(item).find('a:first')[0].attribs.href;
|
||||
const img = $(item).find('img:first')[0];
|
||||
bookShort.book_name = img.attribs.alt
|
||||
bookShort.book_pic = img.attribs.src
|
||||
bookShort.book_remarks = $(item).find('span:first')[0].children[0].data.trim();
|
||||
books.push(bookShort)
|
||||
}
|
||||
return books
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($, id) {
|
||||
let bookDetail = new BookDetail()
|
||||
bookDetail.book_name = $('[property$=book_name]')[0].attribs.content
|
||||
bookDetail.book_year = $('[property$=update_time]')[0].attribs.content
|
||||
bookDetail.book_director = $('[property$=author]')[0].attribs.content
|
||||
bookDetail.book_content = $('[property$=description]')[0].attribs.content
|
||||
bookDetail.book_pic = $($("[class=\"cover\"]")).find("img")[0].attribs.src
|
||||
bookDetail.book_id = id
|
||||
if (id !== undefined) {
|
||||
$ = await this.getHtml(this.siteUrl + id + `list.html`);
|
||||
let urls = [];
|
||||
const links = $('dl>dd>a[href*="/html/"]');
|
||||
for (const l of links) {
|
||||
const name = $(l).text().trim();
|
||||
const link = l.attribs.href;
|
||||
urls.push(name + '$' + link);
|
||||
}
|
||||
bookDetail.volumes = '全卷';
|
||||
bookDetail.urls = urls.join('#');
|
||||
}
|
||||
return bookDetail
|
||||
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
for (const a of $('div.nav > ul > li > a[href!="/"]')) {
|
||||
this.classes.push({
|
||||
type_id: a.attribs.href.replace(/\//g, ''), type_name: a.children[0].data.trim(), tline: 2,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($, id)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let $ = await this.getHtml(this.siteUrl + `/${tid}/${pg}.html`);
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
try {
|
||||
let content = '';
|
||||
while (true) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
content += $('#chaptercontent')
|
||||
.html()
|
||||
.replace(/<br>|请收藏.*?<\/p>/g, '\n')
|
||||
.trim();
|
||||
id = $('a.Readpage_down')[0].attribs.href;
|
||||
if (id.indexOf('_') < 0) break;
|
||||
}
|
||||
this.playUrl = {"content":content + '\n\n'}
|
||||
} catch (e) {
|
||||
this.playUrl = {"content":""}
|
||||
}
|
||||
}
|
||||
|
||||
async search(wd, quick) {
|
||||
const cook = await req(`${this.siteUrl}/user/hm.html?q=${encodeURIComponent(wd)}`, {
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
'User-Agent': Utils.MOBILEUA,
|
||||
Referer: `${this.siteUrl}/s?q=${encodeURIComponent(wd)}`,
|
||||
},
|
||||
});
|
||||
const set_cookie = _.isArray(cook.headers['set-cookie']) ? cook.headers['set-cookie'].join(';;;') : cook.headers['set-cookie'];
|
||||
const cks = set_cookie.split(';;;');
|
||||
const cookie = {};
|
||||
for (const c of cks) {
|
||||
const tmp = c.trim();
|
||||
const idx = tmp.indexOf('=');
|
||||
const k = tmp.substr(0, idx);
|
||||
cookie[k] = tmp.substr(idx + 1, tmp.indexOf(';') - idx - 1);
|
||||
}
|
||||
const resp = await req(`${this.siteUrl}/user/search.html?q=${encodeURIComponent(wd)}&so=undefined`, {
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
'User-Agent': Utils.MOBILEUA,
|
||||
cookie: 'hm=' + cookie['hm'],
|
||||
Referer: `${this.siteUrl}/s?q=${encodeURIComponent(wd)}`,
|
||||
},
|
||||
});
|
||||
let data = JSON.parse(resp.content);
|
||||
let books = [];
|
||||
for (const book of data) {
|
||||
books.push({
|
||||
book_id: book["url_list"],
|
||||
book_name: book["articlename"],
|
||||
book_pic: book["url_img"],
|
||||
book_remarks: book["author"],
|
||||
});
|
||||
}
|
||||
return {
|
||||
tline: 2, list: books,
|
||||
};
|
||||
}
|
||||
|
||||
async proxy(segments, headers) {
|
||||
await this.jadeLog.debug(`正在设置反向代理 segments = ${segments.join(",")},headers = ${JSON.stringify(headers)}`)
|
||||
let what = segments[0];
|
||||
let url = Utils.base64Decode(segments[1]);
|
||||
if (what === 'img') {
|
||||
await this.jadeLog.debug(`反向代理ID为:${url}`)
|
||||
let $ = await this.getHtml(this.siteUrl + url)
|
||||
let bookDetail = await this.parseVodDetailFromDoc($)
|
||||
let resp;
|
||||
if (!_.isEmpty(headers)) {
|
||||
resp = await req(bookDetail.book_pic, {
|
||||
buffer: 2, headers: headers
|
||||
});
|
||||
} else {
|
||||
resp = await req(bookDetail.book_pic, {
|
||||
buffer: 2, headers: {
|
||||
Referer: url, 'User-Agent': Utils.CHROME,
|
||||
},
|
||||
});
|
||||
}
|
||||
return JSON.stringify({
|
||||
code: resp.code, buffer: 2, content: resp.content, headers: resp.headers,
|
||||
});
|
||||
}
|
||||
return JSON.stringify({
|
||||
code: 500, content: '',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new BQQSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
347
cat/tjs/js/changzhang.js
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* @File : changzhang.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/2 16:02
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {Spider} from "./spider.js";
|
||||
import {_, Crypto, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {detailContent, initAli, playContent} from "../lib/ali.js";
|
||||
|
||||
function 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
|
||||
}
|
||||
|
||||
|
||||
class ChangZhangSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.czzy88.com"
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await initAli(this.cfgObj["token"]);
|
||||
}
|
||||
|
||||
|
||||
getName() {
|
||||
return "🏭️┃厂长直连┃🏭️"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "厂长直连"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "changzhang"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async getHtml(url = this.siteUrl, headers = this.getHeader()) {
|
||||
let response = await this.fetch(url, null, headers,false,true);
|
||||
let html = response["content"]
|
||||
if (!_.isEmpty(html) && html.indexOf("人机验证")===-1) {
|
||||
return load(html)
|
||||
} else {
|
||||
await this.jadeLog.error(`html获取失败`, true)
|
||||
}
|
||||
}
|
||||
getSearchHeader() {
|
||||
return {
|
||||
"Cookie": "cf_clearance=otYZbHg1safCIxkCtZfy9DPKbf1Gs_zUskkVDc0MVKM-1707026063-1-ATOpKnTLv9+pv171YE/rzxN/nmvGN9Mucx7vpwp0kW2vZb/cbtz5e2md2/ym7EE+9dT7pPBV+kQOg9vJx2v8cks=;myannoun=1;PHPSESSID=73386nobqugs7r3pb2ljcsp5q4",
|
||||
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/100.0.4896.77 Mobile/15E148 Safari/604.1",
|
||||
"Connection":"keep-alive",
|
||||
"Host":"www.czzy55.com"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
parseVodShortFromElement($, element) {
|
||||
let vodShort = new VodShort()
|
||||
let imgElement = $($(element).find("a")).find("img")[0]
|
||||
vodShort.vod_name = imgElement.attribs.alt
|
||||
vodShort.vod_pic = imgElement.attribs["data-original"]
|
||||
vodShort.vod_remarks = $($($(element).find("[class='hdinfo']")).find("span")).text()
|
||||
vodShort.vod_id = $(element).find("a")[0].attribs.href
|
||||
return vodShort
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let aList = $($("[class=\"mi_cont\"]").find("ul")).find("li")
|
||||
for (const a of aList) {
|
||||
vod_list.push(this.parseVodShortFromElement($, a))
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let aList = $($("[class=\"mi_cont \"]").find("ul")).find("li")
|
||||
for (const a of aList) {
|
||||
vod_list.push(this.parseVodShortFromElement($, a))
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
let nodeElement = $("[class='dyxingq']")
|
||||
vodDetail.vod_pic = $(nodeElement).find("img")[0].attribs.src
|
||||
vodDetail.vod_name = $($(nodeElement).find("h1")[0]).text()
|
||||
vodDetail.vod_content = $($($("[class='yp_context']")).find("p")).text()
|
||||
let infoArray = $(nodeElement).find("[class='moviedteail_list']").find("li")
|
||||
let x = $(infoArray).text()
|
||||
for (const info of infoArray) {
|
||||
let content = $(info).text()
|
||||
if (content.indexOf("类型") > -1) {
|
||||
vodDetail.type_name = content.replaceAll("类型", "").replaceAll(":", "")
|
||||
} else if (content.indexOf("年份") > -1) {
|
||||
vodDetail.vod_year = content.replaceAll("年份", "").replaceAll(":", "")
|
||||
} else if (content.indexOf("地区") > -1) {
|
||||
vodDetail.vod_area = content.replaceAll("地区", "").replaceAll(":", "")
|
||||
} else if (content.indexOf("豆瓣") > -1) {
|
||||
vodDetail.vod_remarks = content.replaceAll("豆瓣", "").replaceAll(":", "")
|
||||
} else if (content.indexOf("主演") > -1) {
|
||||
vodDetail.vod_actor = content.replaceAll("主演", "").replaceAll(":", "")
|
||||
} else if (content.indexOf("导演") > -1) {
|
||||
vodDetail.vod_director = content.replaceAll("导演", "").replaceAll(":", "")
|
||||
} else if (content.indexOf("剧情") > -1) {
|
||||
vodDetail.vod_content = content.replaceAll("剧情", "").replaceAll(":", "")
|
||||
}
|
||||
}
|
||||
let vod_play_from_list = ["厂长资源"]
|
||||
|
||||
let vodPlayList = $("[class='paly_list_btn']")
|
||||
let vod_play_list = []
|
||||
for (const v1 of vodPlayList) {
|
||||
let vodItems = []
|
||||
let aList = $(v1).find("a")
|
||||
for (const tA of aList) {
|
||||
let episodeUrl = tA.attribs.href
|
||||
let episodeName = $(tA).text().replaceAll("立即播放 (", "").replaceAll(")", "")
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
}
|
||||
let valify_formt_list = ["磁力链接", "阿里网盘"]
|
||||
let otherPlayList = $("[class=\"ypbt_down_list\"]").find("li")
|
||||
for (const otherPlay of otherPlayList) {
|
||||
let form_name = $(otherPlay).text()
|
||||
let is_valify = false
|
||||
for (const valify_format_name of valify_formt_list) {
|
||||
if (form_name.indexOf(valify_format_name) > -1) {
|
||||
is_valify = true
|
||||
if (form_name.indexOf("阿里网盘") === -1) {
|
||||
vod_play_from_list.push(valify_format_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_valify) {
|
||||
let vodItems = []
|
||||
for (const ciliPlayUrl of $(otherPlay).find("a")) {
|
||||
let episodeUrl = ciliPlayUrl.attribs.href
|
||||
if ($(otherPlay).text().indexOf("阿里网盘")) {
|
||||
let aliVodDetail = await detailContent([episodeUrl])
|
||||
let aliPlayUrlList = aliVodDetail.vod_play_url.split("$$$")
|
||||
let is_exists = false
|
||||
for (const aliPlayUrl of aliPlayUrlList) {
|
||||
if (!_.isEmpty(aliPlayUrl)) {
|
||||
is_exists = true
|
||||
vod_play_list.push(aliPlayUrl)
|
||||
}
|
||||
}
|
||||
if (is_exists) {
|
||||
for (const aliFormatName of aliVodDetail.vod_play_from.split("$$$")) {
|
||||
vod_play_from_list.push("阿里云盘-" + aliFormatName)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
let episodeName = Utils.getStrByRegex(/\[(.*?)]/, $(ciliPlayUrl).text())
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
const items = $('div.search_list > ul > li');
|
||||
return _.map(items, (item) => {
|
||||
const img = $(item).find('img:first')[0];
|
||||
const a = $(item).find('a:first')[0];
|
||||
const hdinfo = $($(item).find('div.hdinfo')[0]).text().trim();
|
||||
const jidi = $($(item).find('div.jidi')[0]).text().trim();
|
||||
return {
|
||||
vod_id: a.attribs.href,
|
||||
vod_name: img.attribs.alt,
|
||||
vod_pic: img.attribs['data-original'],
|
||||
vod_remarks: jidi || hdinfo || '',
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
const $ = await this.getHtml(this.siteUrl + '/movie_bt');
|
||||
const series = $('div#beautiful-taxonomy-filters-tax-movie_bt_series > a[cat-url*=movie_bt_series]');
|
||||
const tags = $('div#beautiful-taxonomy-filters-tax-movie_bt_tags > a');
|
||||
let tag = {
|
||||
key: 'tag', name: '类型', value: _.map(tags, (n) => {
|
||||
let v = n.attribs['cat-url'] || '';
|
||||
v = v.substring(v.lastIndexOf('/') + 1);
|
||||
return {n: n.children[0].data, v: v};
|
||||
}),
|
||||
};
|
||||
tag['init'] = tag.value[0].v;
|
||||
let classes = _.map(series, (s) => {
|
||||
let typeId = s.attribs['cat-url'];
|
||||
typeId = typeId.substring(typeId.lastIndexOf('/') + 1);
|
||||
this.filterObj[typeId] = [tag];
|
||||
return {
|
||||
type_id: typeId, type_name: s.children[0].data,
|
||||
};
|
||||
});
|
||||
const sortName = ['电影', '电视剧', '国产剧', '美剧', '韩剧', '日剧', '海外剧(其他)', '华语电影', '印度电影', '日本电影', '欧美电影', '韩国电影', '动画', '俄罗斯电影', '加拿大电影'];
|
||||
let sort_classes = _.sortBy(classes, (c) => {
|
||||
const index = sortName.indexOf(c.type_name);
|
||||
return index === -1 ? sortName.length : index;
|
||||
});
|
||||
for (const sort_class of sort_classes){
|
||||
let type_name = sort_class["type_name"]
|
||||
if (type_name!=="会员专区" && type_name !== "站长推荐"){
|
||||
this.classes.push(sort_class)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
if (pg <= 0) pg = 1;
|
||||
const tag = extend.tag || '';
|
||||
const link = this.siteUrl + '/movie_bt' + (tag.length > 0 ? `/movie_bt_tags/${tag}` : '') + '/movie_bt_series/' + tid + (pg > 1 ? `/page/${pg}` : '');
|
||||
let $ = await this.getHtml(link)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
const $ = await this.getHtml(this.siteUrl + '/xssearch?q=' + wd,this.getSearchHeader());
|
||||
let html = $.html()
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
if (flag.indexOf("阿里云盘") > -1) {
|
||||
flag = flag.replaceAll("阿里云盘-","")
|
||||
this.playUrl = JSON.parse(await playContent(flag, id, flags))["url"];
|
||||
} else {
|
||||
if (id.indexOf("magnet") > -1) {
|
||||
this.playUrl = id
|
||||
} else {
|
||||
let $ = await this.getHtml(id)
|
||||
const iframe = $('body iframe[src*=https]');
|
||||
if (iframe.length > 0) {
|
||||
const iframeHtml = (await req(iframe[0].attribs.src, {
|
||||
headers: {
|
||||
Referer: id, 'User-Agent': Utils.CHROME,
|
||||
},
|
||||
})).content;
|
||||
let player = Utils.getStrByRegex(/var player = "(.*?)"/, iframeHtml)
|
||||
let rand = Utils.getStrByRegex(/var rand = "(.*?)"/, iframeHtml)
|
||||
let content = JSON.parse(cryptJs(player, "VFBTzdujpR9FWBhe", rand))
|
||||
this.playUrl = content["url"]
|
||||
} else {
|
||||
const js = $('script:contains(window.wp_nonce)').html();
|
||||
const group = js.match(/(var.*)eval\((\w*\(\w*\))\)/);
|
||||
const md5 = Crypto;
|
||||
const result = eval(group[1] + group[2]);
|
||||
this.playUrl = result.match(/url:.*?['"](.*?)['"]/)[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new ChangZhangSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
192
cat/tjs/js/ciliduo.js
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* @File : ciliduo.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/1 13:26
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 磁力多
|
||||
*/
|
||||
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {detailContent} from "../lib/ali.js";
|
||||
|
||||
|
||||
class CiliDuoSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://of.cilido.top"
|
||||
this.apiUrl = ""
|
||||
this.vodShortObj = {}
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🔞┃磁力多BT┃🔞"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "磁力多BT"
|
||||
}
|
||||
getJSName() {
|
||||
return "ciliduo"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
getProxy(src) {
|
||||
return Utils.base64Decode(src)
|
||||
}
|
||||
|
||||
async home(filter) {
|
||||
try {
|
||||
await this.jadeLog.info("正在解析首页类别", true)
|
||||
let $ = await this.getHtml()
|
||||
let proxy_src = Utils.getStrByRegex(/var proxy = atob\('(.*?)'\)/, $.html())
|
||||
this.apiUrl = this.getProxy(proxy_src)
|
||||
let params = `/?host=${Utils.getHost(this.siteUrl).split("://").slice(-1)[0]}&v=1`
|
||||
let homeContent = await this.fetch(this.apiUrl, params, this.getHeader())
|
||||
return await this.parseVodShortListFromDoc(load(homeContent))
|
||||
} catch (e) {
|
||||
await this.jadeLog.error(`首页解析失败,失败原因为:${e}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let rootElemet = $("[class=\"htab\"]")
|
||||
let navElements = rootElemet.find("a")
|
||||
let vodElements = $("[class=\"hotwords\"]").find("ul")
|
||||
for (let i = 0; i < navElements.length; i++) {
|
||||
let navElement = navElements[i]
|
||||
if (i !== navElements.length - 1) {
|
||||
let type_name = $(navElement).text()
|
||||
if (type_name === "热门") {
|
||||
type_name = "最近更新"
|
||||
}
|
||||
this.classes.push(this.getTypeDic(type_name, type_name))
|
||||
let vodElement = vodElements[i]
|
||||
let vod_list = []
|
||||
for (const vodShorElement of $(vodElement).find("a")) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = vodShorElement.attribs.href
|
||||
vodShort.vod_name = $(vodShorElement).html()
|
||||
vodShort.vod_pic = Utils.RESOURCEURL + "/resources/cili.jpg"
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
this.vodShortObj[type_name] = vod_list
|
||||
}
|
||||
}
|
||||
return this.result.home(this.classes, [], this.filterObj)
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"ssbox\"]")
|
||||
for (const vodElement of vodElements.slice(0, -1)) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = $(vodElement).find("a")[0].attribs.href
|
||||
vodShort.vod_name = $($(vodElement).find("a")[0]).text()
|
||||
vodShort.vod_remarks = $($(vodElement).find("span")[0]).text()
|
||||
vodShort.vod_pic = Utils.RESOURCEURL + "/resources/cili.jpg"
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let html = $.html()
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = $($("[class=\"bt_title\"]")).text()
|
||||
vodDetail.vod_pic = Utils.RESOURCEURL + "/resources/cili.jpg"
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/<br>收录时间:<span>(.*?)<\/span>/, $.html())
|
||||
vodDetail.vod_content = "下载速度:" + Utils.getStrByRegex(/下载速度:<span>(.*?)<\/span>/, $.html())
|
||||
vodDetail.vod_play_from = ["磁力连接"].join("$$$")
|
||||
let vodItems = []
|
||||
let contentElement = $("[class=\"content\"]").find("span")[0]
|
||||
let episodeUrl = contentElement.attribs.href;
|
||||
let episodeName = contentElement.attribs.title;
|
||||
vodItems.push(episodeName + "$" + episodeUrl);
|
||||
vodDetail.vod_play_url = [vodItems.join("#")].join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
this.homeVodList = this.vodShortObj["最近更新"]
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
this.vodList = this.vodShortObj[tid]
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
if (id.indexOf("search") > -1) {
|
||||
let content = await this.fetch(this.apiUrl + id, null, this.getHeader())
|
||||
let vod_list = await this.parseVodShortListFromDocBySearch(load(content))
|
||||
id = vod_list[0].vod_id
|
||||
}
|
||||
await this.jadeLog.debug(id)
|
||||
|
||||
let detailUrl = this.apiUrl + id
|
||||
let detailContent = await this.fetch(detailUrl, null, this.getHeader())
|
||||
this.vodDetail = await this.parseVodDetailFromDoc(load(detailContent))
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let searchUrl = this.apiUrl + `search?word=${wd}`
|
||||
let content = await this.fetch(searchUrl, null, this.getHeader())
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch(load(content))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new CiliDuoSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
233
cat/tjs/js/copymanga.js
Normal file
@ -0,0 +1,233 @@
|
||||
import {Spider} from "./spider.js";
|
||||
import {BookDetail, BookShort} from "../lib/book.js";
|
||||
import {Crypto} from "../lib/cat.js";
|
||||
|
||||
class CopyManhuaSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = 'https://www.copymanga.tv';
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🧑🎨|拷贝漫画|🧑🎨"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "拷贝漫画"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "copymanga"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 20
|
||||
}
|
||||
|
||||
|
||||
async setClasses() {
|
||||
this.classes.push(this.getTypeDic("全部", "c1"))
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let region = {
|
||||
key: 'region', name: '地區', init: '',
|
||||
};
|
||||
let regionValues = [];
|
||||
regionValues.push({n: '全部', v: ''});
|
||||
regionValues.push({n: '日漫', v: '0'});
|
||||
regionValues.push({n: '韓漫', v: '1'});
|
||||
regionValues.push({n: '美漫', v: '2'});
|
||||
region['value'] = regionValues;
|
||||
let ordering = {
|
||||
key: 'ordering', name: '排序', init: '-datetime_updated',
|
||||
};
|
||||
let orderingValues = [];
|
||||
orderingValues.push({n: '更新時間↓', v: '-datetime_updated'});
|
||||
orderingValues.push({n: '更新時間↑', v: 'datetime_updated'});
|
||||
orderingValues.push({n: '熱門↓', v: '-popular'});
|
||||
orderingValues.push({n: '熱門↑', v: 'popular'});
|
||||
ordering['value'] = orderingValues;
|
||||
let status = {
|
||||
key: 'sort', name: '狀態', init: '',
|
||||
};
|
||||
let statusValues = [];
|
||||
statusValues.push({n: '全部', v: ''});
|
||||
statusValues.push({n: '連載中', v: '0'});
|
||||
statusValues.push({n: '已完結', v: '1'});
|
||||
statusValues.push({n: '短篇', v: '2'});
|
||||
status['value'] = statusValues;
|
||||
let extend_list = []
|
||||
let themeValues = [{n: '全部', v: ''}];
|
||||
for (const a of $('div.classify-right>a[href*="theme="]')) {
|
||||
themeValues.push({
|
||||
n: $(a).text().trim(), v: a.attribs.href.match(/.*?theme=(.*)&/)[1],
|
||||
});
|
||||
}
|
||||
extend_list.push({
|
||||
key: 'theme', name: '', init: '', wrap: 1, value: themeValues,
|
||||
});
|
||||
extend_list.push(region);
|
||||
extend_list.push(status);
|
||||
extend_list.push(ordering);
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
let $ = await this.getHtml(this.siteUrl + '/comics');
|
||||
this.filterObj['c1'] = await this.getFilter($);
|
||||
}
|
||||
|
||||
parseVodShortFromJson(obj) {
|
||||
let bookShort = new BookShort()
|
||||
bookShort.book_id = obj["path_word"]
|
||||
bookShort.book_name = obj["name"]
|
||||
bookShort.book_pic = obj["cover"]
|
||||
bookShort.book_remarks = obj.author ? obj.author[0].name : '';
|
||||
return bookShort
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
const list = eval($('div[class="row exemptComic-box"]')[0].attribs.list);
|
||||
let books = [];
|
||||
for (const book of list) {
|
||||
let bookShort = this.parseVodShortFromJson(book)
|
||||
books.push(bookShort)
|
||||
}
|
||||
return books
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vodElements = $("[class=\"container edit\"]").find("[class=\"col-auto\"]")
|
||||
let books = []
|
||||
for (const vodElement of vodElements) {
|
||||
let bookShort = new BookShort()
|
||||
bookShort.book_id = $(vodElement).find("a")[0].attribs.href.split("/comic/")[1]
|
||||
bookShort.book_pic = $(vodElement).find("img")[0].attribs["data-src"]
|
||||
bookShort.book_name = $($(vodElement).find("p")).text()
|
||||
books.push(bookShort)
|
||||
}
|
||||
return books
|
||||
}
|
||||
|
||||
|
||||
async parseVodDetailFromDoc($, id) {
|
||||
let bookDetail = new BookDetail()
|
||||
bookDetail.book_pic = $("[class=\"comicParticulars-left-img loadingIcon\"]").find("img")[0].attribs["data-src"]
|
||||
bookDetail.book_name = $('h6').text().trim()
|
||||
bookDetail.book_director = $('span.comicParticulars-right-txt>a[href*="/author/"]')
|
||||
.map((_, a) => $(a).text().trim())
|
||||
.get()
|
||||
.join('/')
|
||||
bookDetail.book_content = $('p.intro').text().trim()
|
||||
let data = JSON.parse(await this.fetch(this.siteUrl + `/comicdetail/${id}/chapters`, null, this.getHeader()))["results"]
|
||||
let key = Crypto.enc.Utf8.parse('xxxmanga.woo.key');
|
||||
let iv = Crypto.enc.Utf8.parse(data.substr(0, 16));
|
||||
let src = Crypto.enc.Hex.parse(data.substr(16));
|
||||
let dst = Crypto.AES.decrypt({ciphertext: src}, key, {iv: iv, padding: Crypto.pad.Pkcs7});
|
||||
dst = Crypto.enc.Utf8.stringify(dst);
|
||||
const groups = JSON.parse(dst).groups;
|
||||
let urls = groups.default["chapters"]
|
||||
.map((c) => {
|
||||
return c.name + '$' + id + '|' + c.id;
|
||||
})
|
||||
.join('#');
|
||||
bookDetail.volumes = '默認';
|
||||
bookDetail.urls = urls;
|
||||
bookDetail.book_id = id
|
||||
return bookDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
const books = [];
|
||||
for (const book of obj) {
|
||||
books.push(this.parseVodShortFromJson(book))
|
||||
}
|
||||
return books
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml(this.siteUrl)
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let page = pg || 1;
|
||||
if (page === 0) page = 1;
|
||||
let link = this.siteUrl + `/comics?theme=${extend.theme || ''}®ion=${extend.region || ''}&status=${extend.status || ''}&ordering=${extend.ordering || '-datetime_updated'}`;
|
||||
if (page > 1) {
|
||||
link += '&offset=' + (page - 1) * 50 + '&limit=50';
|
||||
}
|
||||
let $ = await this.getHtml(link)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + `/comic/${id}`)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($, id)
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let info = id.split('|');
|
||||
let $ = await this.getHtml(this.siteUrl + `/comic/${info[0]}/chapter/${info[1]}`);
|
||||
const data = $('div.imageData')[0].attribs["contentkey"];
|
||||
let key = Crypto.enc.Utf8.parse('xxxmanga.woo.key');
|
||||
let iv = Crypto.enc.Utf8.parse(data.substr(0, 16));
|
||||
let src = Crypto.enc.Hex.parse(data.substr(16));
|
||||
let dst = Crypto.AES.decrypt({ciphertext: src}, key, {iv: iv, padding: Crypto.pad.Pkcs7});
|
||||
dst = Crypto.enc.Utf8.stringify(dst);
|
||||
const list = JSON.parse(dst);
|
||||
let content = [];
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
content[index] = element.url;
|
||||
}
|
||||
this.playUrl = {
|
||||
"content": content,
|
||||
}
|
||||
}
|
||||
async setSearch(wd, quick) {
|
||||
let page = 1
|
||||
const link = `${this.siteUrl}/api/kb/web/searcha/comics?offset=${page > 1 ? ((page - 1) * 12).toString() : ''}&platform=2&limit=12&q=${wd}&q_type=`;
|
||||
let list = JSON.parse(await this.fetch(link, null, this.getHeader()))["results"]["list"]
|
||||
this.vodList = await this.parseVodShortListFromJson(list)
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new CopyManhuaSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
215
cat/tjs/js/dj0898_book_open.js
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* @File : dj0898_book_open.js.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/22 17:00
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {_} from '../lib/cat.js';
|
||||
import {Spider} from "./spider.js";
|
||||
import {BookShort} from "../lib/book.js";
|
||||
|
||||
class DJ0898Spider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "http://m.dj0898.com";
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🎵┃世纪DJ音乐网┃🎵"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "世纪DJ音乐网"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "dj0898_book_open"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 10
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let books = []
|
||||
const list = $("ul.djddv_djList > li");
|
||||
for (const it of list) {
|
||||
let bookShort = new BookShort();
|
||||
const a = $(it).find("a")[1];
|
||||
bookShort.book_id = a.attribs.href
|
||||
bookShort.book_pic = $(it).find("img:first")[0].attribs.src;
|
||||
const tt = $(it).find("strong:first")[0];
|
||||
bookShort.book_name = tt.children[0].data
|
||||
bookShort.book_remarks = "🎵" + $(it).find("font")[5].children[0].data || ""
|
||||
books.push(bookShort)
|
||||
}
|
||||
return books
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
const list = $("ul.djddv_djList > li");
|
||||
let videos = _.map(list, (it) => {
|
||||
const a = $(it).find("a")[1];
|
||||
const img = $(it).find("img:first")[0];
|
||||
const tt = $(it).find("strong:first")[0];
|
||||
const remarks = $(it).find("font")[5];
|
||||
return {
|
||||
book_id: a.attribs.href,
|
||||
book_name: tt.children[0].data,
|
||||
book_pic: img.attribs["src"],
|
||||
book_remarks: "🎵" + remarks.children[0].data || "",
|
||||
};
|
||||
});
|
||||
const hasMore = $("ul.page_link > li > a:contains(\u00a0)").length > 0;
|
||||
this.page = hasMore ? parseInt(this.page) + 1 : parseInt(this.page);
|
||||
return videos
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
const list = $("ul.djddv_djList > li");
|
||||
return _.map(list, (it) => {
|
||||
const a = $(it).find("a")[1];
|
||||
const img = $(it).find("img:first")[0];
|
||||
const tt = $(it).find("strong:first")[0];
|
||||
const remarks = $(it).find("font:first")[0];
|
||||
return {
|
||||
book_id: a.attribs.href,
|
||||
book_name: tt.children[0].data,
|
||||
book_pic: img.attribs["src"],
|
||||
book_remarks: "🎵" + remarks.children[0].data || "",
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc(id) {
|
||||
const vod = {
|
||||
book_id: id,
|
||||
audio: 1,
|
||||
type_name: '',
|
||||
book_year: '',
|
||||
book_area: '',
|
||||
book_remarks: '',
|
||||
book_actor: '',
|
||||
book_director: '',
|
||||
book_content: '',
|
||||
};
|
||||
const playlist = ["点击播放" + "$" + vod.book_id];
|
||||
vod.volumes = "世纪DJ音乐网";
|
||||
vod.urls = playlist.join("#");
|
||||
return vod
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
this.classes = [{type_id: 1, type_name: "🎧串烧舞曲"}, {type_id: 2, type_name: "🎧外文舞曲"}, {
|
||||
type_id: 3,
|
||||
type_name: "🎧早场暖场"
|
||||
}, {type_id: 4, type_name: "🎧中文舞曲"}, {type_id: 5, type_name: "🎧其他舞曲"}, {
|
||||
type_id: 6,
|
||||
type_name: "🎧国外电音"
|
||||
}, {type_id: 8, type_name: "🎧慢歌连版"}, {type_id: 9, type_name: "🎧酒吧潮歌"}, {
|
||||
type_id: 10,
|
||||
type_name: "🎧中文串烧"
|
||||
}, {type_id: 11, type_name: "🎧外文串烧"}, {type_id: 12, type_name: "🎧中外串烧"}, {
|
||||
type_id: 13,
|
||||
type_name: "🎧车载串烧"
|
||||
}, {type_id: 14, type_name: "🎧越鼓串烧"}, {type_id: 40, type_name: "🎧3D/环绕"}, {
|
||||
type_id: 45,
|
||||
type_name: "🎧口水旋律"
|
||||
}, {type_id: 46, type_name: "🎧精品收藏"}, {type_id: 47, type_name: "🎧开场舞曲"}, {
|
||||
type_id: 48,
|
||||
type_name: "🎧印度舞曲"
|
||||
}, {type_id: 49, type_name: "🎧编排套曲"}, {type_id: 20, type_name: "🎧DuTch"}, {
|
||||
type_id: 21,
|
||||
type_name: "🎧Mash up"
|
||||
}, {type_id: 22, type_name: "🎧ClubHouse"}, {type_id: 23, type_name: "🎧ElectroHouse"}, {
|
||||
type_id: 24,
|
||||
type_name: "🎧越南鼓Dj"
|
||||
}, {type_id: 30, type_name: "🎧Funky"}, {type_id: 31, type_name: "🎧Reggae"}, {
|
||||
type_id: 32,
|
||||
type_name: "🎧Rnb"
|
||||
}, {type_id: 33, type_name: "🎧Hip Hop"}, {type_id: 34, type_name: "🎧Dubstep"}, {
|
||||
type_id: 8017,
|
||||
type_name: "🎧Hardstyle"
|
||||
}, {type_id: 8018, type_name: "🎧Hands Up"}];
|
||||
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml(this.siteUrl + "/dance/lists/id/10/1")
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
const link = this.siteUrl + "/dance/lists/id/" + tid + "/" + pg;
|
||||
let $ = await this.getHtml(link)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
this.vodDetail = await this.parseVodDetailFromDoc(id);
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let $ = await this.getHtml(id)
|
||||
const audio = $("body audio[src*=http]");
|
||||
this.playUrl = audio[0].attribs.src
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let $ = await this.getHtml(this.siteUrl + "/index.php/dance/so/key?key=" + wd + "&cid=0&p=1")
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new DJ0898Spider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
222
cat/tjs/js/doll.js
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* @File : doll.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/4 14:15
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : doll
|
||||
*/
|
||||
|
||||
import {Spider} from "./spider.js";
|
||||
import {Crypto, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
class Doll extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://hongkongdollvideo.com"
|
||||
}
|
||||
|
||||
|
||||
getName() {
|
||||
return "🔞┃玩偶姐姐┃🔞"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "玩偶姐姐"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "doll"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"row\"]").find("[class=\"video-detail\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = $(vodElement).find("a")[0].attribs["href"]
|
||||
let videoInfoElements = $($(vodElement).find("[class=\"video-info\"]")).find("a")
|
||||
vodShort.vod_name = videoInfoElements[0].attribs["title"]
|
||||
vodShort.vod_remarks = $(videoInfoElements[1]).text()
|
||||
vodShort.vod_pic = $(vodElement).find("img")[0].attribs["data-src"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($, key) {
|
||||
let vodDetail = new VodDetail()
|
||||
let vodElement = $("[class=\"container-fluid\"]")
|
||||
vodDetail.vod_name = $($(vodElement).find("[class=\"page-title\"]")[0]).text()
|
||||
vodDetail.vod_remarks = $(vodElement).find("[class=\"tag my-1 text-center\"]")[0].attribs["href"].replaceAll("/", "")
|
||||
vodDetail.vod_pic = $(vodElement).find("video")[0].attribs["poster"]
|
||||
let html = $.html()
|
||||
let voteTag = Utils.getStrByRegex(/var voteTag="(.*?)";/g, html)
|
||||
let videoInfo = JSON.parse(Utils.getStrByRegex(/<script type="application\/ld\+json">(.*?)<\/script>/g, html))
|
||||
//
|
||||
// try {
|
||||
// let play_url_1 = await this.fetch(videoInfo["contentUrl"], null, this.getHeader())
|
||||
// await this.jadeLog.debug(`播放链接为:${play_url_1}`)
|
||||
// } catch (e) {
|
||||
// await this.jadeLog.error(e)
|
||||
// }
|
||||
|
||||
|
||||
voteTag = Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(voteTag))
|
||||
let code = []
|
||||
for (let i = 0; i < voteTag.length; i++) {
|
||||
let k = i % key.length;
|
||||
code.push(String.fromCharCode(voteTag.charCodeAt(i) ^ key.charCodeAt(k)))
|
||||
}
|
||||
let play_url_2 = decodeURIComponent(Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(code.join(""))))
|
||||
vodDetail.vod_play_from = "玩偶姐姐"
|
||||
vodDetail.vod_play_url = "玩偶姐姐" + "$" + play_url_2
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (html !== null) {
|
||||
let $ = load(html)
|
||||
let navElements = $("[class=\"list-unstyled topnav-menu d-flex d-lg-block align-items-center justify-content-center flex-fill topnav-menu-left m-0\"]").find("li")
|
||||
let index = 1
|
||||
let class_id = index.toString()
|
||||
this.classes = []
|
||||
this.classes.push({"type_name": "首页", "type_id": "1"})
|
||||
this.filterObj[class_id] = []
|
||||
for (const navElement of navElements) {
|
||||
let type_list = $(navElement).text().split("\n")
|
||||
let valueElements = $(navElement).find("a")
|
||||
let valueList = [{"n": "全部", "v": class_id}]
|
||||
let type_id = index.toString()
|
||||
for (const valueElement of valueElements) {
|
||||
let title = $(valueElement).text().replaceAll("\n", "")
|
||||
let href = valueElement.attribs["href"]
|
||||
if (href !== undefined) {
|
||||
valueList.push({"n": title, "v": href})
|
||||
}
|
||||
}
|
||||
type_list = type_list.filter(element => element !== "");
|
||||
this.filterObj[class_id].push({"key": type_id, "name": type_list[0], "value": valueList})
|
||||
|
||||
//下面这段是为了切割使用
|
||||
// let new_value_list = []
|
||||
// for (let i = 0; i < valueList.length; i++) {
|
||||
// new_value_list.push(valueList[i])
|
||||
// if (i % 8 === 0 && i !== 0) {
|
||||
// this.filterObj[class_id].push({"key": type_id, "name": type_list[0], "value": new_value_list})
|
||||
// new_value_list = []
|
||||
// }
|
||||
// }
|
||||
// this.filterObj[class_id].push({"key": type_id, "name": type_list[0], "value": new_value_list})
|
||||
|
||||
}
|
||||
let menuElements = $("[id=\"side-menu\"]").find("li")
|
||||
for (const menuElement of menuElements) {
|
||||
let type_id = $(menuElement).find("a")[0].attribs["href"]
|
||||
if (type_id !== undefined && type_id.indexOf(this.siteUrl) > -1) {
|
||||
let type_dic = {
|
||||
"type_name": $(menuElement).text(), "type_id": type_id
|
||||
}
|
||||
this.classes.push(type_dic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
if (extend["1"] !== undefined) {
|
||||
if (extend["1"] !== "1") {
|
||||
tid = extend[1]
|
||||
}
|
||||
}
|
||||
await this.jadeLog.info(`tid = ${tid}`)
|
||||
let cateUrl = ""
|
||||
if (tid.indexOf(this.siteUrl) > -1) {
|
||||
cateUrl = tid + pg.toString() + ".html"
|
||||
} else {
|
||||
cateUrl = this.siteUrl
|
||||
}
|
||||
this.limit = 36
|
||||
let html = await this.fetch(cateUrl, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let html = await this.fetch(id, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
let key = Utils.getStrByRegex(/video\/(\w+).html/, id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($, key)
|
||||
}
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
this.playUrl = id
|
||||
this.playHeader = {}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let searchUrl = this.siteUrl + "search/" + encodeURIComponent(wd)
|
||||
let html = await this.fetch(searchUrl, null, this.getHeader())
|
||||
if (html !== null) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new Doll()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
626
cat/tjs/js/douban.js
Normal file
@ -0,0 +1,626 @@
|
||||
/*
|
||||
* @File : nivod.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/19 14:23
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {VodShort} from "../lib/vod.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {_, Crypto} from "../lib/cat.js";
|
||||
|
||||
|
||||
class DoubanSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = 'https://frodo.douban.com/api/v2'
|
||||
this.apiKey = "0ac44ae016490db2204ce0a042db2916"
|
||||
this.UserAgents = ["api-client/1 com.douban.frodo/7.22.0.beta9(231) Android/23 product/Mate 40 vendor/HUAWEI model/Mate 40 brand/HUAWEI rom/android network/wifi platform/AndroidPad", "api-client/1 com.douban.frodo/7.18.0(230) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1", "api-client/1 com.douban.frodo/7.1.0(205) Android/29 product/perseus vendor/Xiaomi model/Mi MIX 3 rom/miui6 network/wifi platform/mobile nd/1", "api-client/1 com.douban.frodo/7.3.0(207) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1"]
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
return {
|
||||
"Host": "frodo.douban.com",
|
||||
"Connection": "Keep-Alive",
|
||||
"Referer": "https://servicewechat.com/wx2f9b06c1de1ccfca/84/page-frame.html",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat"
|
||||
}
|
||||
}
|
||||
|
||||
getSearchHeader() {
|
||||
let randomNumber = Math.floor(Math.random() * this.UserAgents.length); // 生成一个介于0到9之间的随机整数
|
||||
return {
|
||||
'User-Agent': this.UserAgents[randomNumber]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🍥┃豆瓣┃🍥"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "豆瓣"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "douban"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let classes = [{
|
||||
"type_name": "热门电影", "type_id": "hot_gaia"
|
||||
}, {
|
||||
"type_name": "热播剧集", "type_id": "tv_hot"
|
||||
}, {
|
||||
"type_name": "热播综艺", "type_id": "show_hot"
|
||||
}, {
|
||||
"type_name": "电影筛选", "type_id": "movie"
|
||||
}, {
|
||||
"type_name": "电视筛选", "type_id": "tv"
|
||||
}, {
|
||||
"type_name": "电影榜单", "type_id": "rank_list_movie"
|
||||
}, {
|
||||
"type_name": "电视剧榜单", "type_id": "rank_list_tv"
|
||||
}]
|
||||
for (const class_dic of classes) {
|
||||
this.classes.push({"type_name": class_dic["type_name"], "type_id": class_dic["type_id"]})
|
||||
}
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
this.filterObj = {
|
||||
"hot_gaia": [{
|
||||
"key": "sort", "name": "排序", "value": [{
|
||||
"n": "热度", "v": "recommend"
|
||||
}, {
|
||||
"n": "最新", "v": "time"
|
||||
}, {
|
||||
"n": "评分", "v": "rank"
|
||||
}]
|
||||
}, {
|
||||
"key": "area", "name": "地区", "value": [{
|
||||
"n": "全部", "v": "全部"
|
||||
}, {
|
||||
"n": "华语", "v": "华语"
|
||||
}, {
|
||||
"n": "欧美", "v": "欧美"
|
||||
}, {
|
||||
"n": "韩国", "v": "韩国"
|
||||
}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}]
|
||||
}], "tv_hot": [{
|
||||
"key": "type", "name": "分类", "value": [{
|
||||
"n": "综合", "v": "tv_hot"
|
||||
}, {
|
||||
"n": "国产剧", "v": "tv_domestic"
|
||||
}, {
|
||||
"n": "欧美剧", "v": "tv_american"
|
||||
}, {
|
||||
"n": "日剧", "v": "tv_japanese"
|
||||
}, {
|
||||
"n": "韩剧", "v": "tv_korean"
|
||||
}, {
|
||||
"n": "动画", "v": "tv_animation"
|
||||
}]
|
||||
}], "show_hot": [{
|
||||
"key": "type", "name": "分类", "value": [{
|
||||
"n": "综合", "v": "show_hot"
|
||||
}, {
|
||||
"n": "国内", "v": "show_domestic"
|
||||
}, {
|
||||
"n": "国外", "v": "show_foreign"
|
||||
}]
|
||||
}], "movie": [{
|
||||
"key": "类型", "name": "类型", "value": [{
|
||||
"n": "全部类型", "v": ""
|
||||
}, {
|
||||
"n": "喜剧", "v": "喜剧"
|
||||
}, {
|
||||
"n": "爱情", "v": "爱情"
|
||||
}, {
|
||||
"n": "动作", "v": "动作"
|
||||
}, {
|
||||
"n": "科幻", "v": "科幻"
|
||||
}, {
|
||||
"n": "动画", "v": "动画"
|
||||
}, {
|
||||
"n": "悬疑", "v": "悬疑"
|
||||
}, {
|
||||
"n": "犯罪", "v": "犯罪"
|
||||
}, {
|
||||
"n": "惊悚", "v": "惊悚"
|
||||
}, {
|
||||
"n": "冒险", "v": "冒险"
|
||||
}, {
|
||||
"n": "音乐", "v": "音乐"
|
||||
}, {
|
||||
"n": "历史", "v": "历史"
|
||||
}, {
|
||||
"n": "奇幻", "v": "奇幻"
|
||||
}, {
|
||||
"n": "恐怖", "v": "恐怖"
|
||||
}, {
|
||||
"n": "战争", "v": "战争"
|
||||
}, {
|
||||
"n": "传记", "v": "传记"
|
||||
}, {
|
||||
"n": "歌舞", "v": "歌舞"
|
||||
}, {
|
||||
"n": "武侠", "v": "武侠"
|
||||
}, {
|
||||
"n": "情色", "v": "情色"
|
||||
}, {
|
||||
"n": "灾难", "v": "灾难"
|
||||
}, {
|
||||
"n": "西部", "v": "西部"
|
||||
}, {
|
||||
"n": "纪录片", "v": "纪录片"
|
||||
}, {
|
||||
"n": "短片", "v": "短片"
|
||||
}]
|
||||
}, {
|
||||
"key": "地区", "name": "地区", "value": [{
|
||||
"n": "全部地区", "v": ""
|
||||
}, {
|
||||
"n": "华语", "v": "华语"
|
||||
}, {
|
||||
"n": "欧美", "v": "欧美"
|
||||
}, {
|
||||
"n": "韩国", "v": "韩国"
|
||||
}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {
|
||||
"n": "中国大陆", "v": "中国大陆"
|
||||
}, {
|
||||
"n": "美国", "v": "美国"
|
||||
}, {
|
||||
"n": "中国香港", "v": "中国香港"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "中国台湾"
|
||||
}, {
|
||||
"n": "英国", "v": "英国"
|
||||
}, {
|
||||
"n": "法国", "v": "法国"
|
||||
}, {
|
||||
"n": "德国", "v": "德国"
|
||||
}, {
|
||||
"n": "意大利", "v": "意大利"
|
||||
}, {
|
||||
"n": "西班牙", "v": "西班牙"
|
||||
}, {
|
||||
"n": "印度", "v": "印度"
|
||||
}, {
|
||||
"n": "泰国", "v": "泰国"
|
||||
}, {
|
||||
"n": "俄罗斯", "v": "俄罗斯"
|
||||
}, {
|
||||
"n": "加拿大", "v": "加拿大"
|
||||
}, {
|
||||
"n": "澳大利亚", "v": "澳大利亚"
|
||||
}, {
|
||||
"n": "爱尔兰", "v": "爱尔兰"
|
||||
}, {
|
||||
"n": "瑞典", "v": "瑞典"
|
||||
}, {
|
||||
"n": "巴西", "v": "巴西"
|
||||
}, {
|
||||
"n": "丹麦", "v": "丹麦"
|
||||
}]
|
||||
}, {
|
||||
"key": "sort", "name": "排序", "value": [{
|
||||
"n": "近期热度", "v": "T"
|
||||
}, {
|
||||
"n": "首映时间", "v": "R"
|
||||
}, {
|
||||
"n": "高分优先", "v": "S"
|
||||
}]
|
||||
}, {
|
||||
"key": "年代", "name": "年代", "value": [{
|
||||
"n": "全部年代", "v": ""
|
||||
}, {
|
||||
"n": "2023", "v": "2023"
|
||||
}, {
|
||||
"n": "2022", "v": "2022"
|
||||
}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {
|
||||
"n": "2020", "v": "2020"
|
||||
}, {
|
||||
"n": "2019", "v": "2019"
|
||||
}, {
|
||||
"n": "2010年代", "v": "2010年代"
|
||||
}, {
|
||||
"n": "2000年代", "v": "2000年代"
|
||||
}, {
|
||||
"n": "90年代", "v": "90年代"
|
||||
}, {
|
||||
"n": "80年代", "v": "80年代"
|
||||
}, {
|
||||
"n": "70年代", "v": "70年代"
|
||||
}, {
|
||||
"n": "60年代", "v": "60年代"
|
||||
}, {
|
||||
"n": "更早", "v": "更早"
|
||||
}]
|
||||
}], "tv": [{
|
||||
"key": "类型", "name": "类型", "value": [{
|
||||
"n": "不限", "v": ""
|
||||
}, {
|
||||
"n": "电视剧", "v": "电视剧"
|
||||
}, {
|
||||
"n": "综艺", "v": "综艺"
|
||||
}]
|
||||
}, {
|
||||
"key": "电视剧形式", "name": "电视剧形式", "value": [{
|
||||
"n": "不限", "v": ""
|
||||
}, {
|
||||
"n": "喜剧", "v": "喜剧"
|
||||
}, {
|
||||
"n": "爱情", "v": "爱情"
|
||||
}, {
|
||||
"n": "悬疑", "v": "悬疑"
|
||||
}, {
|
||||
"n": "动画", "v": "动画"
|
||||
}, {
|
||||
"n": "武侠", "v": "武侠"
|
||||
}, {
|
||||
"n": "古装", "v": "古装"
|
||||
}, {
|
||||
"n": "家庭", "v": "家庭"
|
||||
}, {
|
||||
"n": "犯罪", "v": "犯罪"
|
||||
}, {
|
||||
"n": "科幻", "v": "科幻"
|
||||
}, {
|
||||
"n": "恐怖", "v": "恐怖"
|
||||
}, {
|
||||
"n": "历史", "v": "历史"
|
||||
}, {
|
||||
"n": "战争", "v": "战争"
|
||||
}, {
|
||||
"n": "动作", "v": "动作"
|
||||
}, {
|
||||
"n": "冒险", "v": "冒险"
|
||||
}, {
|
||||
"n": "传记", "v": "传记"
|
||||
}, {
|
||||
"n": "剧情", "v": "剧情"
|
||||
}, {
|
||||
"n": "奇幻", "v": "奇幻"
|
||||
}, {
|
||||
"n": "惊悚", "v": "惊悚"
|
||||
}, {
|
||||
"n": "灾难", "v": "灾难"
|
||||
}, {
|
||||
"n": "歌舞", "v": "歌舞"
|
||||
}, {
|
||||
"n": "音乐", "v": "音乐"
|
||||
}]
|
||||
}, {
|
||||
"key": "综艺形式", "name": "综艺形式", "value": [{
|
||||
"n": "不限", "v": ""
|
||||
}, {
|
||||
"n": "真人秀", "v": "真人秀"
|
||||
}, {
|
||||
"n": "脱口秀", "v": "脱口秀"
|
||||
}, {
|
||||
"n": "音乐", "v": "音乐"
|
||||
}, {
|
||||
"n": "歌舞", "v": "歌舞"
|
||||
}]
|
||||
}, {
|
||||
"key": "地区", "name": "地区", "value": [{
|
||||
"n": "全部地区", "v": ""
|
||||
}, {
|
||||
"n": "华语", "v": "华语"
|
||||
}, {
|
||||
"n": "欧美", "v": "欧美"
|
||||
}, {
|
||||
"n": "国外", "v": "国外"
|
||||
}, {
|
||||
"n": "韩国", "v": "韩国"
|
||||
}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {
|
||||
"n": "中国大陆", "v": "中国大陆"
|
||||
}, {
|
||||
"n": "中国香港", "v": "中国香港"
|
||||
}, {
|
||||
"n": "美国", "v": "美国"
|
||||
}, {
|
||||
"n": "英国", "v": "英国"
|
||||
}, {
|
||||
"n": "泰国", "v": "泰国"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "中国台湾"
|
||||
}, {
|
||||
"n": "意大利", "v": "意大利"
|
||||
}, {
|
||||
"n": "法国", "v": "法国"
|
||||
}, {
|
||||
"n": "德国", "v": "德国"
|
||||
}, {
|
||||
"n": "西班牙", "v": "西班牙"
|
||||
}, {
|
||||
"n": "俄罗斯", "v": "俄罗斯"
|
||||
}, {
|
||||
"n": "瑞典", "v": "瑞典"
|
||||
}, {
|
||||
"n": "巴西", "v": "巴西"
|
||||
}, {
|
||||
"n": "丹麦", "v": "丹麦"
|
||||
}, {
|
||||
"n": "印度", "v": "印度"
|
||||
}, {
|
||||
"n": "加拿大", "v": "加拿大"
|
||||
}, {
|
||||
"n": "爱尔兰", "v": "爱尔兰"
|
||||
}, {
|
||||
"n": "澳大利亚", "v": "澳大利亚"
|
||||
}]
|
||||
}, {
|
||||
"key": "sort", "name": "排序", "value": [{
|
||||
"n": "近期热度", "v": "T"
|
||||
}, {
|
||||
"n": "首播时间", "v": "R"
|
||||
}, {
|
||||
"n": "高分优先", "v": "S"
|
||||
}]
|
||||
}, {
|
||||
"key": "年代", "name": "年代", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "2023", "v": "2023"
|
||||
}, {
|
||||
"n": "2022", "v": "2022"
|
||||
}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {
|
||||
"n": "2020", "v": "2020"
|
||||
}, {
|
||||
"n": "2019", "v": "2019"
|
||||
}, {
|
||||
"n": "2010年代", "v": "2010年代"
|
||||
}, {
|
||||
"n": "2000年代", "v": "2000年代"
|
||||
}, {
|
||||
"n": "90年代", "v": "90年代"
|
||||
}, {
|
||||
"n": "80年代", "v": "80年代"
|
||||
}, {
|
||||
"n": "70年代", "v": "70年代"
|
||||
}, {
|
||||
"n": "60年代", "v": "60年代"
|
||||
}, {
|
||||
"n": "更早", "v": "更早"
|
||||
}]
|
||||
}, {
|
||||
"key": "平台", "name": "平台", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "腾讯视频", "v": "腾讯视频"
|
||||
}, {
|
||||
"n": "爱奇艺", "v": "爱奇艺"
|
||||
}, {
|
||||
"n": "优酷", "v": "优酷"
|
||||
}, {
|
||||
"n": "湖南卫视", "v": "湖南卫视"
|
||||
}, {
|
||||
"n": "Netflix", "v": "Netflix"
|
||||
}, {
|
||||
"n": "HBO", "v": "HBO"
|
||||
}, {
|
||||
"n": "BBC", "v": "BBC"
|
||||
}, {
|
||||
"n": "NHK", "v": "NHK"
|
||||
}, {
|
||||
"n": "CBS", "v": "CBS"
|
||||
}, {
|
||||
"n": "NBC", "v": "NBC"
|
||||
}, {
|
||||
"n": "tvN", "v": "tvN"
|
||||
}]
|
||||
}], "rank_list_movie": [{
|
||||
"key": "榜单", "name": "榜单", "value": [{
|
||||
"n": "实时热门电影", "v": "movie_real_time_hotest"
|
||||
}, {
|
||||
"n": "一周口碑电影榜", "v": "movie_weekly_best"
|
||||
}, {
|
||||
"n": "豆瓣电影Top250", "v": "movie_top250"
|
||||
}]
|
||||
}], "rank_list_tv": [{
|
||||
"key": "榜单", "name": "榜单", "value": [{
|
||||
"n": "实时热门电视", "v": "tv_real_time_hotest"
|
||||
}, {
|
||||
"n": "华语口碑剧集榜", "v": "tv_chinese_best_weekly"
|
||||
}, {
|
||||
"n": "全球口碑剧集榜", "v": "tv_global_best_weekly"
|
||||
}, {
|
||||
"n": "国内口碑综艺榜", "v": "show_chinese_best_weekly"
|
||||
}, {
|
||||
"n": "国外口碑综艺榜", "v": "show_global_best_weekly"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const item of obj) {
|
||||
let vod_short = new VodShort()
|
||||
vod_short.vod_id = "msearch:" + item["id"]
|
||||
if (item["title"] === undefined) {
|
||||
vod_short.vod_name = item["target"]["title"]
|
||||
} else {
|
||||
vod_short.vod_name = item["title"]
|
||||
}
|
||||
if (item["pic"] === undefined) {
|
||||
vod_short.vod_pic = item["target"]["cover_url"]
|
||||
} else {
|
||||
vod_short.vod_pic = item["pic"]["normal"]
|
||||
}
|
||||
if (item["rating"] === undefined) {
|
||||
vod_short.vod_remarks = "评分:" + item["target"]["rating"]["value"].toString()
|
||||
} else {
|
||||
vod_short.vod_remarks = "评分:" + item["rating"]["value"].toString()
|
||||
}
|
||||
vod_list.push(vod_short);
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
get_tags(extend) {
|
||||
let tag_list = []
|
||||
for (const key of Object.keys(extend)) {
|
||||
if (key !== "sort") {
|
||||
tag_list.push(extend[key])
|
||||
}
|
||||
}
|
||||
return tag_list.join(",")
|
||||
}
|
||||
|
||||
sign(url, ts, method = 'GET') {
|
||||
let _api_secret_key = "bf7dddc7c9cfe6f7"
|
||||
let url_path = "%2F" + url.split("/").slice(3).join("%2F")
|
||||
let raw_sign = [method.toLocaleUpperCase(), url_path, ts.toString()].join("&")
|
||||
return Crypto.HmacSHA1(raw_sign, _api_secret_key).toString(Crypto.enc.Base64)
|
||||
}
|
||||
async setHomeVod() {
|
||||
let url = this.siteUrl + "/subject_collection/subject_real_time_hotest/items"
|
||||
let content = await this.fetch(url, {"apikey": this.apiKey}, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
let items = content_json["subject_collection_items"]
|
||||
this.homeVodList = await this.parseVodShortListFromJson(items)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let sort = extend["sort"] ?? "show_hot";
|
||||
let tag_str = this.get_tags(extend)
|
||||
this.count = 0
|
||||
this.limit = 20;
|
||||
this.total = 0;
|
||||
let start = 0
|
||||
if (parseInt(pg) > 1){
|
||||
start = (parseInt(pg) - 1) * this.limit
|
||||
}
|
||||
let cateUrl = ""
|
||||
let params = {"start": start.toString(), "count": this.limit.toString()}
|
||||
let itemKey = "items"
|
||||
switch (tid) {
|
||||
case "hot_gaia":
|
||||
sort = extend["sort"] ?? "recommend"
|
||||
let area = extend["area"] ?? "全部"
|
||||
params["sort"] = sort
|
||||
params["area"] = area
|
||||
cateUrl = "/movie/hot_gaia"
|
||||
break
|
||||
case "tv_hot":
|
||||
let type = extend["type"] ?? "tv_hot"
|
||||
cateUrl = "/subject_collection/" + type + "/items"
|
||||
itemKey = "subject_collection_items"
|
||||
break
|
||||
case "show_hot":
|
||||
let showType = extend["type"] ?? "show_hot"
|
||||
cateUrl = "/subject_collection/" + showType + "/items"
|
||||
itemKey = "subject_collection_items";
|
||||
break
|
||||
case "movie":
|
||||
params["sort"] = sort
|
||||
params["tags"] = tag_str
|
||||
cateUrl = "/movie/recommend"
|
||||
break
|
||||
case "tv":
|
||||
params["sort"] = sort
|
||||
params["tags"] = tag_str
|
||||
cateUrl = "/tv/recommend"
|
||||
break
|
||||
case "rank_list_movie":
|
||||
let rankMovieType = extend["榜单"] ?? "movie_real_time_hotest"
|
||||
cateUrl = "/subject_collection/" + rankMovieType + "/items"
|
||||
itemKey = "subject_collection_items"
|
||||
break
|
||||
case "rank_list_tv":
|
||||
let rankTVType = extend["榜单"] ?? "tv_real_time_hotest"
|
||||
cateUrl = "/subject_collection/" + rankTVType + "/items"
|
||||
itemKey = "subject_collection_items"
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
params["apikey"] = this.apiKey
|
||||
let content = await this.fetch(this.siteUrl + cateUrl, params, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
let items = content_json[itemKey]
|
||||
this.vodList = await this.parseVodShortListFromJson(items)
|
||||
}
|
||||
}
|
||||
async setSearch(wd, quick) {
|
||||
let _api_url = "https://frodo.douban.com/api/v2"
|
||||
let _api_key = "0dad551ec0f84ed02907ff5c42e8ec70"
|
||||
let url = _api_url + "/search/movie"
|
||||
let date = new Date()
|
||||
let ts = date.getFullYear().toString() + (date.getMonth() + 1).toString() + date.getDate().toString()
|
||||
let params = {
|
||||
'_sig': this.sign(url, ts),
|
||||
'_ts': ts,
|
||||
'apiKey': _api_key,
|
||||
'count': 20,
|
||||
'os_rom': 'android',
|
||||
'q': encodeURIComponent(wd),
|
||||
'start': 0
|
||||
}
|
||||
let content = await this.fetch(url, params, this.getSearchHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
this.vodList = await this.parseVodShortListFromJson(content_json["items"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new DoubanSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
280
cat/tjs/js/dygangs.js
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* @File : dygangs.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/21 15:06
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 电影港
|
||||
*/
|
||||
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {_, load} from "../lib/cat.js";
|
||||
|
||||
|
||||
class MoviePortSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.dygangs.xyz"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "电影港"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🏖️┃电影港┃🏖️"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "dygangs"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let navElements = $($("[class=\"top-nav\"]")[0]).find("a")
|
||||
for (const navElement of navElements) {
|
||||
let type_id = navElement.attribs.href.replaceAll(this.siteUrl, "")
|
||||
let type_name = $(navElement).text()
|
||||
if (type_id !== "/") {
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async getFilter($, index) {
|
||||
let element = $("[class=\"nav-down-2 clearfix\"]")[index]
|
||||
let extend_list = []
|
||||
if (element !== undefined) {
|
||||
let name = "按类型"
|
||||
let extend_dic = {"key": name, "name": name, "value": []}
|
||||
extend_dic["name"] = name
|
||||
extend_dic["value"].push({"n": "全部", "v": "0"})
|
||||
for (const ele of $(element).find("a")) {
|
||||
let type_name = $(ele).html()
|
||||
let type_id = ele.attribs.href.split("/").slice(-2)[0]
|
||||
extend_dic["value"].push({"n": type_name, "v": type_id})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
let index = 0
|
||||
for (const type_dic of this.classes.slice(1, 5)) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "最近更新") {
|
||||
let url = this.siteUrl + `${type_id}`
|
||||
let $ = await this.getHtml(url)
|
||||
this.filterObj[type_id] = await this.getFilter($, index)
|
||||
}
|
||||
index = index + 1
|
||||
}
|
||||
}
|
||||
|
||||
parseVodShortFromElement($, element) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = element.attribs.href
|
||||
vodShort.vod_name = element.attribs.title
|
||||
vodShort.vod_pic = $(element).find("img")[0].attribs["data-original"]
|
||||
vodShort.vod_remarks = $($(element).find("i")[0]).text().replaceAll(" ", "").replaceAll("\n", "")
|
||||
if (_.isEmpty(vodShort.vod_pic)){
|
||||
vodShort.vod_pic = Utils.RESOURCEURL + "/resources/dygang.jpg"
|
||||
}
|
||||
return vodShort
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"index-tj-l\"]").find("li")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShortElement = $(vodElement).find("a")[0]
|
||||
let vodShort = this.parseVodShortFromElement($, vodShortElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"index-area clearfix\"]").find("li")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShortElement = $(vodElement).find("a")[0]
|
||||
let vodShort = this.parseVodShortFromElement($, vodShortElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail();
|
||||
let vodDetailElement = $("[ class=\"ct mb clearfix\"]")
|
||||
vodDetail.vod_pic = $(vodDetailElement).find("img")[0].attribs["src"]
|
||||
vodDetail.vod_name = Utils.getStrByRegex(/◎片 名 (.*?)<br>/, $(vodDetailElement).html())
|
||||
vodDetail.vod_area = Utils.getStrByRegex(/◎产 地 (.*?)<br>/, $(vodDetailElement).html())
|
||||
vodDetail.vod_year = Utils.getStrByRegex(/◎年 代 (.*?)<br>/, $(vodDetailElement).html())
|
||||
vodDetail.type_name = Utils.getStrByRegex(/◎类 别 (.*?)<br>/, $(vodDetailElement).html())
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/◎集 数 (.*?)<br>/, $(vodDetailElement).html())
|
||||
let content = Utils.getStrByRegex(/◎主 演 (.*?)<\/p>/s, $(vodDetailElement).html())
|
||||
if (_.isEmpty(content)) {
|
||||
content = Utils.getStrByRegex(/◎演 员 (.*?)<\/p>/s, $(vodDetailElement).html())
|
||||
}
|
||||
let actor_list = []
|
||||
for (const actor of content.split("\n")) {
|
||||
actor_list.push(actor.replaceAll(" ", "").replaceAll("<br>", "").replaceAll(" ", ""))
|
||||
}
|
||||
vodDetail.vod_actor = actor_list.join("/")
|
||||
vodDetail.vod_director = Utils.getStrByRegex(/◎导 演 (.*?)<br>/, $(vodDetailElement).html())
|
||||
vodDetail.vod_content = Utils.getStrByRegex(/◎简 介<\/p>(.*?)<br>/s, $(vodDetailElement).html()).replaceAll("<p>", "").replaceAll("\n", "")
|
||||
if (_.isEmpty(vodDetail.vod_content)) {
|
||||
vodDetail.vod_content = Utils.getStrByRegex(/◎简 介<br>(.*?)<\/p>/s, $(vodDetailElement).html()).replaceAll("<p>", "").replaceAll("\n", "")
|
||||
|
||||
}
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
|
||||
|
||||
let playFormatElements = $("[class=\"playfrom tab8 clearfix\"]")
|
||||
let playUrlElements = $("[class=\"videourl clearfix\"]")
|
||||
for (let i = 0; i < playFormatElements.length; i++) {
|
||||
let playFormatElement = playFormatElements[i]
|
||||
let format_name = $($(playFormatElement).find("li")).html()
|
||||
vod_play_from_list.push(format_name.replaceAll("<i class=\"playerico ico-Azhan\"></i> ", ""))
|
||||
let vodItems = []
|
||||
for (const playUrlElement of $(playUrlElements[i]).find("a")) {
|
||||
let episodeName = playUrlElement.attribs.title
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
|
||||
}
|
||||
let playFormatElement = $($(vodDetailElement).find("span")[0]).find("span")
|
||||
if (playFormatElement.length > 0) {
|
||||
let format_name = $(playFormatElement).html()
|
||||
vod_play_from_list.push(Utils.getStrByRegex(/【(.*?)】/, format_name.replaceAll("下载地址", "磁力链接")))
|
||||
let vodItems = []
|
||||
for (const playUrlElement of $($($(vodDetailElement).find("tbody")).find("tr")).find("a")) {
|
||||
let episodeName = $(playUrlElement).html().replaceAll(".mp4", "")
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
|
||||
}
|
||||
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let url = this.siteUrl + tid
|
||||
if (extend["按类型"] !== undefined && extend["按类型"] !== "0") {
|
||||
url = url + `${extend["按类型"]}/`
|
||||
}
|
||||
if (parseInt(pg) > 1) {
|
||||
url = url + `index_${pg}.html`
|
||||
}
|
||||
let $ = await this.getHtml(url)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = this.siteUrl + "/e/search/index.php"
|
||||
let params = {"keyboard": wd, "submit": "搜 索", "show": "title,zhuyan", "tempid": "1"}
|
||||
let resp = await this.post(url, params, this.getHeader())
|
||||
let $ = load(resp)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
if (id.indexOf("http") > -1) {
|
||||
let $ = await this.getHtml(id)
|
||||
let url = Utils.getStrByRegex(/url: '(.*?)',/, $.html())
|
||||
if (_.isEmpty(url)) {
|
||||
let videoUrl = $($("[class=\"video\"]")[0]).find("iframe")[0].attribs["src"]
|
||||
let html = await this.fetch(videoUrl, null, {"User-Agent": Utils.CHROME})
|
||||
this.playUrl = Utils.getStrByRegex(/url: '(.*?)',/, html)
|
||||
if (_.isEmpty(this.playUrl)){
|
||||
let urlHost = Utils.getHost(videoUrl)
|
||||
this.playUrl = urlHost + Utils.getStrByRegex(/var main = "(.*?)";/, html)
|
||||
}
|
||||
} else {
|
||||
this.playUrl = url
|
||||
}
|
||||
} else {
|
||||
this.playUrl = id
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new MoviePortSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
214
cat/tjs/js/dyttbt.js
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* @File : freeok.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/19 10:26
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 电影天堂(已失效)
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
|
||||
class DyttSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.dy2018.com"
|
||||
this.dyttReconnectTimes = 0
|
||||
|
||||
}
|
||||
|
||||
getName() {
|
||||
return `👼┃电影天堂BT┃👼`
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "电影天堂BT"
|
||||
}
|
||||
getJSName() {
|
||||
return "dyttbt"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
// getHeader() {
|
||||
// return {"User-Agent": "PostmanRuntime/7.36.1", "Host":"www.dy2018.com"};
|
||||
// }
|
||||
|
||||
async getHtml(url = this.siteUrl, headers = this.getHeader()) {
|
||||
try {
|
||||
let buffer = await this.fetch(url, null, headers, false, false, 1)
|
||||
let html = Utils.decode(buffer, "gb2312")
|
||||
if (!_.isEmpty(html) && Utils.getStrByRegex(/<script src="(.*?)"><\/script>/, html) !== "/_guard/auto.js") {
|
||||
return load(html)
|
||||
} else {
|
||||
if (this.dyttReconnectTimes < this.maxReconnectTimes) {
|
||||
Utils.sleep(2)
|
||||
this.dyttReconnectTimes = this.dyttReconnectTimes + 1
|
||||
return await this.getHtml(url, headers)
|
||||
} else {
|
||||
await this.jadeLog.error(`html获取失败`, true)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
await this.jadeLog.error(`获取html出错,出错原因为${e}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
async setFilterObj() {
|
||||
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let vodShortElements = $("[class=\"title_all\"]")
|
||||
for (const vodShortElement of vodShortElements) {
|
||||
let spanElement = $(vodShortElement).find("span")[0]
|
||||
let aElement = $(vodShortElement).find("a")[0]
|
||||
let type_name = $(spanElement).text()
|
||||
let type_id = aElement.attribs["href"]
|
||||
if (type_id.indexOf("https:") === -1 && type_id.indexOf("http:") === -1) {
|
||||
type_id = this.siteUrl + type_id
|
||||
}
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
let containElements = $($("[id=\"menu\"]").find("[class=\"contain\"]")).find("a").slice(0, -3)
|
||||
for (const contaElement of containElements) {
|
||||
let type_name = $(contaElement).text()
|
||||
let type_id = contaElement.attribs["href"]
|
||||
if (type_id.indexOf("https:") === -1 && type_id.indexOf("http:") === -1) {
|
||||
type_id = this.siteUrl + type_id
|
||||
}
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let vodShortElements = $($("[class=\"co_content8\"]")[0]).find("tbody")
|
||||
for (const vodShortElement of vodShortElements) {
|
||||
let vodShort = new VodShort()
|
||||
let vodElements = $(vodShortElement).find("tr")
|
||||
vodShort.vod_name = Utils.getStrByRegex(/《(.*?)》/, $(vodElements[1]).text())
|
||||
vodShort.vod_id = $(vodElements[1]).find("a").slice(-1)[0].attribs.href
|
||||
vodShort.vod_remarks = "日期:" + Utils.getStrByRegex(/日期:(.*?) /, $(vodElements[2]).text()) + " 热度:" + Utils.getStrByRegex(/点击:(.*?) /, $(vodElements[2]).text())
|
||||
vodShort.vod_pic = Utils.RESOURCEURL + "/resources/dytt.jpg"
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodShortElements = $($("[class=\"co_area2\"]")[0]).find("li").slice(1)
|
||||
for (const vodShortElement of vodShortElements) {
|
||||
let vodShort = new VodShort()
|
||||
let vodElement = $(vodShortElement).find("a")[0]
|
||||
vodShort.vod_id = vodElement.attribs["href"]
|
||||
vodShort.vod_name = Utils.getStrByRegex(/《(.*?)》/, vodElement.attribs["title"])
|
||||
vodShort.vod_remarks = $($(vodShortElement).find("span")).text().replaceAll("", "")
|
||||
vodShort.vod_pic = Utils.RESOURCEURL + "/resources/dytt.jpg"
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = Utils.getStrByRegex(/《(.*?)》/, Utils.getStrByRegex(/<title>(.*?)<\/title>/, $.html()))
|
||||
let zoomElement = $("[id=\"Zoom\"]")
|
||||
vodDetail.vod_pic = $(zoomElement).find("img")[0].attribs.src
|
||||
let content = $(zoomElement).text()
|
||||
vodDetail.vod_year = Utils.getStrByRegex(/年 代 (.*?)◎/, content)
|
||||
vodDetail.type_name = Utils.getStrByRegex(/类 别 (.*?)◎/, content)
|
||||
vodDetail.vod_area = Utils.getStrByRegex(/产 地 (.*?)◎/, content)
|
||||
vodDetail.vod_director = Utils.getStrByRegex(/导 演 (.*?)◎/, content)
|
||||
vodDetail.vod_content = Utils.getStrByRegex(/简 介 (.*?)◎/, content)
|
||||
vodDetail.vod_actor = Utils.getStrByRegex(/主 演(.*?)◎/, content).replaceAll(" ", "*")
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/豆瓣评分 (.*?)\//, content)
|
||||
vodDetail.vod_play_from = "磁力链接"
|
||||
let playList = $(zoomElement).find("[class=\"player_list\"]").find("a")
|
||||
let vodItems = []
|
||||
for (const playEle of playList) {
|
||||
vodItems.push($(playEle).text() + "$" + playEle.attribs.href);
|
||||
}
|
||||
vodDetail.vod_play_url = [vodItems.join("#")].join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let cateUrl = tid
|
||||
if (tid.indexOf("index") > -1){
|
||||
cateUrl = tid.split(".html")[0] + "_" + pg + ".html"
|
||||
}
|
||||
let $ = await this.getHtml(cateUrl)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
let spider = new DyttSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
96
cat/tjs/js/feifan.js
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* @File : feifan.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/02/06 14:58
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 非凡资源
|
||||
*/
|
||||
|
||||
import {VodSpider} from "./vodSpider.js";
|
||||
|
||||
class FeiFanSpider extends VodSpider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "http://cj.ffzyapi.com"
|
||||
this.remove18 = true
|
||||
this.type_id_18 = 34
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "非凡资源"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🥗┃非凡资源┃🥗"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "feifan"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async spiderInit(inReq) {
|
||||
await super.spiderInit(inReq);
|
||||
}
|
||||
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new FeiFanSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
96
cat/tjs/js/feifan18.js
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* @File : feifan.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/02/06 14:58
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 非凡资源
|
||||
*/
|
||||
|
||||
import {VodSpider} from "./vodSpider.js";
|
||||
|
||||
class FeiFan18Spider extends VodSpider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "http://cj.ffzyapi.com"
|
||||
this.remove18 = false
|
||||
this.type_id_18 = 34
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "非凡资源18+"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🔞┃非凡资源18+┃🔞"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "feifan18"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async spiderInit(inReq) {
|
||||
await super.spiderInit(inReq);
|
||||
}
|
||||
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new FeiFan18Spider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
333
cat/tjs/js/freeok.js
Normal file
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* @File : freeok.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/19 10:26
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : OK资源网 已失效
|
||||
*/
|
||||
import {_} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {player} from "../lib/freeok_setttings.js";
|
||||
const OCR_API = 'https://api.nn.ci/ocr/b64/text';//ocr在线识别接口
|
||||
|
||||
|
||||
class OkSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.freeok.vip"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return `🆗┃OK资源网┃🆗`
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return `OK资源网`
|
||||
}
|
||||
getJSName() {
|
||||
return "freeok"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
async init(cfg) {
|
||||
this.cookie = await this.load_cache()
|
||||
await super.init(cfg);
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $($("[class=\"module\"]")).find("a").slice(0, 12)
|
||||
for (const vodElement of vodElements) {
|
||||
vod_list.push(this.parseVodShortFromElement($, vodElement))
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
parseVodShortFromElement($, element) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_name = element.attribs["title"]
|
||||
vodShort.vod_id = element.attribs["href"]
|
||||
vodShort.vod_remarks = $($(element).find("[class=\"module-item-note\"]")).text()
|
||||
vodShort.vod_pic = $(element).find("[class=\"lazy lazyload\"]")[0].attribs["data-original"]
|
||||
return vodShort
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let itemElements = $($("[class=\"module-items module-poster-items-base \"]")).find("a")
|
||||
for (const itemElement of itemElements) {
|
||||
vod_list.push(this.parseVodShortFromElement($, itemElement))
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const result of obj["Data"]["result"]){
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = result["vod_url"].replaceAll(this.siteUrl,"")
|
||||
vodShort.vod_pic = result["vod_pic"]
|
||||
vodShort.vod_name = result["vod_name"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByHot($) {
|
||||
let vod_list = []
|
||||
let itemElement = $($("[class=\"module-items module-card-items\"]"))[0]
|
||||
let titleElements = $(itemElement).find("[class=\"module-card-item-title\"]").find("a")
|
||||
let infoElements = $(itemElement).find("[class=\"module-card-item-info\"]")
|
||||
let picElements = $(itemElement).find("[class=\"module-item-pic\"]").find("img")
|
||||
|
||||
for (let i = 0; i < titleElements.length; i++) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = titleElements[i].attribs["href"]
|
||||
vodShort.vod_name = $(titleElements[i]).text()
|
||||
vodShort.vod_pic = picElements[i].attribs["data-original"]
|
||||
vodShort.vod_remarks = $($(infoElements[i])).text().split("\n")[5]
|
||||
vod_list.push(vodShort)
|
||||
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail();
|
||||
let moudleElements = $("[class=\"module-info-tag-link\"]").find("a")
|
||||
let mobilePlay = $("[class=\"module-mobile-play\"]").find("a")[0]
|
||||
vodDetail.vod_name = mobilePlay.attribs["title"].replaceAll("立刻播放", "")
|
||||
vodDetail.vod_content = $($("[class=\"module-info-introduction-content\"]")).text().replaceAll("\n", "").replaceAll(" ", "")
|
||||
let type_list = []
|
||||
for (const moudleElement of moudleElements.slice(2)) {
|
||||
type_list.push($(moudleElement).text())
|
||||
}
|
||||
vodDetail.type_name = type_list.join("/")
|
||||
vodDetail.vod_year = $(moudleElements[0]).text()
|
||||
vodDetail.vod_area = $(moudleElements[1]).text()
|
||||
let itemElements = $("[class=\"module-info-item\"]")
|
||||
let itemText = ""
|
||||
for (const itemElement of itemElements) {
|
||||
itemText = itemText + $(itemElement).text().replaceAll("\n", "").replaceAll(":", ":") + "\n"
|
||||
}
|
||||
vodDetail.vod_pic = $("[class=\"module-item-pic\"]").find("img")[0].attribs["data-original"]
|
||||
vodDetail.vod_director = Utils.getStrByRegex(/导演:(.*?)\n/, itemText)
|
||||
vodDetail.vod_actor = Utils.getStrByRegex(/主演:(.*?)\n/, itemText)
|
||||
vodDetail.vod_year = Utils.getStrByRegex(/上映:(.*?)\n/, itemText)
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/备注:(.*?)\n/, itemText)
|
||||
if (_.isEmpty(vodDetail.vod_remarks)) {
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/集数:(.*?)\n/, itemText)
|
||||
}
|
||||
let playElements = $($("[class=\"module-tab-items-box hisSwiper\"]")).find("span")
|
||||
let play_from_list = []
|
||||
let playUrlElements = $("[class=\"module-list sort-list tab-list his-tab-list\"]")
|
||||
let play_url_list = []
|
||||
for (let i = 0; i < playElements.length; i++) {
|
||||
let text = $(playElements[i]).text()
|
||||
if (text.indexOf("夸克") === -1) {
|
||||
let playDetailElements = $(playUrlElements[i]).find("a")
|
||||
let vodItems = []
|
||||
for (const playDetailElement of playDetailElements) {
|
||||
let play_name = playDetailElement.attribs["title"].replaceAll("播放", "").replaceAll(vodDetail.vod_name, "")
|
||||
let play_url = playDetailElement.attribs["href"]
|
||||
vodItems.push(`${play_name}$${play_url}`)
|
||||
}
|
||||
play_url_list.push(vodItems.join("#"))
|
||||
play_from_list.push($(playElements[i]).text())
|
||||
}
|
||||
}
|
||||
vodDetail.vod_play_from = play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = play_url_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml(this.siteUrl, this.getHeader())
|
||||
let navElements = $($("[class=\"navbar-items swiper-wrapper\"]")).find("a")
|
||||
let type_name = $(navElements.slice(0, 8).slice(-1)[0]).text().replaceAll("\n", "")
|
||||
let type_id = navElements.slice(0, 8).slice(-1)[0].attribs["href"]
|
||||
this.classes.push({"type_name": type_name, "type_id": type_id})
|
||||
for (const navElement of navElements.slice(0, 8)) {
|
||||
let type_name = $(navElement).text().replaceAll("\n", "")
|
||||
if (type_name !== "首页" && type_name !== "热榜") {
|
||||
let type_id = navElement.attribs["href"].split("/").slice(-1)[0].split(".")[0]
|
||||
this.classes.push({"type_name": type_name, "type_id": type_id})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let titleElements = $("[class=\"module-item-title\"]")
|
||||
let boxElements = $("[class=\"module-item-box\"]")
|
||||
let extend_list = []
|
||||
let type_id_dic = {"类型": 1, "剧情": 4, "地区": 2, "语言": 5, "年份": 12, "排序": 3}
|
||||
for (let i = 0; i < titleElements.length; i++) {
|
||||
let extend_dic = {"key": (i + 1).toString(), "name": $(titleElements[i]).text(), "value": []}
|
||||
let typeElements = $(boxElements[i]).find("a")
|
||||
for (let j = 0; j < typeElements.length; j++) {
|
||||
let type_name = $(typeElements[j]).text()
|
||||
let type_id = decodeURIComponent(typeElements[j].attribs["href"].split("-")[type_id_dic[$(titleElements[i]).text()]]).replaceAll(".html", "")
|
||||
extend_dic["value"].push({"n": type_name, "v": $(titleElements[i]).text() + "-" + type_id})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
|
||||
}
|
||||
return extend_list;
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const class_dic of this.classes) {
|
||||
if (class_dic["type_name"] !== "最近更新" && class_dic["type_name"] !== "热榜") {
|
||||
let cateUrl = this.siteUrl + `/vod-show/${class_dic["type_id"]}--------1---.html`
|
||||
let $ = await this.getHtml(cateUrl, this.getHeader())
|
||||
this.filterObj[class_dic["type_id"]] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml(this.siteUrl, this.getHeader())
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
getCateUrl(tid, pg, extend) {
|
||||
let value_list = Object.values(extend)
|
||||
let type_id_dic = {"类型": 1, "剧情": 3, "地区": 1, "语言": 4, "年份": 11, "排序": 2}
|
||||
let urlParams = [tid.toString(), "", "","","", "", "", "", "", "", "",""]
|
||||
urlParams[8] = pg.toString()
|
||||
for (const value of value_list) {
|
||||
if (value.split("-")[0] === "类型") {
|
||||
urlParams[0] = value.split("-")[1].split("show/")[1].toString()
|
||||
} else {
|
||||
let type_index = type_id_dic[value.split("-")[0]]
|
||||
urlParams[type_index] = value.split("-")[1]
|
||||
}
|
||||
|
||||
}
|
||||
return this.siteUrl + `/vod-show/` + urlParams.join("-") + ".html"
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let cateUrl
|
||||
if (tid.indexOf(".html") > -1) {
|
||||
cateUrl = this.siteUrl + tid
|
||||
let $ = await this.getHtml(cateUrl, this.getHeader());
|
||||
this.vodList = await this.parseVodShortListFromDocByHot($)
|
||||
} else {
|
||||
cateUrl = this.getCateUrl(tid, pg, extend)
|
||||
let $ = await this.getHtml(cateUrl, this.getHeader());
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = `http://123.207.150.253/zxapi/public/?service=App.F.Fetch&req_p=${wd}&type=okys`
|
||||
let content = await this.fetch(url,null,this.getHeader())
|
||||
this.vodList = await this.parseVodShortListFromJson(JSON.parse(content))
|
||||
|
||||
}
|
||||
|
||||
async refreshCookie() {
|
||||
let passUrl = this.siteUrl + "/index.php/verify/index.html?"
|
||||
let passHtml = await this.fetch(passUrl,null,this.getHeader(),false,true)
|
||||
|
||||
let response2 = await this.post(OCR_API,passHtml["content"],this.getHeader())
|
||||
this.cookie = Utils.getStrByRegex(/(.*?);/,passHtml["cookie"])
|
||||
let verifyUrl = this.siteUrl + "/index.php/ajax/verify_check?type=search&verify=5286"
|
||||
let headers = this.getHeader()
|
||||
headers["cookie"] = this.cookie
|
||||
let response = await this.post(verifyUrl,null,headers)
|
||||
await this.write_cache()
|
||||
}
|
||||
|
||||
|
||||
|
||||
async load_cache() {
|
||||
try {
|
||||
return await local.get("freeok_cookie", "cookie")
|
||||
} catch (e) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
async write_cache() {
|
||||
await local.set("freeok_cookie", "cookie", JSON.stringify(this.cookie))
|
||||
}
|
||||
|
||||
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + id, this.getHeader())
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let $ = await this.getHtml(this.siteUrl + id, this.getHeader())
|
||||
const js = JSON.parse($('script:contains(player_)').html().replace('var player_aaaa=', ''));
|
||||
let url = this.siteUrl + "/okplayer/"
|
||||
let params = {
|
||||
"url": decodeURIComponent(js.url), "next": decodeURIComponent(js.url_next), "title": js.vod_data.vod_name
|
||||
}
|
||||
let playHtml = await this.fetch(url, params, this.getHeader());
|
||||
let view_port_id = Utils.getStrByRegex(/<meta name="viewport"(.*?)>/, playHtml).split("id=\"")[1].replaceAll("now_", "")
|
||||
let player_id = Utils.getStrByRegex(/meta charset="UTF-8" id="(.*?)">/, playHtml).replaceAll("now_", "")
|
||||
let player_url = Utils.getStrByRegex(/"url": "(.*?)",/, playHtml)
|
||||
this.playUrl = player(player_url, view_port_id, player_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let spider = new OkSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
228
cat/tjs/js/gitcafe.js
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* @File : gitcafe.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/18 9:56
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 阿里纸条
|
||||
*/
|
||||
|
||||
import {_, load} from "../lib/cat.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {detailContent, initAli, playContent} from "../lib/ali.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
|
||||
class GitCafeSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://ali.gitcafe.ink"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🦊┃阿里纸条┃🦊"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "阿里纸条"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "gitcafe"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async getApiUrl() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let paper_js_url = Utils.getStrByRegex(/<script src='(.*?)'><\/script>/, html)
|
||||
let paper_js_content = await this.fetch(paper_js_url, null, this.getHeader())
|
||||
return {
|
||||
"api": "https:" + Utils.getStrByRegex(/ return '(.*?)' \+ /, paper_js_content) + new Date().getTime(),
|
||||
"search_api": Utils.getStrByRegex(/const SEARCH_API = '(.*?)';/, paper_js_content)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getContentJson() {
|
||||
let url_json = await this.getApiUrl()
|
||||
let content = await this.fetch(url_json["api"], null, this.getHeader())
|
||||
this.search_api = url_json["search_api"]
|
||||
if (!_.isEmpty(content)) {
|
||||
return JSON.parse(content)
|
||||
}
|
||||
}
|
||||
|
||||
async spiderInit() {
|
||||
this.content_json = await this.getContentJson()
|
||||
this.token_dic = await this.load_cache()
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await this.spiderInit()
|
||||
await super.init(cfg);
|
||||
await initAli(this.cfgObj["token"]);
|
||||
}
|
||||
|
||||
|
||||
async parseClassFromJson(obj) {
|
||||
let data_list = Object.keys(obj["data"]).slice(0, 19)
|
||||
for (const data_key of data_list) {
|
||||
this.classes.push({"type_name": obj["data"][data_key]["name"], "type_id": data_key})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
let class_id_list = this.getClassIdList()
|
||||
for (const data_obj of obj) {
|
||||
let vodShort = new VodShort()
|
||||
if (class_id_list.includes(data_obj["cat"])) {
|
||||
vodShort.vod_id = JSON.stringify(data_obj)
|
||||
vodShort.vod_name = data_obj["title"]
|
||||
vodShort.vod_remarks = data_obj["date"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let classNamesList = this.getClassNameList()
|
||||
let classIdList = this.getClassIdList()
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = obj["title"]
|
||||
vodDetail.vod_remarks = obj["creatime"] ?? obj["date"]
|
||||
vodDetail.type_name = classNamesList[classIdList.indexOf(obj["cat"])]
|
||||
vodDetail.vod_content = obj["des"]
|
||||
let ali_url = "https://www.aliyundrive.com/s/" + obj["alikey"]
|
||||
let aliVodDetail = await detailContent([ali_url])
|
||||
vodDetail.vod_play_url = aliVodDetail.vod_play_url
|
||||
vodDetail.vod_play_from = aliVodDetail.vod_play_from
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
await this.parseClassFromJson(this.content_json)
|
||||
}
|
||||
|
||||
|
||||
async setHomeVod() {
|
||||
this.homeVodList = await this.parseVodShortListFromJson(this.content_json["info"]["new"])
|
||||
}
|
||||
|
||||
|
||||
async setDetail(id) {
|
||||
let content_json = JSON.parse(id)
|
||||
this.vodDetail = await this.parseVodDetailfromJson(content_json)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
this.vodList = await this.parseVodShortListFromJson(this.content_json["data"][tid]["catdata"])
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
await this.refreshToken();
|
||||
let params = {
|
||||
"action": "search", "from": "web", "token": this.token_dic["token"], "keyword": wd
|
||||
}
|
||||
let content = await this.post(this.search_api, params, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
this.vodList = await this.parseVodShortListFromJson(content_json["data"])
|
||||
}
|
||||
}
|
||||
|
||||
async refreshToken() {
|
||||
let this_time = new Date().getTime()
|
||||
if (_.isEmpty(this.token_dic["token"])) {
|
||||
await this.get_token()
|
||||
await this.jadeLog.debug("Token为空,刷新Token")
|
||||
} else if (this_time - parseInt(this.token_dic["date"]) > 24 * 60 * 60 * 1000) {
|
||||
await this.jadeLog.debug(`Token到期,上次获取Token时间为:${this_time},当前时间为:${parseInt(this.token_dic["date"])},刷新Token`)
|
||||
await this.get_token()
|
||||
} else {
|
||||
await this.jadeLog.debug(`Token仍然有效,无需刷新`, true)
|
||||
}
|
||||
}
|
||||
|
||||
async get_token() {
|
||||
|
||||
try {
|
||||
let params = {
|
||||
"action": "get_token", "from": "web",
|
||||
}
|
||||
let content = await this.post(this.search_api, params, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
let this_time = new Date().getTime()
|
||||
this.token_dic["token"] = content_json["data"]
|
||||
this.token_dic["date"] = this_time.toString()
|
||||
await this.write_cache()
|
||||
}
|
||||
} catch (e) {
|
||||
await this.jadeLog.error("获取Token失败,失败原因为:" + e)
|
||||
}
|
||||
}
|
||||
|
||||
async write_cache() {
|
||||
await local.set("gitcafe_token", "token", JSON.stringify(this.token_dic))
|
||||
}
|
||||
|
||||
async load_cache() {
|
||||
try {
|
||||
let str = await local.get("gitcafe_token", "token")
|
||||
return JSON.parse(str)
|
||||
} catch (e) {
|
||||
return {"token": "", "date": ""}
|
||||
}
|
||||
}
|
||||
|
||||
async play(flag, id, flags) {
|
||||
return await playContent(flag, id, flags);
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new GitCafeSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
156
cat/tjs/js/haiwaikan.js
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* @File : haiwaikan.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/04/02 9:15
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 海外看
|
||||
*/
|
||||
import {VodSpider} from "./vodSpider.js";
|
||||
class HaiWaiKanSpider extends VodSpider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://haiwaikan.com"
|
||||
this.remove18 = true
|
||||
this.type_id_18 = 0
|
||||
}
|
||||
|
||||
async spiderInit(inReq) {
|
||||
await super.spiderInit(inReq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "海外看"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "☕┃海外看┃☕墙"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "haiwaikan"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let content = await this.fetch(this.siteUrl + "/api.php/provide/vod/from", {"ac": "list"}, this.getHeader())
|
||||
let content_json = JSON.parse(content)
|
||||
for (const class_dic of content_json["class"]) {
|
||||
if (class_dic["type_id"] < 26 ) {
|
||||
this.classes.push(this.getTypeDic(class_dic["type_name"], class_dic["type_id"].toString()))
|
||||
}
|
||||
}
|
||||
this.content_json = content_json
|
||||
}
|
||||
|
||||
async getFilter(type_id,obj) {
|
||||
let extend_list = []
|
||||
let extend_dic = {"key": "1", "name": "全部类别", "value": [{"n":"全部类别","v":type_id.toString()}]}
|
||||
for (const type_dic of obj["class"]){
|
||||
let a_type_id = type_dic["type_id"]
|
||||
let max_type_id = 0
|
||||
let min_type_id = 0
|
||||
if (type_id === 20){
|
||||
max_type_id = 50
|
||||
min_type_id = 27
|
||||
}
|
||||
if (type_id === 21){
|
||||
max_type_id = 128
|
||||
min_type_id = 100
|
||||
}
|
||||
if (type_id === 22){
|
||||
max_type_id = 143
|
||||
min_type_id = 134
|
||||
}
|
||||
if (type_id === 23){
|
||||
max_type_id = 135
|
||||
min_type_id = 127
|
||||
}
|
||||
if (a_type_id < max_type_id && a_type_id > min_type_id){
|
||||
extend_dic["value"].push({"n":type_dic["type_name"],"v":a_type_id.toString()})
|
||||
}
|
||||
}
|
||||
if (extend_dic["value"].length > 1){
|
||||
extend_list.push(extend_dic)
|
||||
return extend_list
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
let content = await this.fetch(this.siteUrl + "/api.php/provide/vod/from", {"ac": "list"}, this.getHeader())
|
||||
let content_json = JSON.parse(content)
|
||||
for (const type_dic of this.classes){
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "最近更新"){
|
||||
let extend_list = await this.getFilter(parseInt(type_id),content_json)
|
||||
if (extend_list !== null){
|
||||
this.filterObj[type_id] = extend_list
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
let spider = new HaiWaiKanSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
273
cat/tjs/js/haoxi.js
Normal file
@ -0,0 +1,273 @@
|
||||
/*
|
||||
* @File : haoxi.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/7 14:24
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 好戏追剧 已失效
|
||||
*/
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
class HaoXiSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://haoxi.vip"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return `好戏追剧`
|
||||
}
|
||||
|
||||
getName() {
|
||||
return `🌿┃好戏追剧┃🌿`
|
||||
}
|
||||
getJSName() {
|
||||
return "haoxi"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
parseVodShortFromElement($, element) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = $(element).find("a")[0].attribs.href
|
||||
vodShort.vod_name = $(element).find("a")[0].attribs.title
|
||||
if (vodShort.vod_name === undefined) {
|
||||
vodShort.vod_name = $($($(element).find("[class=\"thumb-txt cor4 hide\"]")).find("a")).html()
|
||||
}
|
||||
vodShort.vod_pic = $(element).find("img")[0].attribs["data-src"]
|
||||
vodShort.vod_remarks = $($(element).find("[class=\"public-list-prb hide ft2\"]")).html()
|
||||
return vodShort
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"flex bottom4\"]").find("[class=\"public-list-box public-pic-a [swiper]\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = this.parseVodShortFromElement($, vodElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"public-list-box public-pic-b [swiper]\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = this.parseVodShortFromElement($, vodElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"public-list-box search-box flex rel\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = this.parseVodShortFromElement($, vodElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetailElement = $("[class=\"vod-detail style-detail rel cor1 hader0\"]")
|
||||
let vodDetail = new VodDetail();
|
||||
vodDetail.vod_pic = $(vodDetailElement).find("img")[0].attribs.src
|
||||
vodDetail.vod_name = $($(vodDetailElement).find("[class=\"slide-info-title hide\"]")).html()
|
||||
let elements = $(vodDetailElement).find("[class=\"slide-info hide\"]")
|
||||
vodDetail.vod_year = $($($(elements[0]).find("[class=\"slide-info-remarks\"]")[0]).find("a")[0]).html()
|
||||
vodDetail.vod_area = $($($(elements[0]).find("[class=\"slide-info-remarks\"]")[1]).find("a")[0]).html()
|
||||
vodDetail.type_name = $($($(elements[0]).find("[class=\"slide-info-remarks\"]")[2]).find("a")[0]).html()
|
||||
vodDetail.vod_remarks = $(elements[1]).text().replaceAll("备注 :", "")
|
||||
vodDetail.vod_director = $(elements[2]).text().replaceAll("导演 :", "")
|
||||
vodDetail.vod_actor = $(elements[3]).text().replaceAll("演员 :", "")
|
||||
vodDetail.vod_content = $($("[class=\"text cor3\"]")).text()
|
||||
let playElements = $("[class=\"box-width cor5\"]")
|
||||
let playFormatElements = playElements.find("[class=\"swiper-slide\"]")
|
||||
let playUrlElements = playElements.find("[class=\"anthology-list-box none\"]")
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
for (let i = 0; i < playFormatElements.length; i++) {
|
||||
let playFormatElement = playFormatElements[i]
|
||||
let format_name = playFormatElement.children[1].data
|
||||
format_name = format_name.replaceAll(" ", "")
|
||||
vod_play_from_list.push(format_name)
|
||||
let vodItems = []
|
||||
if (format_name === "http下载") {
|
||||
for (const playUrlElement of $(playUrlElements[i]).find("a")) {
|
||||
let episodeName = $(playUrlElement).text()
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
if (episodeName !== "复制地址") {
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const playUrlElement of $(playUrlElements[i]).find("a")) {
|
||||
let episodeName = $(playUrlElement).text()
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let navElements = $("[class=\"head flex between no-null header_nav0\"]").find("li")
|
||||
for (const navElement of navElements) {
|
||||
let type_name = $($(navElement).find("a")).text()
|
||||
let type_id = Utils.getStrByRegex(/\/vodtype\/(.*?)\//, $(navElement).find("a")[0].attribs.href)
|
||||
if (Utils.isNumeric(type_id)) {
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let elements = $("[class=\"nav-swiper rel\"]")
|
||||
let extend_list = []
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let extend_dic = {"key": (i + 1).toString(), "name": "", "value": []}
|
||||
let name = $($($(elements[i]).find("[class=\"filter-text bj cor5\"]")[0]).find("span")).html()
|
||||
if (name !== "已选" && name !== "频道") {
|
||||
extend_dic["name"] = name
|
||||
for (const ele of $(elements[i]).find("li")) {
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": $(ele).text()})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
}
|
||||
let sortElments = $("[class=\"site-tabs b-b br\"]")
|
||||
let extend_dic = {"key": (elements.length + 1).toString(), "name": "排序", "value": []}
|
||||
extend_dic["value"].push({"n": "全部", "v": "/"})
|
||||
for (const ele of $(sortElments).find("a")) {
|
||||
let type_id_list = ele.attribs.href.split("-")
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": type_id_list[2]})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
|
||||
return extend_list
|
||||
}
|
||||
|
||||
|
||||
async setFilterObj() {
|
||||
for (const class_dic of this.classes) {
|
||||
let type_id = class_dic["type_id"]
|
||||
if (Utils.isNumeric(type_id)) {
|
||||
let url = this.siteUrl + `/vodshow/${type_id}-----------`
|
||||
let $ = await this.getHtml(url)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
get_extend_sort_dic(tid) {
|
||||
return {
|
||||
"3": 3, "4": 1, "5": 11, "6": 4, "9": 5, "10": 2,
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
// "1-大陆-hits-Netflix-英语-A----正片--2023/version/4K/"
|
||||
let urlParams = [tid.toString(), "", "", "", "", "", "", "", pg.toString(), "", "", ""]
|
||||
let extend_dic = this.get_extend_sort_dic(parseInt(tid))
|
||||
for (const key of Object.keys(extend_dic)) {
|
||||
if (extend[key] === "0") {
|
||||
urlParams[extend_dic[key]] = ""
|
||||
} else {
|
||||
if (extend[key] !== "全部") {
|
||||
urlParams[extend_dic[key]] = extend[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
let reqUrl = this.siteUrl + '/vodshow/' + urlParams.join("-");
|
||||
if (extend[7] !== undefined && extend[7] !== "全部") {
|
||||
reqUrl = reqUrl + `/version/${extend[7]}/`
|
||||
}
|
||||
await this.jadeLog.debug(`分类URL:${reqUrl}`)
|
||||
let $ = await this.getHtml(reqUrl)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let $ = await this.getHtml(this.siteUrl + `/vodsearch/-------------/?wd=${wd}`)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
if (flag !== "http下载") {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
let playConfig = JSON.parse(Utils.getStrByRegex(/var player_aaaa=(.*?)<\/script>/, $.html()))
|
||||
this.playUrl = playConfig['url']
|
||||
} else {
|
||||
this.playUrl = id
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new HaoXiSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
153
cat/tjs/js/hscangku.js
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* @File : hscangku.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/01/03 19:19
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
|
||||
class HsCangkuSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://hsck12.shop/"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🔞┃黄色仓库┃🔞"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "黄色仓库"
|
||||
}
|
||||
getJSName() {
|
||||
return "hscangku"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"stui-vodlist clearfix\"]").find("li")
|
||||
for (const vod_element of vodElements) {
|
||||
let vodShort = new VodShort()
|
||||
let vodElement = $(vod_element).find("a")[0]
|
||||
vodShort.vod_id = vodElement.attribs["href"]
|
||||
vodShort.vod_name = vodElement.attribs["title"]
|
||||
vodShort.vod_pic = vodElement.attribs["data-original"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
let element = $($("[class=\"stui-pannel__head clearfix\"]")[1]).find("h3")
|
||||
let stui_pannel_bd_element = $("div.stui-pannel-bd > div")
|
||||
let video_element = stui_pannel_bd_element.find("video")[0]
|
||||
vodDetail.vod_name = element.text()
|
||||
vodDetail.vod_pic = video_element.attribs["poster"]
|
||||
vodDetail.vod_play_from = "黄色仓库"
|
||||
vodDetail.vod_play_url = $(video_element).find("source")[0].attribs["src"]
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
this.classes = [
|
||||
{
|
||||
"type_name": "国产视频",
|
||||
"type_id": "?type=gc"
|
||||
},
|
||||
{
|
||||
"type_name": "国产新片",
|
||||
"type_id": "?type=ycgc"
|
||||
},
|
||||
{
|
||||
"type_name": "无码中文字幕",
|
||||
"type_id": "?type=wz"
|
||||
},
|
||||
{
|
||||
"type_name": "有码中文字幕",
|
||||
"type_id": "?type=yz"
|
||||
},
|
||||
{
|
||||
"type_name": "日本无码",
|
||||
"type_id": "?type=rw"
|
||||
}
|
||||
]
|
||||
}
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let url = this.siteUrl + tid + "&p=" + pg.toString()
|
||||
let html = await this.fetch(url, null,this.getHeader())
|
||||
this.limit = 40;
|
||||
if (html !== null) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
this.total = parseInt($("[class=\"active\"]").find("span").text())
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let url = this.siteUrl + id
|
||||
let html = await this.fetch(url,null,this.getHeader())
|
||||
if (html !== null) {
|
||||
let $ = load(html)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
this.playUrl = id
|
||||
this.playHeader = {}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new HsCangkuSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
709
cat/tjs/js/huya.js
Normal file
@ -0,0 +1,709 @@
|
||||
/*
|
||||
* @File : huya.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/7 9:13
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
class HuyaSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.isJustLive = false
|
||||
this.dataFrom = ""
|
||||
this.customArea = ""
|
||||
this.huYaPlayForamtObj = {"AL": "蓝光8M", "TX": "蓝光4M", "HW": "超清", "HS": "流畅"}
|
||||
this.livePlayForamtObj = {"HD": "蓝光8M", "LD": "蓝光4M", "SD": "超清", "OD": "流畅"}
|
||||
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🐯┃虎牙直播┃🐯"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "虎牙直播"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "huya"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async spiderInit() {
|
||||
if (this.cfgObj.hasOwnProperty('custom')) {
|
||||
this.customArea = this.cfgObj.custom;
|
||||
}
|
||||
if (this.cfgObj.hasOwnProperty('from')) {
|
||||
this.dataFrom = this.cfgObj.from;
|
||||
}
|
||||
if (this.dataFrom !== 'justlive') {
|
||||
this.siteUrl = 'https://www.huya.com';
|
||||
} else {
|
||||
this.siteUrl = 'http://live.yj1211.work';
|
||||
this.isJustLive = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit()
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
let header = super.getHeader();
|
||||
header["User-Agent"] = Utils.MOBILEUA
|
||||
return header
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
this.classes = [{
|
||||
"type_id": "1", "type_name": "网游"
|
||||
}, {"type_id": "2", "type_name": "单机"}, {"type_id": "3", "type_name": "手游"}, {
|
||||
"type_id": "8", "type_name": "娱乐"
|
||||
}]
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
this.filterObj = {
|
||||
"1": [{
|
||||
"key": "cateId",
|
||||
"name": "分类",
|
||||
"init": "1",
|
||||
"value": [{"n": "英雄联盟", "v": "1"}, {"n": "CS2", "v": "862"}, {
|
||||
"n": "穿越火线", "v": "4"
|
||||
}, {"n": "lol云顶之弈", "v": "5485"}, {"n": "无畏契约", "v": "5937"}, {
|
||||
"n": "CFHD", "v": "6079"
|
||||
}, {"n": "逆战", "v": "135"}, {"n": "炉石传说", "v": "393"}, {"n": "DOTA2", "v": "7"}, {
|
||||
"n": "地下城与勇士", "v": "2"
|
||||
}, {"n": "魔兽世界", "v": "8"}, {"n": "坦克世界", "v": "802"}, {"n": "DOTA1", "v": "6"}, {
|
||||
"n": "梦三国", "v": "489"
|
||||
}, {"n": "魔兽争霸3", "v": "4615"}, {"n": "问道", "v": "107"}, {
|
||||
"n": "命运方舟", "v": "3058"
|
||||
}, {"n": "QQ飞车", "v": "9"}, {"n": "星际争霸", "v": "5"}, {
|
||||
"n": "网游竞技", "v": "100023"
|
||||
}, {"n": "射击综合游戏", "v": "100141"}, {"n": "暴雪专区", "v": "100043"}, {
|
||||
"n": "彩虹岛Online", "v": "683"
|
||||
}, {"n": "剑灵", "v": "897"}, {"n": "军事游戏", "v": "100133"}, {
|
||||
"n": "冒险岛", "v": "2243"
|
||||
}, {"n": "暗黑破坏神", "v": "1123"}, {"n": "诛仙3", "v": "1646"}, {
|
||||
"n": "热血江湖", "v": "387"
|
||||
}, {"n": "英魂之刃", "v": "1830"}, {"n": "武林外传一世琴缘", "v": "1661"}, {
|
||||
"n": "起凡:群雄逐鹿", "v": "1612"
|
||||
}, {"n": "神武4电脑版", "v": "3227"}, {"n": "龙之谷", "v": "15"}, {
|
||||
"n": "炉石战棋", "v": "5751"
|
||||
}, {"n": "御龙在天", "v": "675"}, {"n": "逆水寒", "v": "2952"}, {
|
||||
"n": "千年3", "v": "878"
|
||||
}, {"n": "永恒之塔", "v": "446"}, {"n": "体育游戏", "v": "100135"}, {
|
||||
"n": "寻仙", "v": "734"
|
||||
}, {"n": "战舰世界", "v": "1947"}, {"n": "QQ三国", "v": "1090"}, {
|
||||
"n": "流放之路", "v": "427"
|
||||
}, {"n": "反恐精英Online", "v": "1918"}, {"n": "反恐行动online", "v": "861"}, {
|
||||
"n": "征途", "v": "2715"
|
||||
}, {"n": "战争雷霆", "v": "624"}, {"n": "丝路传说2", "v": "1026"}, {
|
||||
"n": "星际战甲", "v": "627"
|
||||
}, {"n": "NBA2KOL系列", "v": "3959"}, {"n": "九阴真经", "v": "1009"}, {
|
||||
"n": "跑跑卡丁车", "v": "162"
|
||||
}, {"n": "诛仙世界", "v": "7749"}, {"n": "QQ华夏", "v": "1878"}, {
|
||||
"n": "天涯明月刀", "v": "1219"
|
||||
}, {"n": "大话西游:归来", "v": "8239"}, {"n": "荒野行动PC版", "v": "3185"}, {
|
||||
"n": "新剑侠情缘", "v": "586"
|
||||
}, {"n": "剑网3", "v": "900"}, {"n": "生死狙击2", "v": "6091"}, {
|
||||
"n": "全球使命", "v": "939"
|
||||
}, {"n": "梦想世界3", "v": "486"}, {"n": "枪神纪", "v": "496"}, {
|
||||
"n": "新天龙八部", "v": "5671"
|
||||
}, {"n": "铁甲雄兵", "v": "2765"}, {"n": "神泣", "v": "2531"}, {
|
||||
"n": "斗战神", "v": "591"
|
||||
}, {"n": "造梦西游OL", "v": "6815"}, {"n": "天堂", "v": "1966"}, {
|
||||
"n": "大话西游2", "v": "2975"
|
||||
}, {"n": "使命召唤:战区", "v": "5911"}, {"n": "希望OL", "v": "1161"}, {
|
||||
"n": "极光世界 弑神传", "v": "514"
|
||||
}, {"n": "守望先锋归来", "v": "2174"}, {"n": "QQ自由幻想", "v": "1862"}, {
|
||||
"n": "命运2", "v": "2942"
|
||||
}, {"n": "奇迹世界2", "v": "766"}, {"n": "坦克大战", "v": "4359"}, {
|
||||
"n": "生死狙击", "v": "2471"
|
||||
}, {"n": "天翼决", "v": "779"}, {"n": "QQ幻想", "v": "2419"}, {
|
||||
"n": "新飞飞(FlyFF)", "v": "1582"
|
||||
}, {"n": "刀剑英雄", "v": "915"}, {"n": "FIFA Online系列", "v": "100079"}, {
|
||||
"n": "全球使命3", "v": "2953"
|
||||
}, {"n": "完美端游系列", "v": "3034"}, {"n": "战意", "v": "2599"}, {
|
||||
"n": "泡泡堂", "v": "440"
|
||||
}, {"n": "赛尔号", "v": "2755"}, {"n": "大唐无双零", "v": "1584"}, {
|
||||
"n": "QQ炫舞", "v": "2440"
|
||||
}, {"n": "007:传奇", "v": "1135"}, {"n": "天下", "v": "1597"}, {
|
||||
"n": "天谕", "v": "1899"
|
||||
}, {"n": "新倩女幽魂", "v": "1579"}, {"n": "传世无双", "v": "984"}, {
|
||||
"n": "剑侠世界", "v": "903"
|
||||
}, {"n": "Warhaven", "v": "9053"}, {"n": "诺亚传说", "v": "190"}, {
|
||||
"n": "新挑战", "v": "583"
|
||||
}, {"n": "超激斗梦境", "v": "5691"}, {"n": "QQ音速", "v": "1085"}, {"n": "征途2", "v": "677"}, {
|
||||
"n": "征程", "v": "678"
|
||||
}, {"n": "蜀门", "v": "4711"}, {"n": "完美世界:诸神之战", "v": "7217"}, {
|
||||
"n": "战之刃:幸存者", "v": "8627"
|
||||
}, {"n": "黎明之光", "v": "41"}, {"n": "天命西游", "v": "1086"}, {
|
||||
"n": "炫舞时代", "v": "3353"
|
||||
}, {"n": "荣誉空间", "v": "225"}, {"n": "卡拉彼丘", "v": "9073"}, {
|
||||
"n": "成吉思汗怀旧版", "v": "640"
|
||||
}, {"n": "英雄年代3", "v": "1232"}, {"n": "天书奇谈", "v": "2225"}, {
|
||||
"n": "劲舞团", "v": "2420"
|
||||
}, {"n": "远征Online梦想版", "v": "142"}, {"n": "奇迹:传奇", "v": "3917"}, {
|
||||
"n": "封印者", "v": "2294"
|
||||
}, {"n": "上古世纪", "v": "1046"}, {"n": "梦幻诛仙2", "v": "488"}, {
|
||||
"n": "TERA Online", "v": "1072"
|
||||
}, {"n": "倚天Ⅱ", "v": "959"}, {"n": "街头篮球", "v": "206"}, {
|
||||
"n": "永恒轮回:无限", "v": "7847"
|
||||
}, {"n": "火线精英", "v": "2550"}, {"n": "忍者村大战2", "v": "2369"}, {
|
||||
"n": "领地人生", "v": "2282"
|
||||
}, {"n": "仙侠世界", "v": "995"}, {"n": "洛奇英雄传", "v": "432"}, {
|
||||
"n": "KARDS", "v": "8261"
|
||||
}, {"n": "无限法则", "v": "3189"}, {"n": "全面战争:竞技场", "v": "5901"}, {
|
||||
"n": "自由篮球", "v": "243"
|
||||
}, {"n": "FC Online4", "v": "3683"}, {"n": "战地之王", "v": "618"}, {
|
||||
"n": "古剑奇谭OL", "v": "1892"
|
||||
}, {"n": "QQ仙侠传", "v": "2291"}, {"n": "300英雄", "v": "1132"}]
|
||||
}], "2": [{
|
||||
"key": "cateId",
|
||||
"name": "分类",
|
||||
"init": "2793",
|
||||
"value": [{"n": "天天吃鸡", "v": "2793"}, {"n": "永劫无间", "v": "6219"}, {
|
||||
"n": "主机游戏", "v": "100032"
|
||||
}, {"n": "猛兽派对", "v": "6165"}, {"n": "互动点播", "v": "5907"}, {
|
||||
"n": "我的世界", "v": "1732"
|
||||
}, {"n": "方舟", "v": "1997"}, {"n": "单机热游", "v": "100002"}, {
|
||||
"n": "怀旧游戏", "v": "100125"
|
||||
}, {"n": "逃离塔科夫", "v": "3493"}, {"n": "俄罗斯钓鱼4", "v": "5495"}, {
|
||||
"n": "部落:上升", "v": "1318"
|
||||
}, {"n": "Dark and Darker", "v": "7905"}, {"n": "The Front", "v": "9497"}, {
|
||||
"n": "Apex英雄", "v": "5011"
|
||||
}, {"n": "生化危机4重制版", "v": "8013"}, {"n": "DayZ独立版", "v": "1125"}, {
|
||||
"n": "星空", "v": "7857"
|
||||
}, {"n": "互动剧游", "v": "6919"}, {"n": "艾尔登法环", "v": "5801"}, {
|
||||
"n": "恐惧之间", "v": "6679"
|
||||
}, {"n": "SCUM", "v": "4245"}, {"n": "Dread Hunger", "v": "7601"}, {
|
||||
"n": "塞尔达传说:王国之泪", "v": "7883"
|
||||
}, {"n": "欧洲卡车模拟", "v": "475"}, {"n": "洛克王国", "v": "2864"}, {
|
||||
"n": "卧龙:苍天陨落", "v": "7859"
|
||||
}, {"n": "无人深空", "v": "2566"}, {"n": "帝国神话", "v": "6821"}, {
|
||||
"n": "饥荒", "v": "74"
|
||||
}, {"n": "森林之子", "v": "7943"}, {"n": "星球大战系列", "v": "554"}, {
|
||||
"n": "极限竞速:地平线", "v": "2634"
|
||||
}, {"n": "最终幻想16", "v": "7869"}, {"n": "Among Us", "v": "6163"}, {
|
||||
"n": "怪物猎人:崛起", "v": "6479"
|
||||
}, {"n": "怪物猎人物语", "v": "7101"}, {"n": "骑马与砍杀系列", "v": "4783"}, {
|
||||
"n": "荒野大镖客2", "v": "4319"
|
||||
}, {"n": "太荒初境", "v": "7685"}, {"n": "罗布乐思", "v": "5771"}, {
|
||||
"n": "只狼:影逝二度", "v": "4505"
|
||||
}, {"n": "双人成行", "v": "6737"}, {"n": "重生边缘", "v": "6201"}, {
|
||||
"n": "海贼王 寻秘世界", "v": "5097"
|
||||
}, {"n": "战神:诸神黄昏", "v": "7771"}, {"n": "恐鬼症", "v": "6205"}, {
|
||||
"n": "鬼谷八荒", "v": "6571"
|
||||
}, {"n": "霍格沃茨之遗", "v": "7881"}, {"n": "全面战争", "v": "3521"}, {
|
||||
"n": "仁王2", "v": "5795"
|
||||
}, {"n": "奥拉星", "v": "2846"}, {"n": "甜蜜之家", "v": "6739"}, {
|
||||
"n": "仙剑奇侠传七", "v": "6509"
|
||||
}, {"n": "消逝的光芒2", "v": "7581"}, {"n": "渡神记", "v": "6231"}, {
|
||||
"n": "归家异途", "v": "2949"
|
||||
}, {"n": "博德之门3", "v": "6147"}, {"n": "看门狗:军团", "v": "6155"}, {
|
||||
"n": "使命召唤系列", "v": "100137"
|
||||
}, {"n": "育碧游戏", "v": "100139"}, {"n": "帝国时代4", "v": "4835"}, {
|
||||
"n": "英灵神殿", "v": "6609"
|
||||
}, {"n": "蛮荒志异:起源", "v": "9709"}, {"n": "岛", "v": "3019"}, {
|
||||
"n": "沙盒与副本", "v": "9151"
|
||||
}, {"n": "扫雷", "v": "2349"}, {"n": "街机游戏", "v": "5999"}, {
|
||||
"n": "幽灵线:东京", "v": "7669"
|
||||
}, {"n": "怪物猎人世界", "v": "3519"}, {"n": "盗贼之海", "v": "3641"}, {
|
||||
"n": "纸人", "v": "5257"
|
||||
}, {"n": "黑色像素人", "v": "28"}, {"n": "对马岛之魂", "v": "6039"}, {
|
||||
"n": "瑞奇与叮当", "v": "2455"
|
||||
}, {"n": "港诡实录", "v": "5853"}, {"n": "剑士", "v": "1467"}, {
|
||||
"n": "魔法门之英雄无敌系列", "v": "2096"
|
||||
}, {"n": "星露谷物语", "v": "2443"}, {"n": "仙剑奇侠传四", "v": "1659"}, {
|
||||
"n": "伤害世界", "v": "2337"
|
||||
}, {"n": "禁闭求生", "v": "6065"}, {"n": "真三国无双", "v": "1599"}, {
|
||||
"n": "恐怖黎明", "v": "3435"
|
||||
}, {"n": "其他单机", "v": "3069"}, {"n": "幽灵行动:荒野", "v": "2794"}, {
|
||||
"n": "边缘", "v": "151"
|
||||
}, {"n": "阿凡达", "v": "106"}, {"n": "全面战争:三国", "v": "3004"}, {
|
||||
"n": "黎明之星", "v": "40"
|
||||
}, {"n": "翼星求生", "v": "7463"}, {"n": "黎明传说", "v": "39"}, {
|
||||
"n": "恶魔之魂", "v": "6151"
|
||||
}, {"n": "艾兰岛", "v": "3855"}, {"n": "龙与地下城:秘影历代记HD", "v": "17"}, {
|
||||
"n": "龙与地下城:匕首谷", "v": "18"
|
||||
}, {"n": "沙石镇时光", "v": "7687"}, {"n": "三国志曹操传", "v": "2592"}, {
|
||||
"n": "FIFA足球", "v": "1888"
|
||||
}, {"n": "最终幻想7:重制版", "v": "5809"}, {"n": "四海兄弟", "v": "5995"}, {
|
||||
"n": "最终幻想:起源", "v": "7653"
|
||||
}, {"n": "摔跤城大乱斗", "v": "7773"}, {"n": "音乐游戏", "v": "2761"}, {
|
||||
"n": "精灵与萤火意志", "v": "5895"
|
||||
}, {"n": "桥梁建造师", "v": "3275"}, {"n": "哈迪斯", "v": "6153"}, {
|
||||
"n": "小缇娜的奇幻之地", "v": "7647"
|
||||
}, {"n": "宝可梦:剑盾", "v": "5715"}, {"n": "边境", "v": "4779"}, {
|
||||
"n": "人类一败涂地", "v": "3289"
|
||||
}, {"n": "糖豆人:终极淘汰赛", "v": "6083"}, {"n": "精灵宝可梦Let's Go", "v": "4375"}, {
|
||||
"n": "战锤40K:暗潮", "v": "3016"
|
||||
}, {"n": "石油骚动", "v": "2585"}, {"n": "紫塞秋风", "v": "6045"}, {
|
||||
"n": "阿尔比恩", "v": "8115"
|
||||
}, {"n": "猎人:荒野的召唤", "v": "2906"}, {"n": "FIFA 23", "v": "7789"}, {
|
||||
"n": "都市:天际线", "v": "2201"
|
||||
}, {"n": "乐高大电影:游戏版", "v": "1439"}, {"n": "全面战争:阿提拉", "v": "2383"}, {
|
||||
"n": "斩妖行", "v": "6105"
|
||||
}, {"n": "马里奥赛车8", "v": "5947"}, {"n": "复仇者联盟", "v": "6121"}, {"n": "霓虹深渊", "v": "5743"}]
|
||||
}], "3": [{
|
||||
"key": "cateId",
|
||||
"name": "分类",
|
||||
"init": "2336",
|
||||
"value": [{"n": "王者荣耀", "v": "2336"}, {"n": "和平精英", "v": "3203"}, {
|
||||
"n": "英雄联盟手游", "v": "6203"
|
||||
}, {"n": "CF手游", "v": "2413"}, {"n": "金铲铲之战", "v": "7185"}, {
|
||||
"n": "三国杀", "v": "1669"
|
||||
}, {"n": "原神", "v": "5489"}, {"n": "棋牌桌游", "v": "100036"}, {
|
||||
"n": "综合手游", "v": "100029"
|
||||
}, {"n": "劲乐幻想", "v": "7691"}, {"n": "新游广场", "v": "100052"}, {
|
||||
"n": "崩坏:星穹铁道", "v": "7349"
|
||||
}, {"n": "火影忍者手游", "v": "2429"}, {"n": "第五人格", "v": "3115"}, {
|
||||
"n": "问道手游", "v": "2477"
|
||||
}, {"n": "暗区突围", "v": "7209"}, {"n": "QQ飞车手游", "v": "2928"}, {
|
||||
"n": "球球大作战", "v": "2411"
|
||||
}, {"n": "明日之后", "v": "3483"}, {"n": "皇室战争", "v": "2439"}, {
|
||||
"n": "COD手游", "v": "4769"
|
||||
}, {"n": "手游休闲", "v": "100004"}, {"n": "二次元手游", "v": "100091"}, {
|
||||
"n": "摸了个鱼", "v": "9283"
|
||||
}, {"n": "MMORPG", "v": "100273"}, {"n": "动作游戏", "v": "100197"}, {
|
||||
"n": "战争冲突", "v": "7449"
|
||||
}, {"n": "王者模拟战", "v": "5699"}, {"n": "逆水寒手游", "v": "7725"}, {
|
||||
"n": "幻塔", "v": "6437"
|
||||
}, {"n": "欢乐斗地主", "v": "1749"}, {"n": "神武4手游", "v": "3135"}, {
|
||||
"n": "阴阳师", "v": "2598"
|
||||
}, {"n": "DNF手游", "v": "4921"}, {"n": "欢乐麻将", "v": "1751"}, {
|
||||
"n": "英雄联盟电竞经理", "v": "7177"
|
||||
}, {"n": "狼人杀手游", "v": "100049"}, {"n": "新天龙八部手游", "v": "6945"}, {
|
||||
"n": "中国象棋", "v": "1671"
|
||||
}, {"n": "天天象棋", "v": "4997"}, {"n": "传奇世界3D", "v": "3961"}, {
|
||||
"n": "御龙在天手游", "v": "2568"
|
||||
}, {"n": "高能英雄", "v": "8359"}, {"n": "天龙八部手游", "v": "2852"}, {
|
||||
"n": "三国志战略版", "v": "5619"
|
||||
}, {"n": "忍者必须死3", "v": "4041"}, {"n": "SKY光遇", "v": "3719"}, {
|
||||
"n": "虎牙领主争霸", "v": "7529"
|
||||
}, {"n": "云上城之歌", "v": "5977"}, {"n": "晶核", "v": "7279"}, {
|
||||
"n": "仙境传说RO", "v": "2675"
|
||||
}, {"n": "天天狼人", "v": "2774"}, {"n": "JJ棋牌", "v": "3841"}, {
|
||||
"n": "奇迹MU:觉醒", "v": "3116"
|
||||
}, {"n": "指尖四川麻将", "v": "7215"}, {"n": "部落冲突", "v": "1797"}, {
|
||||
"n": "新剑侠情缘手游", "v": "6259"
|
||||
}, {"n": "萌宠远征", "v": "9385"}, {"n": "暗黑破坏神:不朽", "v": "6385"}, {
|
||||
"n": "英雄杀", "v": "2688"
|
||||
}, {"n": "热血江湖手游", "v": "2817"}, {"n": "蛋仔派对", "v": "6909"}, {
|
||||
"n": "完美世界手游", "v": "4237"
|
||||
}, {"n": "巅峰战舰", "v": "2502"}, {"n": "狼人杀", "v": "2785"}, {
|
||||
"n": "斗罗大陆:魂师对决", "v": "6745"
|
||||
}, {"n": "武侠乂手游", "v": "4929"}, {"n": "诛仙手游", "v": "2647"}, {
|
||||
"n": "斗破苍穹手游", "v": "4337"
|
||||
}, {"n": "迷你世界", "v": "2683"}, {"n": "风云", "v": "3061"}, {
|
||||
"n": "妄想山海", "v": "6007"
|
||||
}, {"n": "曙光英雄", "v": "6169"}, {"n": "三国战纪2", "v": "6049"}, {
|
||||
"n": "梦幻新诛仙", "v": "5975"
|
||||
}, {"n": "黎明觉醒:生机", "v": "6131"}, {"n": "远征", "v": "7837"}, {
|
||||
"n": "航海王:燃烧意志", "v": "3943"
|
||||
}, {"n": "火炬之光:无限", "v": "6399"}, {"n": "巅峰极速", "v": "6979"}, {
|
||||
"n": "圣境之塔", "v": "7055"
|
||||
}, {"n": "魔力宝贝", "v": "2891"}, {"n": "香肠派对", "v": "3639"}, {
|
||||
"n": "创造与魔法", "v": "2931"
|
||||
}, {"n": "JJ斗地主", "v": "6271"}, {"n": "永恒纪元:戒", "v": "2646"}, {
|
||||
"n": "天涯明月刀手游", "v": "5115"
|
||||
}, {"n": "狼人杀官方", "v": "3679"}, {"n": "混沌起源", "v": "5985"}, {
|
||||
"n": "多多自走棋", "v": "5133"
|
||||
}, {"n": "梦幻诛仙手游", "v": "2672"}, {"n": "鸿图之下", "v": "6027"}, {
|
||||
"n": "新笑傲江湖", "v": "5669"
|
||||
}, {"n": "凡人修仙传:人界篇", "v": "8297"}, {"n": "多乐棋牌", "v": "6209"}, {
|
||||
"n": "口袋觉醒", "v": "5953"
|
||||
}, {"n": "跑跑卡丁车手游", "v": "2620"}, {"n": "奶块", "v": "2775"}, {
|
||||
"n": "月圆之夜", "v": "4339"
|
||||
}, {"n": "率土之滨", "v": "2691"}, {"n": "征途2手游", "v": "2811"}, {
|
||||
"n": "英魂之刃口袋版", "v": "2760"
|
||||
}, {"n": "精灵盛典:黎明", "v": "6123"}, {"n": "方舟手游", "v": "4035"}, {
|
||||
"n": "掼蛋", "v": "6225"
|
||||
}, {"n": "绝世仙王", "v": "6619"}, {"n": "流星群侠传", "v": "3927"}, {
|
||||
"n": "寻仙手游", "v": "2979"
|
||||
}, {"n": "一梦江湖", "v": "3082"}, {"n": "Lost Light(萤火突击国际服)", "v": "6859"}, {
|
||||
"n": "弹幕云游戏", "v": "7001"
|
||||
}, {"n": "猎魂觉醒", "v": "3071"}, {"n": "冒险岛:枫之传说", "v": "8005"}, {
|
||||
"n": "征途手游", "v": "2556"
|
||||
}, {"n": "海岛奇兵", "v": "2624"}, {"n": "倩女幽魂手游", "v": "2503"}, {
|
||||
"n": "超凡先锋", "v": "6507"
|
||||
}, {"n": "龙之谷2手游", "v": "2736"}, {"n": "崩坏3", "v": "2639"}, {
|
||||
"n": "猫和老鼠", "v": "2758"
|
||||
}, {"n": "七人传奇:光与暗之交战", "v": "8125"}, {"n": "JJ麻将", "v": "9487"}, {
|
||||
"n": "拉轰西游", "v": "9543"
|
||||
}, {"n": "三国战纪", "v": "6047"}, {"n": "自由幻想手游", "v": "4015"}, {
|
||||
"n": "秦时明月世界", "v": "5279"
|
||||
}, {"n": "新斗罗大陆", "v": "6657"}, {"n": "新神魔大陆", "v": "5939"}, {
|
||||
"n": "逃跑吧!少年", "v": "4137"
|
||||
}, {"n": "太古神王2", "v": "6649"}, {"n": "剑侠世界3", "v": "7183"}, {
|
||||
"n": "天天吃鸡手机版", "v": "4341"
|
||||
}, {"n": "时空猎人3", "v": "6411"}, {"n": "合金弹头:觉醒", "v": "6931"}, {
|
||||
"n": "明日方舟", "v": "4925"
|
||||
}, {"n": "原始征途", "v": "7713"}, {"n": "奇迹:最强者", "v": "3215"}, {
|
||||
"n": "天天酷跑", "v": "1715"
|
||||
}, {"n": "FC 足球世界", "v": "3873"}, {"n": "万国觉醒", "v": "6159"}, {
|
||||
"n": "机动都市阿尔法", "v": "5411"
|
||||
}, {"n": "航海王热血航线", "v": "6181"}, {"n": "幻世九歌", "v": "7199"}, {
|
||||
"n": "植物大战僵尸", "v": "485"
|
||||
}, {"n": "无悔华夏", "v": "7063"}, {"n": "时空猎人", "v": "1742"}, {
|
||||
"n": "荒野乱斗", "v": "4613"
|
||||
}, {"n": "拳皇98终极之战OL", "v": "2687"}, {"n": "蛇蛇争霸", "v": "2680"}, {
|
||||
"n": "王牌竞速", "v": "6463"
|
||||
}, {"n": "重返帝国", "v": "6955"}, {"n": "吞噬星空:黎明", "v": "6651"}, {
|
||||
"n": "口袋妖怪", "v": "2541"
|
||||
}, {"n": "QQ炫舞手游", "v": "2991"}, {"n": "一拳超人:最强之男", "v": "4629"}, {
|
||||
"n": "荣耀新三国", "v": "6943"
|
||||
}, {"n": "少年三国志2", "v": "6125"}, {"n": "我的起源", "v": "5365"}, {
|
||||
"n": "决战平安京", "v": "3064"
|
||||
}, {"n": "剑灵2", "v": "7223"}, {"n": "开心消消乐", "v": "1712"}, {
|
||||
"n": "小小蚁国", "v": "7803"
|
||||
}, {"n": "最强NBA", "v": "2988"}, {"n": "剑侠情缘手游", "v": "2621"}, {
|
||||
"n": "长安幻想", "v": "6727"
|
||||
}, {"n": "我叫MT4", "v": "4087"}, {"n": "全明星街球派对", "v": "8401"}, {
|
||||
"n": "大话西游手游", "v": "2626"
|
||||
}, {"n": "荣耀大天使", "v": "6477"}, {"n": "镇魂街:天生为王", "v": "6557"}, {
|
||||
"n": "摩尔庄园", "v": "5981"
|
||||
}, {"n": "游戏王:决斗链接", "v": "4451"}, {"n": "剑侠世界2手游", "v": "3150"}, {
|
||||
"n": "青云诀2", "v": "6009"
|
||||
}, {"n": "战地无疆", "v": "7909"}, {"n": "一念逍遥", "v": "6419"}, {
|
||||
"n": "永劫无间手游", "v": "7579"
|
||||
}, {"n": "尘白禁区", "v": "7297"}, {"n": "元梦之星", "v": "9521"}, {
|
||||
"n": "不良人3", "v": "5891"
|
||||
}, {"n": "剑灵:革命", "v": "4545"}, {"n": "魔力宝贝:旅人", "v": "7573"}, {
|
||||
"n": "米加小镇", "v": "7269"
|
||||
}, {"n": "龙武手游", "v": "5219"}, {"n": "斗罗大陆2:绝世唐门", "v": "6581"}, {
|
||||
"n": "西行纪燃魂", "v": "8303"
|
||||
}, {"n": "坦克世界闪击战", "v": "4977"}, {"n": "军棋", "v": "2561"}, {
|
||||
"n": "饥荒:新家", "v": "6491"
|
||||
}, {"n": "拳皇命运", "v": "3379"}, {"n": "实况足球", "v": "3741"}, {
|
||||
"n": "战舰世界闪击战", "v": "4101"
|
||||
}, {"n": "时空召唤", "v": "2551"}, {"n": "王牌战争:文明重启", "v": "5479"}, {
|
||||
"n": "雀魂麻将", "v": "7107"
|
||||
}, {"n": "欢乐升级", "v": "3925"}, {"n": "绿色征途", "v": "4227"}, {
|
||||
"n": "弹弹堂手游", "v": "2857"
|
||||
}, {"n": "太极熊猫3:猎龙", "v": "2778"}, {"n": "哈利波特:魔法觉醒", "v": "5835"}, {
|
||||
"n": "天地劫:幽城再临", "v": "5987"
|
||||
}, {"n": "热血街篮", "v": "5859"}, {"n": "神雕侠侣手游", "v": "1781"}, {
|
||||
"n": "山海镜花", "v": "5089"
|
||||
}, {"n": "三国志战棋版", "v": "7937"}, {"n": "神雕侠侣2", "v": "4209"}, {
|
||||
"n": "仙魔决", "v": "1674"
|
||||
}, {"n": "王者荣耀星之破晓", "v": "7927"}, {"n": "我在江湖之神魔道", "v": "7699"}, {
|
||||
"n": "梦幻模拟战", "v": "3481"
|
||||
}, {"n": "单机手游", "v": "2777"}, {"n": "斗罗大陆-斗神再临", "v": "6631"}, {
|
||||
"n": "未来之役", "v": "6831"
|
||||
}, {"n": "风云岛行动", "v": "4695"}, {"n": "新游推荐", "v": "3160"}, {
|
||||
"n": "火影忍者OL", "v": "3901"
|
||||
}, {"n": "九灵神域", "v": "7719"}, {"n": "武动乾坤", "v": "3829"}, {
|
||||
"n": "秦时明月2", "v": "1784"
|
||||
}, {"n": "文明与征服", "v": "7071"}, {"n": "战双:帕弥什", "v": "4133"}, {
|
||||
"n": "大航海时代:海上霸主", "v": "6929"
|
||||
}, {"n": "剑网1:归来", "v": "7361"}, {"n": "绝区零", "v": "7711"}, {
|
||||
"n": "黑色沙漠手游", "v": "7287"
|
||||
}, {"n": "虎牙吃鸡", "v": "7465"}, {"n": "一剑斩仙", "v": "6843"}, {
|
||||
"n": "传奇天下", "v": "6927"
|
||||
}, {"n": "斗斗堂", "v": "7133"}, {"n": "斗罗大陆", "v": "6119"}, {
|
||||
"n": "天谕手游", "v": "5925"
|
||||
}, {"n": "坎公骑冠剑", "v": "6641"}, {"n": "最终幻想觉醒", "v": "2721"}, {
|
||||
"n": "神将三国", "v": "6621"
|
||||
}, {"n": "灌篮高手正版授权手游", "v": "5399"}, {"n": "剑与家园", "v": "2838"}, {
|
||||
"n": "极无双2", "v": "7825"
|
||||
}, {"n": "光明大陆", "v": "2832"}, {"n": "荒野行动", "v": "3084"}, {
|
||||
"n": "战斗法则", "v": "9513"
|
||||
}, {"n": "疯狂原始人", "v": "4619"}, {"n": "逆战手游", "v": "7575"}, {
|
||||
"n": "石器时代:觉醒", "v": "9159"
|
||||
}]
|
||||
}], "8": [{
|
||||
"key": "cateId",
|
||||
"name": "分类",
|
||||
"init": "1663",
|
||||
"value": [{"n": "星秀", "v": "1663"}, {"n": "户外", "v": "2165"}, {
|
||||
"n": "一起看", "v": "2135"
|
||||
}, {"n": "二次元", "v": "2633"}, {"n": "虚拟偶像", "v": "6055"}, {"n": "旅游", "v": "6791"}, {
|
||||
"n": "放映厅", "v": "6245"
|
||||
}, {"n": "娱乐天地", "v": "100022"}, {"n": "交友", "v": "4079"}, {
|
||||
"n": "组队", "v": "5367"
|
||||
}, {"n": "吃喝玩乐", "v": "100044"}, {"n": "原创", "v": "6861"}, {
|
||||
"n": "虎牙文化", "v": "4089"
|
||||
}, {"n": "体育", "v": "2356"}, {"n": "虎牙地方", "v": "5123"}, {"n": "颜值", "v": "2168"}, {
|
||||
"n": "科技", "v": "2408"
|
||||
}, {"n": "音乐", "v": "3793"}, {"n": "趣分享", "v": "5883"}, {"n": "一起买", "v": "7759"}, {
|
||||
"n": "派对", "v": "7785"
|
||||
}]
|
||||
}],
|
||||
};
|
||||
if (this.customArea.length > 0) {
|
||||
const filterCfg = this.customArea.split('#');
|
||||
this.classes.unshift({"type_id": "custom", "type_name": "自选"});
|
||||
const filterList = _.map(filterCfg, (it) => {
|
||||
const filterKv = it.split(',');
|
||||
return {
|
||||
n: filterKv[1], v: filterKv[0],
|
||||
};
|
||||
});
|
||||
const defKey = filterList[0];
|
||||
this.filterObj["custom"] = [{"key": "cateId", "name": "分类", "init": defKey.v, "value": filterList}];
|
||||
}
|
||||
if (this.isJustLive) {
|
||||
// key-value映射修改
|
||||
const filterKeys = Object.keys(this.filterObj);
|
||||
for (const filterKey of filterKeys) {
|
||||
const filterItem = this.filterObj[filterKey];
|
||||
for (const typeItem of filterItem) {
|
||||
typeItem.value = _.map(typeItem.value, (it) => {
|
||||
// 修改默认值
|
||||
if (it.v === typeItem.init) {
|
||||
typeItem.init = it.n;
|
||||
}
|
||||
return {
|
||||
n: it.n, v: it.n,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
this.classes.unshift({"type_id": "home", "type_name": "首页"});
|
||||
}
|
||||
|
||||
async setHome(filter) {
|
||||
await this.setClasses()
|
||||
await this.setFilterObj()
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
if (this.isJustLive) {
|
||||
for (const it of obj["data"]) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = it["roomId"]
|
||||
vodShort.vod_name = it["ownerName"] + it["roomName"]
|
||||
vodShort.vod_pic = it["roomPic"]
|
||||
vodShort.vod_remarks = it["categoryName"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
|
||||
} else {
|
||||
for (const it of obj.data.datas) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = it["profileRoom"]
|
||||
vodShort.vod_name = it["nick"] + it["introduction"]
|
||||
vodShort.vod_pic = it["screenshot"]
|
||||
vodShort.vod_remarks = it["gameFullName"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
getPlayUrlData(streamInfo, ratio) {
|
||||
const hlsUrl = streamInfo["sHlsUrl"] + '/' + streamInfo["sStreamName"] + '.' + streamInfo["sHlsUrlSuffix"];
|
||||
const srcAntiCode = unescape(streamInfo["sHlsAntiCode"]);
|
||||
let codeList = srcAntiCode.split('&');
|
||||
codeList = codeList.filter(code => code !== '');
|
||||
let cryptoInfo = {};
|
||||
for (const code of codeList) {
|
||||
const [k, v] = code.split('=');
|
||||
cryptoInfo[k] = v;
|
||||
}
|
||||
const fm = Utils.unquote(cryptoInfo["fm"]);
|
||||
const fmDecoded = Utils.base64Decode(fm);
|
||||
const hashPrefix = fmDecoded.split('_')[0];
|
||||
const ctype = cryptoInfo["ctype"] || '';
|
||||
const txyp = cryptoInfo["txyp"] || '';
|
||||
const fs = cryptoInfo.fs || '';
|
||||
const t = cryptoInfo.t || '';
|
||||
const u = 1463993859134;
|
||||
const curTime = Date.now();
|
||||
const seqid = Math.floor(curTime + u);
|
||||
const wsTime = (Math.floor(curTime / 1e3) + 3600).toString(16);
|
||||
const v0 = seqid + '|' + ctype + '|' + t;
|
||||
const v1 = Utils.md5Encode(v0);
|
||||
const v2 = hashPrefix + '_' + u + '_' + streamInfo["sStreamName"] + '_' + v1 + '_' + wsTime;
|
||||
const hash = Utils.md5Encode(v2);
|
||||
// ratio = ""
|
||||
const purl = `${hlsUrl}?wsSecret=${hash}&wsTime=${wsTime}&seqid=${seqid}&ctype=${ctype}&ver=1&txyp=${txyp}&fs=${fs}&ratio=${ratio}&u=${u}&t=${t}&sv=2107230339`;
|
||||
return {
|
||||
cdnType: streamInfo["sCdnType"], playUrl: purl,
|
||||
};
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(liveInfo, streamInfoList, bitInfoList) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = liveInfo["introduction"] ?? liveInfo["sIntroduction"]
|
||||
vodDetail.vod_pic = liveInfo["screenshot"] ?? liveInfo["sScreenshot"]
|
||||
vodDetail.vod_remarks = liveInfo["gameFullName"] ?? liveInfo["sGameFullName"]
|
||||
vodDetail.type_name = liveInfo["gameFullName"] ?? liveInfo["sGameFullName"]
|
||||
vodDetail.vod_director = liveInfo["nick"] ?? liveInfo["sNick"]
|
||||
vodDetail.vod_content = liveInfo["activityCount"] ?? liveInfo["lActivityCount"]
|
||||
vodDetail.vod_content = vodDetail.vod_content + '人在线'
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
for (const streamInfo of streamInfoList) {
|
||||
let vodItems = []
|
||||
for (const bitinfo of bitInfoList) {
|
||||
let format_name = this.huYaPlayForamtObj[streamInfo["sCdnType"]]
|
||||
if (format_name === bitinfo["sDisplayName"]) {
|
||||
const urlData = this.getPlayUrlData(streamInfo, bitinfo["iBitRate"]);
|
||||
vod_play_from_list.push(format_name)
|
||||
vodItems.push("虎牙直播" + '$' + urlData["playUrl"])
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc(vodData, playData) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = vodData["data"]["roomName"]
|
||||
vodDetail.vod_director = vodData["data"]["ownerName"]
|
||||
vodDetail.vod_pic = vodData["data"]["roomPic"]
|
||||
vodDetail.vod_remarks = vodData["data"]["categoryName"]
|
||||
vodDetail.vod_content = vodData["data"]["online"] + "人在线"
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
for (const key of Object.keys(this.livePlayForamtObj)) {
|
||||
let vodItems = []
|
||||
if (playData.data.hasOwnProperty(key)) {
|
||||
vod_play_from_list.push(this.livePlayForamtObj[key])
|
||||
vodItems.push("JustLive" + '$' + playData["data"][key])
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
}
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch(data) {
|
||||
let vod_list = [];
|
||||
for (const vod of data.response['3']["docs"]) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = vod["room_id"]
|
||||
vodShort.vod_name = vod["game_nick"] + vod["game_introduction"]
|
||||
vodShort.vod_pic = vod["game_screenshot"]
|
||||
vodShort.vod_remarks = vod["game_name"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
if (pg <= 0 || typeof pg == 'undefined') pg = 1;
|
||||
let url = '';
|
||||
let data = {}
|
||||
if (this.isJustLive) {
|
||||
if (tid === 'home') {
|
||||
url = this.siteUrl + '/api/live/getRecommendByPlatform?platform=huya&size=20&page=' + pg;
|
||||
} else {
|
||||
url = this.siteUrl + '/api/live/getRecommendByPlatformArea?platform=huya&size=20&area=' + extend.cateId + '&page=' + pg;
|
||||
}
|
||||
data = JSON.parse(await this.fetch(url, null, this.getHeader()));
|
||||
} else {
|
||||
if (tid === 'home') {
|
||||
url = this.siteUrl + '/cache.php?m=LiveList&do=getLiveListByPage&tagAll=1&page=' + pg;
|
||||
} else {
|
||||
url = this.siteUrl + '/cache.php?m=LiveList&do=getLiveListByPage&gameId=' + extend["cateId"] + '&tagAll=0&page=' + pg;
|
||||
}
|
||||
data = JSON.parse(await this.fetch(url, null, this.getHeader()));
|
||||
}
|
||||
this.vodList = await this.parseVodShortListFromJson(data)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let liveInfo = null;
|
||||
let streamInfoList = null;
|
||||
if (this.isJustLive) {
|
||||
await this.jadeLog.debug("JustLive", true)
|
||||
const vodInfo = await this.fetch(this.siteUrl + `/api/live/getRoomInfo?platform=huya&roomId=${id}`, null, this.getHeader())
|
||||
const playInfo = await this.fetch(this.siteUrl + `/api/live/getRealUrl?platform=huya&roomId=${id}`, null, this.getHeader())
|
||||
const vodData = JSON.parse(vodInfo);
|
||||
const playData = JSON.parse(playInfo)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc(vodData, playData)
|
||||
} else {
|
||||
await this.jadeLog.debug("虎牙直播", true)
|
||||
const resp = await this.fetch('https://mp.huya.com/cache.php?m=Live&do=profileRoom&roomid=' + id, null, this.getHeader());
|
||||
const data = JSON.parse(resp);
|
||||
liveInfo = data.data["liveData"];
|
||||
let bitInfo = JSON.parse(liveInfo["bitRateInfo"])
|
||||
streamInfoList = data.data.stream["baseSteamInfoList"];
|
||||
this.vodDetail = await this.parseVodDetailfromJson(liveInfo, streamInfoList, bitInfo)
|
||||
}
|
||||
|
||||
/** 网页链接
|
||||
*
|
||||
* await this.jadeLog.debug("虎牙直播",true)
|
||||
* const headers = {
|
||||
* 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': Utils.MOBILEUA,
|
||||
* };
|
||||
* let content = await this.fetch('https://m.huya.com/' + id, null, headers);
|
||||
* let liveData = JSON.parse(Utils.getStrByRegex(/<script> window.HNF_GLOBAL_INIT = (.*?)<\/script>/, content))
|
||||
* const vodData = liveData["roomInfo"];
|
||||
* liveInfo = vodData["tLiveInfo"];
|
||||
* streamInfoList = vodData["tLiveInfo"]["tLiveStreamInfo"]["vStreamInfo"]["value"]
|
||||
* let bitInfoList = vodData["tLiveInfo"]["tLiveStreamInfo"]["vBitRateInfo"]["value"]
|
||||
* this.vodDetail = await this.parseVodDetailfromJson(liveInfo, streamInfoList, bitInfoList)
|
||||
* */
|
||||
|
||||
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
const resp = await this.fetch('https://search.cdn.huya.com/?m=Search&do=getSearchContent&q=' + wd + '&uid=0&v=4&typ=-5&livestate=0&rows=40&start=0', null, this.getHeader());
|
||||
const data = JSON.parse(resp);
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch(data)
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new HuyaSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
319
cat/tjs/js/ikanbot.js
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* @File : ikanbot.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/15 10:32
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
|
||||
import {Spider} from "./spider.js";
|
||||
import {load, _} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
|
||||
function _0xf746(_0xbb40c4, _0x1cb776) {
|
||||
const _0x45e084 = _0x45e0();
|
||||
return _0xf746 = function (_0xf74696, _0x4d32af) {
|
||||
_0xf74696 = _0xf74696 - 0x1a8;
|
||||
let _0xcbfa28 = _0x45e084[_0xf74696];
|
||||
return _0xcbfa28;
|
||||
}, _0xf746(_0xbb40c4, _0x1cb776);
|
||||
}
|
||||
|
||||
function _0x45e0() {
|
||||
const _0x58b10c = ['1580630GngmmA', '117uvwflw', 'join', 'current_id', '565448Apkhig', '23092JwmytW', '707152yowhOv', 'getElementById', '855936CGaczt', 'length', '2966831GCGpvn', '611266nfcTEf', 'value', 'substring'];
|
||||
_0x45e0 = function () {
|
||||
return _0x58b10c;
|
||||
};
|
||||
return _0x45e0();
|
||||
}
|
||||
|
||||
(function (_0x27923d, _0x43d7fc) {
|
||||
const _0x439396 = _0xf746, _0x30f164 = _0x27923d();
|
||||
while (!![]) {
|
||||
try {
|
||||
const _0xa560eb = -parseInt(_0x439396(0x1b4)) / 0x1 + parseInt(_0x439396(0x1ad)) / 0x2 + -parseInt(_0x439396(0x1b1)) / 0x3 * (-parseInt(_0x439396(0x1b5)) / 0x4) + -parseInt(_0x439396(0x1b0)) / 0x5 + parseInt(_0x439396(0x1aa)) / 0x6 + parseInt(_0x439396(0x1ac)) / 0x7 + parseInt(_0x439396(0x1a8)) / 0x8;
|
||||
if (_0xa560eb === _0x43d7fc) break; else _0x30f164['push'](_0x30f164['shift']());
|
||||
} catch (_0x3ae316) {
|
||||
_0x30f164['push'](_0x30f164['shift']());
|
||||
}
|
||||
}
|
||||
}(_0x45e0, 0x4a3d9));
|
||||
|
||||
function get_tks(play_id, e_token) {
|
||||
const _0xf07220 = _0xf746;
|
||||
let _0x35162d = play_id, _0xf25678 = e_token;
|
||||
if (!_0x35162d || !_0xf25678) return;
|
||||
let _0x3882a3 = _0x35162d['length'], _0x52a097 = _0x35162d[_0xf07220(0x1af)](_0x3882a3 - 0x4, _0x3882a3),
|
||||
_0x2d9d1b = [];
|
||||
for (let _0x570711 = 0x0; _0x570711 < _0x52a097[_0xf07220(0x1ab)]; _0x570711++) {
|
||||
let _0x23e537 = parseInt(_0x52a097[_0x570711]), _0x48b93d = _0x23e537 % 0x3 + 0x1;
|
||||
_0x2d9d1b[_0x570711] = _0xf25678[_0xf07220(0x1af)](_0x48b93d, _0x48b93d + 0x8), _0xf25678 = _0xf25678[_0xf07220(0x1af)](_0x48b93d + 0x8, _0xf25678[_0xf07220(0x1ab)]);
|
||||
}
|
||||
return _0x2d9d1b[_0xf07220(0x1b2)]('');
|
||||
}
|
||||
|
||||
class IKanBotSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://v.ikanbot.com"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🤖┃爱看机器人┃🤖"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "爱看机器人"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "ikanbot"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async spiderInit(inReq = null) {
|
||||
if (inReq !== null) {
|
||||
this.jsBase = await js2Proxy(inReq, "img", this.getHeader());
|
||||
} else {
|
||||
this.jsBase = await js2Proxy(true, this.siteType, this.siteKey, 'img/', this.getHeader());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = [];
|
||||
let VodShortElements = $($("[class=\"row list-wp\"]")).find("a")
|
||||
for (const vodShortElement of VodShortElements) {
|
||||
let vodShort = new VodShort()
|
||||
let reElement = $(vodShortElement).find("img")[0]
|
||||
vodShort.vod_id = vodShortElement.attribs["href"]
|
||||
vodShort.vod_pic = this.jsBase + Utils.base64Encode(reElement.attribs["data-src"])
|
||||
vodShort.vod_name = reElement.attribs["alt"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
getChildren(detail, index) {
|
||||
try {
|
||||
return detail[index].children[0].data;
|
||||
} catch (e) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
const detail = $('div.detail > .meta');
|
||||
let vodDetail = new VodDetail();
|
||||
vodDetail.vod_pic = this.jsBase + Utils.base64Encode($('div.item-root > img')[0].attribs['data-src'])
|
||||
vodDetail.vod_name = this.getChildren(detail, 0)
|
||||
vodDetail.vod_year = this.getChildren(detail, 1)
|
||||
vodDetail.vod_area = this.getChildren(detail, 3);
|
||||
vodDetail.vod_actor = this.getChildren(detail, 4);
|
||||
|
||||
let id = Utils.getStrByRegex(/<input type="hidden" id="current_id" value="(.*?)"/, $.html())
|
||||
let token = Utils.getStrByRegex(/<input type="hidden" id="e_token" value="(.*?)"/, $.html())
|
||||
let mtype = Utils.getStrByRegex(/<input type="hidden" id="mtype" value="(.*?)"/, $.html())
|
||||
let params = {
|
||||
"videoId": id, "mtype": mtype, "token": get_tks(id, token),
|
||||
}
|
||||
let content = await this.fetch(this.siteUrl + '/api/getResN', params, this.getHeader())
|
||||
|
||||
const list = JSON.parse(content)["data"]["list"];
|
||||
let playlist = {};
|
||||
|
||||
let index = 0
|
||||
let form_list = []
|
||||
for (const l of list) {
|
||||
const flagData = JSON.parse(l["resData"]);
|
||||
for (const f of flagData) {
|
||||
index = index + 1
|
||||
const from = f.flag;
|
||||
const urls = f.url;
|
||||
if (!from || !urls) continue;
|
||||
if (playlist[from]) continue;
|
||||
form_list.push(`线路${index}`)
|
||||
playlist[from] = urls;
|
||||
}
|
||||
}
|
||||
vodDetail.vod_play_from = form_list.join('$$$');
|
||||
vodDetail.vod_play_url = _.values(playlist).join('$$$');
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let vod_list = []
|
||||
const items = $('div.media > div.media-left > a');
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort();
|
||||
const img = $(item).find('img:first')[0];
|
||||
vodShort.vod_id = item.attribs.href
|
||||
vodShort.vod_name = img.attribs.alt
|
||||
vodShort.vod_pic = this.jsBase + Utils.base64Encode(img.attribs['data-src'])
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let html = await this.fetch(this.siteUrl + "/category", null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
let classElements = $($($("[class=\"row visible-xs-block visible-sm-block\"]")).find("li")).find("a")
|
||||
for (const classElement of classElements) {
|
||||
this.classes.push({"type_name": $(classElement).text(), "type_id": classElement.attribs["href"]})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const class_dic of this.classes.slice(1, 9)) {
|
||||
let type_id = class_dic["type_id"]
|
||||
if (type_id.indexOf("category") === -1 || type_id.indexOf(",") > -1) {
|
||||
let type_url = type_id.split(",").slice(-1)[0]
|
||||
let html = await this.fetch(this.siteUrl + type_url, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
let containerElement = $("[class=\"row visible-xs-block visible-sm-block\"]")
|
||||
let filterElements = containerElement.find("[class=\"nav nav-pills\"]").find("a")
|
||||
let value_list = []
|
||||
if (type_id.indexOf(",") > -1) {
|
||||
value_list.push({"n": "全部", "v": type_id.split(",")[0]})
|
||||
|
||||
}
|
||||
let extend_dic = {
|
||||
"key": type_id, "name": $(containerElement.find("h5")).text(), "value": value_list
|
||||
}
|
||||
for (const filterElement of filterElements) {
|
||||
value_list.push({"n": $(filterElement).text(), "v": filterElement.attribs["href"]})
|
||||
}
|
||||
if (value_list.length > 0) {
|
||||
this.filterObj[type_id] = [extend_dic]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let categoryUrl = (this.siteUrl + (extend[tid] || tid.split(",")[0]))
|
||||
let update_page = false
|
||||
if (categoryUrl.indexOf("html") > -1) {
|
||||
categoryUrl = categoryUrl.replace('.html', pg > 1 ? `-p-${pg}.html` : '.html');
|
||||
} else {
|
||||
categoryUrl = categoryUrl + `?p=${pg}`
|
||||
update_page = true
|
||||
|
||||
}
|
||||
await this.jadeLog.debug(`分类URL:${categoryUrl}`)
|
||||
let html = await this.fetch(categoryUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
let pageDoc = $('div.page-more > a:contains(下一页)')
|
||||
if (update_page) {
|
||||
this.page = parseInt(pageDoc[0].attribs["href"].split("p=")[1])
|
||||
}
|
||||
const hasMore = pageDoc.length > 0;
|
||||
this.limit = 24
|
||||
this.count = hasMore ? parseInt(pg) + 1 : parseInt(pg);
|
||||
this.total = this.limit * this.count
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let html = await this.fetch(this.siteUrl + id, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html);
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
const html = await this.fetch(this.siteUrl + '/search?q=' + wd, null, this.getHeader());
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
this.playUrl = id
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
let spider = new IKanBotSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
274
cat/tjs/js/jable.js
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* @File : jable.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/4 9:44
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
class JableTVSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://jable.tv"
|
||||
this.cookie = ""
|
||||
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "Jable"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🔞┃Jable┃🔞"
|
||||
}
|
||||
getJSName() {
|
||||
return "jable"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
// let header = super.getHeader()
|
||||
let header = {}
|
||||
header["User-Agent"] = "PostmanRuntime/7.36.3"
|
||||
header["Host"] = "jable.tv"
|
||||
// header["Postman-Token"] = "33290483-3c8d-413f-a160-0d3aea9e6f95"
|
||||
return header
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml(this.siteUrl)
|
||||
let navElements = $("[class=\"title-box\"]")
|
||||
let defaultTypeIdElements = $("div.row")
|
||||
for (const navElement of $(defaultTypeIdElements[0]).find("a")) {
|
||||
let type_name = $(navElement).text()
|
||||
let type_id = navElement.attribs.href
|
||||
if (type_id.indexOf(this.siteUrl) > -1) {
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
navElements = navElements.slice(1, 9)
|
||||
defaultTypeIdElements = defaultTypeIdElements.slice(1, 9)
|
||||
for (let i = 0; i < navElements.length; i++) {
|
||||
let typeId = $(defaultTypeIdElements[i]).find("a")[0].attribs["href"]
|
||||
this.classes.push(this.getTypeDic("标签", typeId));
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
async getSortFilter($) {
|
||||
let sortElements = $("[class=\"sorting-nav\"]").find("a")
|
||||
let extend_dic = {"name": "排序", "key": "sort", "value": []}
|
||||
for (const sortElement of sortElements) {
|
||||
let typeId = sortElement.attribs["data-parameters"].split("sort_by:")[1]
|
||||
let typeName = $(sortElement).text()
|
||||
extend_dic["value"].push({"n": typeName, "v": typeId})
|
||||
}
|
||||
return extend_dic
|
||||
}
|
||||
|
||||
async getFilter($, index, type_id, type_name) {
|
||||
let extend_list = []
|
||||
if (index < 4) {
|
||||
let extend_dic = {"name": type_name, "key": "type", "value": []}
|
||||
let type_seletc_list = ["div.img-box > a", "[class=\"horizontal-img-box ml-3 mb-3\"] > a", "", "sort"]
|
||||
let type_id_select_list = ["div.absolute-center > h4", "div.detail"]
|
||||
let default$ = await this.getHtml(type_id)
|
||||
for (const element of default$(type_seletc_list[index])) {
|
||||
let typeId = element.attribs["href"]
|
||||
let typeName = $($(element).find(type_id_select_list[index])).text().replaceAll("\t", "").replaceAll("\n", '').replaceAll(" ", "");
|
||||
extend_dic["value"].push({"n": typeName, "v": typeId})
|
||||
}
|
||||
if (extend_dic.value.length > 0) {
|
||||
extend_list.push(extend_dic)
|
||||
//排序
|
||||
let sortDetail$ = await this.getHtml(extend_dic["value"][0]["v"])
|
||||
let sort_extend_dic = await this.getSortFilter(sortDetail$)
|
||||
if (sort_extend_dic.value.length > 0) {
|
||||
extend_list.push(sort_extend_dic)
|
||||
}
|
||||
} else {
|
||||
//排序
|
||||
let sort_extend_dic = await this.getSortFilter(default$)
|
||||
if (sort_extend_dic.value.length > 0) {
|
||||
extend_list.push(sort_extend_dic)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
let defaultTypeIdElements = $("div.row").slice(1, 9)
|
||||
let navElements = $("[class=\"title-box\"]").slice(1, 9)
|
||||
for (let i = 0; i < navElements.length; i++) {
|
||||
let extend_dic = {"name": $($(navElements[i]).find("h2")).text(), "key": "type", "value": []}
|
||||
for (const filterElement of $(defaultTypeIdElements[i]).find("a")) {
|
||||
let filter_type_id = filterElement.attribs.href
|
||||
if (filter_type_id.indexOf(this.siteUrl) > -1) {
|
||||
extend_dic["value"].push({"n": $(filterElement).text(), "v": filter_type_id})
|
||||
}
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
|
||||
let sortDetail$ = await this.getHtml(type_id)
|
||||
let sort_extend_dic = await this.getSortFilter(sortDetail$)
|
||||
if (sort_extend_dic.value.length > 0) {
|
||||
extend_list.push(sort_extend_dic)
|
||||
}
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
let $ = await this.getHtml(this.siteUrl)
|
||||
let classes = this.classes.slice(1)
|
||||
for (let i = 0; i < classes.length; i++) {
|
||||
let type_name = classes[i].type_name
|
||||
let type_id = classes[i].type_id
|
||||
// if (type_id.indexOf("models") > 1) {
|
||||
// type_id = `https://jable.tv/models/?mode=async&function=get_block&block_id=list_models_models_list&sort_by=total_videos&_=${new Date().getTime()}`
|
||||
// }
|
||||
let extend_list = await this.getFilter($, i, type_id, type_name)
|
||||
if (extend_list.length > 1 && i < 4) {
|
||||
type_id = extend_list[0]["value"][0]["v"]
|
||||
this.classes[i + 1] = this.getTypeDic(type_name, type_id)
|
||||
}
|
||||
this.filterObj[type_id] = extend_list
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("div.video-img-box")
|
||||
for (const element of vodElements) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_pic = $(element).find("img").attr("data-src");
|
||||
let url = $(element).find("a").attr("href");
|
||||
vodShort.vod_id = url.split("/")[4];
|
||||
vodShort.vod_name = url.split("/")[4];
|
||||
let remarks_list = $($(element).find("[class=\"sub-title\"]")).text().split("\n")
|
||||
if (remarks_list.length > 1){
|
||||
vodShort.vod_remarks = remarks_list[1].replaceAll(" ", "").replaceAll("\t", "")
|
||||
}else{
|
||||
vodShort.vod_remarks = "精选"
|
||||
}
|
||||
if (!_.isEmpty(vodShort.vod_pic) && vodShort.vod_remarks !== "[限時優惠]只需1元即可無限下載"){
|
||||
vod_list.push(vodShort);
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail();
|
||||
let leftElement = $("[class=\"header-left\"]")
|
||||
vodDetail.vod_name = $($(leftElement).find("h4")).text();
|
||||
vodDetail.vod_pic = Utils.getStrByRegex(/<video poster="(.*?)" id=/, $.html())
|
||||
vodDetail.vod_year = $($("[class=\"inactive-color\"]")).text()
|
||||
let episodeName = $($("[class=\"header-right d-none d-md-block\"] > h6")).text().replaceAll("\n", "").replaceAll("●","")
|
||||
let vodItems = []
|
||||
let episodeUrl = Utils.getStrByRegex(/var hlsUrl = '(.*?)';/, $.html())
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
let vod_play_list = []
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
let vod_play_from_list = ["Jable"]
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml(this.siteUrl)
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + "/videos/" + id + "/")
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let extend_type = extend["type"] ?? tid
|
||||
let sort_by = extend["sort"] ?? "video_viewed"
|
||||
this.limit = 24
|
||||
let cateUrl;
|
||||
this.total = 0
|
||||
this.count = 0
|
||||
if (tid.indexOf("latest-updates") > 1) {
|
||||
cateUrl = `https://jable.tv/latest-updates/?mode=async&function=get_block&block_id=list_videos_latest_videos_list&sort_by=post_date&from=${pg}&_=1709730132217`
|
||||
} else {
|
||||
cateUrl = extend_type + `/${pg}/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=${sort_by}&_=${new Date().getTime()}`
|
||||
}
|
||||
let $ = await this.getHtml(cateUrl);
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
let page = $($("[class=\"page-item\"]").slice(-1)[0]).text()
|
||||
if (page.indexOf("最後") > -1) {
|
||||
} else {
|
||||
if (parseInt(page) === this.page || _.isEmpty(page)) {
|
||||
await this.jadeLog.debug("分类页面到底了")
|
||||
this.total = this.page
|
||||
this.count = this.page
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let searchUrl = this.siteUrl + `/search/${wd}/`
|
||||
let $ = await this.getHtml(searchUrl)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new JableTVSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
295
cat/tjs/js/jiafeimao.js
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* @File : jiafeimao.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/24 9:15
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 加菲猫 (已失效)
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
class JiaFeiMaoSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://jfmys.app"
|
||||
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "加菲猫"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return `🐈┃加菲猫┃🐈`
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "jiafeimao"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
getPic(url){
|
||||
if (url.indexOf("http:") > -1 || url.indexOf("https:") > -1){
|
||||
return url
|
||||
}else{
|
||||
return this.siteUrl + url
|
||||
}
|
||||
}
|
||||
parseVodShortFromElement($, element) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = Utils.getStrByRegex(/id\/(.*?)\//,$(element).find("a")[0].attribs.href)
|
||||
vodShort.vod_name = $(element).find("a")[0].attribs.title
|
||||
vodShort.vod_pic = this.getPic($(element).find("img")[0].attribs["data-src"])
|
||||
vodShort.vod_remarks = $($(element).find("[class=\"v-tips\"]")).html()
|
||||
return vodShort
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $(".icon > .container").find("[class=\"imain clearfix\"]").find("li")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = this.parseVodShortFromElement($, vodElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"tv-list clearfix\"]").find("li")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = this.parseVodShortFromElement($, vodElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = $($("[class=\"iptit\"]").find("h3")).html().split(" ")[0]
|
||||
vodDetail.vod_content = $($("[class=\"idetail container\"]").find("[class=\"infor_intro\"]")).text()
|
||||
let vodPlayElements = $("[class=\"fjcon\"]")
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
let playFormatElemets = $($(vodPlayElements).find("[class=\"fjtop clearfix\"]")).find("a")
|
||||
let playUrlElements = $(vodPlayElements).find("[class=\"xjn_ul play-list\"]")
|
||||
for (let i = 0; i < playFormatElemets.length; i++) {
|
||||
let playFormatElement = playFormatElemets[i]
|
||||
vod_play_from_list.push("线路" +( i+1).toString())
|
||||
let vodItems = []
|
||||
for (const playUrlElement of $(playUrlElements[i]).find("a")) {
|
||||
let episodeName = $(playUrlElement).text()
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
parseVodDetail(vod_data) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = vod_data["vod_name"]
|
||||
vodDetail.vod_pic = this.getPic(vod_data["vod_pic"])
|
||||
vodDetail.vod_remarks = vod_data["vod_remarks"]
|
||||
vodDetail.vod_area = vod_data["vod_area"]
|
||||
vodDetail.vod_year = vod_data["vod_year"]
|
||||
vodDetail.vod_actor = vod_data["vod_actor"]
|
||||
vodDetail.vod_director = vod_data["vod_director"]
|
||||
vodDetail.vod_content = vod_data["vod_content"].replaceAll("<p>","").replaceAll("</p>","")
|
||||
let vod_play_from = []
|
||||
for (let i = 0; i < vod_data["vod_play_from"].split("$$$").length; i++) {
|
||||
vod_play_from.push("线路"+(i+1).toString())
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from.join("$$$")
|
||||
vodDetail.vod_play_url = vod_data["vod_play_url"]
|
||||
vodDetail.type_name = vod_data["type_name"]
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let vodDetail;
|
||||
let vod_data_list = obj["list"]
|
||||
if (vod_data_list.length > 0) {
|
||||
let vod_data = vod_data_list[0]
|
||||
vodDetail = this.parseVodDetail(vod_data)
|
||||
}
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"tv-bd search-list\"]").find("[class=\"item clearfix\"]")
|
||||
for (const vodElement of vodElements){
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = Utils.getStrByRegex(/id\/(.*?).html/, $($(vodElement).find("[class=\"s_tit\"]")).find("a")[0].attribs.href)
|
||||
vodShort.vod_name = $($($(vodElement).find("[class=\"s_tit\"]")).find("a")).text()
|
||||
vodShort.vod_pic = this.getPic($(vodElement).find("img")[0].attribs.src)
|
||||
vodShort.vod_remarks = $($(vodElement).find("[class=\"s_score\"]")).text()
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let content = $($("[class=\"container\"]").find("script")).html()
|
||||
let navContent = Utils.getStrByRegex(/document.write\('(.*?);/, content)
|
||||
for (const navElement of $(navContent).find("a")) {
|
||||
let type_id = navElement.attribs["href"]
|
||||
let type_name = $(navElement).text()
|
||||
if (type_id !== "/" && type_name !== "专题" && type_name !== "站长模板") {
|
||||
this.classes.push(this.getTypeDic(type_name, Utils.getStrByRegex(/id\/(.*?).html/, type_id)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async getFilter($) {
|
||||
let elements = $($("[class=\"container\"]").find("[class=\"select_list clearfix\"]")).find("li")
|
||||
let extend_list = []
|
||||
let key_value_dic = {
|
||||
"分类": /id\/(.*?).html/,
|
||||
"地区": /area\/(.*?)\//,
|
||||
"年份": /year\/(.*?).html/,
|
||||
"字母": /letter\/(.*?).html/,
|
||||
"排序": /by\/(.*?)\//,
|
||||
}
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let element = elements[i]
|
||||
let name = $($($(element).find("[class=\"v-tit\"]"))).text().replaceAll(":", "")
|
||||
if (name !== "频道") {
|
||||
let extend_dic = {"key": (i + 1).toString(), "name": name, "value": []}
|
||||
for (const ele of $(element).find("a")) {
|
||||
let type_id = Utils.getStrByRegex(key_value_dic[name], ele.attribs.href)
|
||||
if (_.isEmpty(type_id)) {
|
||||
type_id = "/"
|
||||
}
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": decodeURIComponent(type_id)})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
}
|
||||
|
||||
let sortElments = $("[class=\"v-hd clearfix\"]")
|
||||
let extend_dic = {"key": (elements.length + 1).toString(), "name": "排序", "value": []}
|
||||
extend_dic["value"].push({"n": "全部", "v": "/"})
|
||||
for (const ele of $(sortElments).find("a")) {
|
||||
let type_id = Utils.getStrByRegex(key_value_dic["排序"], ele.attribs.href)
|
||||
if (_.isEmpty(type_id)) {
|
||||
type_id = "/"
|
||||
}
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": type_id})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const class_dic of this.classes) {
|
||||
let type_id = class_dic["type_id"]
|
||||
if (type_id !== "最近更新") {
|
||||
let $ = await this.getHtml(this.siteUrl + `/index.php/vod/show/id/${type_id}.html`)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
getExtend(extend, key, value) {
|
||||
if (extend[key] !== undefined && extend[key] !== "/") {
|
||||
return value + "/" + extend[key] + "/"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let area = this.getExtend(extend, "3", "area")
|
||||
let sort = this.getExtend(extend, "6", "by")
|
||||
let id = this.getExtend(extend, "2", "id")
|
||||
let letter = this.getExtend(extend, "5", "letter")
|
||||
let year = this.getExtend(extend, "4", "year")
|
||||
if (_.isEmpty(id)) {
|
||||
id = "id/" + tid + "/"
|
||||
}
|
||||
let url = this.siteUrl + `/index.php/vod/show/${area}${sort}${id}${letter}${year}page/${pg}.html`
|
||||
let $ = await this.getHtml(url)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
|
||||
async setDetail(id) {
|
||||
let content = await this.fetch(this.siteUrl + "/api.php/provide/vod", {
|
||||
"ac": "detail", "ids": id
|
||||
}, this.getHeader())
|
||||
this.vodDetail = await this.parseVodDetailfromJson(JSON.parse(content))
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let $ = await this.getHtml(this.siteUrl + "/index.php/vod/search.html?wd="+wd)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new JiaFeiMaoSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
491
cat/tjs/js/jianpian.js
Normal file
@ -0,0 +1,491 @@
|
||||
/*
|
||||
* @File : jianpian.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/15 10:32
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 完成荐片所有功能(添加弹幕)
|
||||
*/
|
||||
|
||||
import {Spider} from "./spider.js";
|
||||
import {_, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
|
||||
class JianPianSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "http://api2.rinhome.com"
|
||||
// this.siteUrl = "https://ownjpykxttjzuhy.jiesiwa.com"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🌼┃荐片┃🌼"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "荐片"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "jianpian"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
return {
|
||||
"User-Agent": "jianpian-android/360",
|
||||
"JPAUTH": "y261ow7kF2dtzlxh1GS9EB8nbTxNmaK/QQIAjctlKiEv",
|
||||
"Referer": "www.jianpianapp.com"
|
||||
}
|
||||
}
|
||||
|
||||
async spiderInit(inReq=null) {
|
||||
if (inReq !== null){
|
||||
this.jsBase = await js2Proxy(inReq,"img",this.getHeader());
|
||||
}else{
|
||||
this.jsBase = await js2Proxy(true, this.siteType, this.siteKey, 'img/', this.getHeader());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
this.danmuStaus = true
|
||||
await this.spiderInit(null)
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromJson(data_list) {
|
||||
let vod_list = [];
|
||||
for (const data of data_list) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = data["id"]
|
||||
if (data["path"] !== undefined) {
|
||||
if (!this.catOpenStatus) {
|
||||
vodShort.vod_pic = data["path"] + "@Referer=www.jianpianapp.com@User-Agent=jianpian-version353@JPAUTH=y261ow7kF2dtzlxh1GS9EB8nbTxNmaK/QQIAjctlKiEv"
|
||||
} else {
|
||||
vodShort.vod_pic = this.jsBase + Utils.base64Encode(data["path"])
|
||||
}
|
||||
} else {
|
||||
if (!this.catOpenStatus) {
|
||||
vodShort.vod_pic = data["thumbnail"] + "@Referer=www.jianpianapp.com@User-Agent=jianpian-version353@JPAUTH=y261ow7kF2dtzlxh1GS9EB8nbTxNmaK/QQIAjctlKiE"
|
||||
|
||||
} else {
|
||||
vodShort.vod_pic = this.jsBase + Utils.base64Encode(data["thumbnail"])
|
||||
}
|
||||
}
|
||||
vodShort.vod_name = data["title"]
|
||||
if (this.catOpenStatus) {
|
||||
vodShort.vod_remarks = `评分:${data["score"]}`
|
||||
} else {
|
||||
if (data["playlist"] !== undefined) {
|
||||
vodShort.vod_remarks = `评分:${data["score"]}` + " " + data["playlist"]["title"]
|
||||
} else {
|
||||
vodShort.vod_remarks = `评分:${data["score"]}`
|
||||
}
|
||||
}
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
|
||||
objToList(list, key, split_value = "*") {
|
||||
let value_list = []
|
||||
for (const dic of list) {
|
||||
value_list.push(dic[key])
|
||||
}
|
||||
return value_list.join(split_value)
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let vodDetail = new VodDetail();
|
||||
vodDetail.vod_id = obj["id"]
|
||||
vodDetail.vod_year = obj["year"]["title"]
|
||||
vodDetail.vod_pic = this.jsBase + Utils.base64Encode(obj["thumbnail"])
|
||||
vodDetail.type_name = obj["category"][0]["title"]
|
||||
vodDetail.vod_name = obj["title"]
|
||||
vodDetail.vod_content = obj["description"]
|
||||
vodDetail.vod_area = obj["area"]["title"]
|
||||
vodDetail.vod_director = this.objToList(obj["directors"], "name")
|
||||
vodDetail.vod_actor = this.objToList(obj["actors"], "name")
|
||||
vodDetail.vod_remarks = "评分:" + obj["score"]
|
||||
let playKeyList = [{"btbo_downlist": "btbo"}, {"xunlei_downlist": "迅雷"}, {"m3u8_downlist": "m3u8"}, {"new_ftp_list": "new_ftp"}, {"new_m3u8_list": "new_m3u8"}]
|
||||
let playlist = {}
|
||||
let urlList = []
|
||||
for (const dic of playKeyList) {
|
||||
let key = Object.keys(dic)[0]
|
||||
let value = Object.values(dic)[0]
|
||||
if (obj[key].length > 0) {
|
||||
let url_str_list = []
|
||||
for (const dic of obj[key]) {
|
||||
url_str_list.push(dic["title"] + "$" + dic["url"])
|
||||
}
|
||||
|
||||
if (urlList.indexOf(url_str_list.join("#")) === -1) {
|
||||
urlList.push(url_str_list.join("#"))
|
||||
playlist[value] = url_str_list.join("#")
|
||||
} else {
|
||||
await this.jadeLog.warning(`key为:${key},播放链接重复,无需保存`)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
vodDetail.vod_play_url = _.values(playlist).join('$$$');
|
||||
vodDetail.vod_play_from = _.keys(playlist).join('$$$');
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
|
||||
async setClasses() {
|
||||
let type_name_list = ["全部", "电影", "电视剧", "动漫", "综艺"]
|
||||
let type_id_list = ["0", "1", "2", "3", "4"]
|
||||
for (let i = 0; i < type_name_list.length; i++) {
|
||||
let type_name = type_name_list[i]
|
||||
let type_id = type_id_list[i]
|
||||
this.classes.push({"type_name": type_name, "type_id": type_id})
|
||||
}
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
this.filterObj = {
|
||||
"0": [{
|
||||
"key": "area", "name": "地區", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "国产", "v": "1"
|
||||
}, {
|
||||
"n": "中国香港", "v": "3"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "6"
|
||||
}, {
|
||||
"n": "美国", "v": "5"
|
||||
}, {
|
||||
"n": "韩国", "v": "18"
|
||||
}, {
|
||||
"n": "日本", "v": "2"
|
||||
}]
|
||||
}, {
|
||||
"key": "year", "name": "年份", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "2024", "v": "119"
|
||||
}, {
|
||||
"n": "2023", "v": "153"
|
||||
}, {
|
||||
"n": "2022", "v": "101"
|
||||
}, {
|
||||
"n": "2021", "v": "118"
|
||||
}, {
|
||||
"n": "2020", "v": "16"
|
||||
}, {
|
||||
"n": "2019", "v": "7"
|
||||
}, {
|
||||
"n": "2018", "v": "2"
|
||||
}, {
|
||||
"n": "2017", "v": "3"
|
||||
}, {
|
||||
"n": "2016", "v": "22"
|
||||
}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{
|
||||
"n": "热门", "v": "hot"
|
||||
}, {
|
||||
"n": "更新", "v": "updata"
|
||||
}, {
|
||||
"n": "评分", "v": "rating"
|
||||
}]
|
||||
}], "1": [{
|
||||
"key": "area", "name": "地區", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "国产", "v": "1"
|
||||
}, {
|
||||
"n": "中国香港", "v": "3"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "6"
|
||||
}, {
|
||||
"n": "美国", "v": "5"
|
||||
}, {
|
||||
"n": "韩国", "v": "18"
|
||||
}, {
|
||||
"n": "日本", "v": "2"
|
||||
}]
|
||||
}, {
|
||||
"key": "year", "name": "年份", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "2024", "v": "119"
|
||||
}, {
|
||||
"n": "2023", "v": "153"
|
||||
}, {
|
||||
"n": "2022", "v": "101"
|
||||
}, {
|
||||
"n": "2021", "v": "118"
|
||||
}, {
|
||||
"n": "2020", "v": "16"
|
||||
}, {
|
||||
"n": "2019", "v": "7"
|
||||
}, {
|
||||
"n": "2018", "v": "2"
|
||||
}, {
|
||||
"n": "2017", "v": "3"
|
||||
}, {
|
||||
"n": "2016", "v": "22"
|
||||
}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{
|
||||
"n": "热门", "v": "hot"
|
||||
}, {
|
||||
"n": "更新", "v": "updata"
|
||||
}, {
|
||||
"n": "评分", "v": "rating"
|
||||
}]
|
||||
}], "2": [{
|
||||
"key": "area", "name": "地區", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "国产", "v": "1"
|
||||
}, {
|
||||
"n": "中国香港", "v": "3"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "6"
|
||||
}, {
|
||||
"n": "美国", "v": "5"
|
||||
}, {
|
||||
"n": "韩国", "v": "18"
|
||||
}, {
|
||||
"n": "日本", "v": "2"
|
||||
}]
|
||||
}, {
|
||||
"key": "year", "name": "年份", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "2024", "v": "119"
|
||||
}, {
|
||||
"n": "2023", "v": "153"
|
||||
}, {
|
||||
"n": "2022", "v": "101"
|
||||
}, {
|
||||
"n": "2021", "v": "118"
|
||||
}, {
|
||||
"n": "2020", "v": "16"
|
||||
}, {
|
||||
"n": "2019", "v": "7"
|
||||
}, {
|
||||
"n": "2018", "v": "2"
|
||||
}, {
|
||||
"n": "2017", "v": "3"
|
||||
}, {
|
||||
"n": "2016", "v": "22"
|
||||
}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{
|
||||
"n": "热门", "v": "hot"
|
||||
}, {
|
||||
"n": "更新", "v": "updata"
|
||||
}, {
|
||||
"n": "评分", "v": "rating"
|
||||
}]
|
||||
}], "3": [{
|
||||
"key": "area", "name": "地區", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "国产", "v": "1"
|
||||
}, {
|
||||
"n": "中国香港", "v": "3"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "6"
|
||||
}, {
|
||||
"n": "美国", "v": "5"
|
||||
}, {
|
||||
"n": "韩国", "v": "18"
|
||||
}, {
|
||||
"n": "日本", "v": "2"
|
||||
}]
|
||||
}, {
|
||||
"key": "year", "name": "年份", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "2024", "v": "119"
|
||||
}, {
|
||||
"n": "2023", "v": "153"
|
||||
}, {
|
||||
"n": "2022", "v": "101"
|
||||
}, {
|
||||
"n": "2021", "v": "118"
|
||||
}, {
|
||||
"n": "2020", "v": "16"
|
||||
}, {
|
||||
"n": "2019", "v": "7"
|
||||
}, {
|
||||
"n": "2018", "v": "2"
|
||||
}, {
|
||||
"n": "2017", "v": "3"
|
||||
}, {
|
||||
"n": "2016", "v": "22"
|
||||
}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{
|
||||
"n": "热门", "v": "hot"
|
||||
}, {
|
||||
"n": "更新", "v": "updata"
|
||||
}, {
|
||||
"n": "评分", "v": "rating"
|
||||
}]
|
||||
}], "4": [{
|
||||
"key": "area", "name": "地區", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "国产", "v": "1"
|
||||
}, {
|
||||
"n": "中国香港", "v": "3"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "6"
|
||||
}, {
|
||||
"n": "美国", "v": "5"
|
||||
}, {
|
||||
"n": "韩国", "v": "18"
|
||||
}, {
|
||||
"n": "日本", "v": "2"
|
||||
}]
|
||||
}, {
|
||||
"key": "year", "name": "年份", "value": [{
|
||||
"n": "全部", "v": "0"
|
||||
}, {
|
||||
"n": "2024", "v": "119"
|
||||
}, {
|
||||
"n": "2023", "v": "153"
|
||||
}, {
|
||||
"n": "2022", "v": "101"
|
||||
}, {
|
||||
"n": "2021", "v": "118"
|
||||
}, {
|
||||
"n": "2020", "v": "16"
|
||||
}, {
|
||||
"n": "2019", "v": "7"
|
||||
}, {
|
||||
"n": "2018", "v": "2"
|
||||
}, {
|
||||
"n": "2017", "v": "3"
|
||||
}, {
|
||||
"n": "2016", "v": "22"
|
||||
}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{
|
||||
"n": "热门", "v": "hot"
|
||||
}, {
|
||||
"n": "更新", "v": "updata"
|
||||
}, {
|
||||
"n": "评分", "v": "rating"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let content = await this.fetch(this.siteUrl + "/api/tag/hand?code=unknown601193cf375db73d&channel=wandoujia", null, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
let data_list = content_json["data"][0]["video"]
|
||||
this.homeVodList = await this.parseVodShortListFromJson(data_list)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let cateId = extend["cateId"] ?? tid
|
||||
let area = extend["area"] ?? "0";
|
||||
let year = extend["year"] ?? "0";
|
||||
let by = extend["by"] ?? "hot";
|
||||
this.limit = 24
|
||||
let categoryUrl = this.siteUrl + `/api/crumb/list?area=${area}&category_id=${cateId}&page=${pg}&type=0&limit=24&sort=${by}&year=${year}`
|
||||
await this.jadeLog.debug(`分类URL:${categoryUrl}`)
|
||||
let content = await this.fetch(categoryUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
let data = content_json["data"]
|
||||
this.vodList = await this.parseVodShortListFromJson(data)
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let url = this.siteUrl + "/api/node/detail?channel=wandoujia&token=&id=" + id;
|
||||
let content = await this.fetch(url, null, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content);
|
||||
let data_list = content_json["data"]
|
||||
this.vodDetail = await this.parseVodDetailfromJson(data_list)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = this.siteUrl + "/api/video/search?page=1" + "&key=" + wd;
|
||||
const content = await this.fetch(url, null, this.getHeader());
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
let data_list = content_json["data"]
|
||||
this.vodList = await this.parseVodShortListFromJson(data_list)
|
||||
}
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
await this.jadeLog.debug(`播放链接为:${id}`)
|
||||
this.playUrl = id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let spider = new JianPianSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
281
cat/tjs/js/jiujiuliu.js
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* @File : jiujiuliu.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/4 14:15
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 996影视
|
||||
*/
|
||||
|
||||
import {Spider} from "./spider.js";
|
||||
import {_, Crypto, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
class JiuJiuLiuSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.cs1369.com"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🥃┃九九六影视┃🥃"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "九九六影视"
|
||||
}
|
||||
getJSName() {
|
||||
return "jiujiuliu"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"stui-vodlist clearfix\"]").find("li")
|
||||
for (const vodElement of vodElements) {
|
||||
let resource = $(vodElement).find("[class=\"stui-vodlist__thumb lazyload\"]")[0]
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = resource.attribs["href"]
|
||||
vodShort.vod_name = resource.attribs["title"]
|
||||
vodShort.vod_pic = resource.attribs["data-original"]
|
||||
vodShort.vod_remarks = $($(resource).find("[class=\"pic-text text-right\"]")[0]).text()
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"stui-pannel_bd\"]").find("li")
|
||||
for (const vodElement of vodElements) {
|
||||
let resource = $($(vodElement).find("[class=\"thumb\"]")[0]).find("a")[0]
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = resource.attribs["href"]
|
||||
vodShort.vod_name = resource.attribs["title"]
|
||||
vodShort.vod_pic = resource.attribs["data-original"]
|
||||
vodShort.vod_remarks = Utils.getStrByRegex(/类型:(.*?)地区/, $($(vodElement).find("[class=\"hidden-mi\"]")[0]).text())
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
let vodElement = $("[class=\"col-pd clearfix\"]")[1]
|
||||
let vodShortElement = $(vodElement).find("[class=\"stui-content__thumb\"]")[0]
|
||||
let vodItems = []
|
||||
for (const playElement of $("[class=\"stui-content__playlist clearfix\"]").find("a")) {
|
||||
let episodeUrl = this.siteUrl + playElement.attribs["href"];
|
||||
let episodeName = $(playElement).text();
|
||||
vodItems.push(episodeName + "$" + episodeUrl);
|
||||
}
|
||||
vodDetail.vod_name = $(vodShortElement).find("[class=\"stui-vodlist__thumb picture v-thumb\"]")[0].attribs["title"]
|
||||
vodDetail.vod_pic = $(vodShortElement).find("img")[0].attribs["data-original"]
|
||||
vodDetail.vod_remarks = $($(vodShortElement).find("[class=\"pic-text text-right\"]")[0]).text()
|
||||
let data_str = $($(vodElement).find("[class=\"data\"]")).text().replaceAll(" ", " ")
|
||||
vodDetail.type_name = Utils.getStrByRegex(/类型:(.*?) /, data_str)
|
||||
vodDetail.vod_area = Utils.getStrByRegex(/地区:(.*?) /, data_str)
|
||||
vodDetail.vod_year = Utils.getStrByRegex(/年份:(.*?) /, data_str)
|
||||
vodDetail.vod_actor = Utils.getStrByRegex(/主演:(.*?) /, data_str)
|
||||
vodDetail.vod_director = Utils.getStrByRegex(/导演:(.*?) /, data_str)
|
||||
vodDetail.vod_content = $($("[class=\"stui-pannel_bd\"]").find("[class=\"col-pd\"]")).text()
|
||||
vodDetail.vod_play_from = ["996"].join("$$$")
|
||||
vodDetail.vod_play_url = [vodItems.join("#")].join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (html !== null) {
|
||||
let $ = load(html)
|
||||
let menuElements = $("[class=\"stui-header__menu type-slide\"]").find("a")
|
||||
for (const menuElement of menuElements) {
|
||||
let type_dic = {
|
||||
"type_name": $(menuElement).text(),
|
||||
"type_id": "/show/id/" + menuElement.attribs["href"].split("/").slice(-1)[0].split(".")[0]
|
||||
}
|
||||
if ($(menuElement).text() !== "首页") {
|
||||
this.classes.push(type_dic)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let hdElements = $("[class=\"stui-pannel_hd\"]")
|
||||
let extend_list = []
|
||||
let index = 0
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let cateElemet = hdElements[i]
|
||||
let typeElements = $(cateElemet).find("ul")
|
||||
if (i === 0) {
|
||||
for (const typeElement of typeElements) {
|
||||
let extend_dic = {
|
||||
"key": (index + 1).toString(), "name": $($(typeElement).find("li")[0]).text(), "value": []
|
||||
}
|
||||
for (const ele of $(typeElement).find("li").slice(1)) {
|
||||
if (!_.isEmpty($(ele).text())) {
|
||||
if (index === 0) {
|
||||
extend_dic["value"].push({
|
||||
"n": $(ele).text(),
|
||||
"v": $(ele).find("a")[0].attribs["href"].split("/").slice(-1)[0].split(".")[0]
|
||||
})
|
||||
} else {
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": $(ele).text()})
|
||||
}
|
||||
}
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
index = index + 1
|
||||
}
|
||||
} else {
|
||||
let extend_dic = {
|
||||
"key": (index + 1).toString(), "name": $($(cateElemet).find("li")[0]).text(), "value": []
|
||||
}
|
||||
extend_dic["value"].push({"n": "全部", "v": "time"})
|
||||
for (const ele of $(cateElemet).find("li").slice(1)) {
|
||||
if (!_.isEmpty($(ele).text())) {
|
||||
extend_dic["value"].push({
|
||||
"n": $(ele).text(), "v": $(ele).find("a")[0].attribs["href"].split("/")[3]
|
||||
})
|
||||
}
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const type_dic of this.classes) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "/" && type_id !== "最近更新") {
|
||||
let url = this.siteUrl + type_id + ".html"
|
||||
let html = await this.fetch(url, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
getParams(params, value) {
|
||||
let x = value ?? "全部"
|
||||
if (x === "全部" || x === undefined) {
|
||||
return ""
|
||||
} else {
|
||||
return params + value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let typeName = this.getParams("/id/", extend["1"])
|
||||
if (_.isEmpty(typeName)) {
|
||||
typeName = "/id/" + tid.split("/").slice(-1)[0]
|
||||
}
|
||||
let plot = this.getParams("/class/", extend["2"])
|
||||
let area = this.getParams("/area/", extend["3"])
|
||||
let year = this.getParams("/year/", extend["4"])
|
||||
let language = this.getParams("/lang/ ", extend["5"])
|
||||
let letter = this.getParams("/letter/ ", extend["6"])
|
||||
let time = this.getParams("/by/", extend["7"])
|
||||
let cateUrl = this.siteUrl + `/show${area}${time}${plot}${typeName}${language}${letter}${year}/page/${pg.toString()}.html`
|
||||
await this.jadeLog.info(`类别URL为:${cateUrl}`)
|
||||
this.limit = 36
|
||||
let html = await this.fetch(cateUrl, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let detailUrl = this.siteUrl + id
|
||||
let html = await this.fetch(detailUrl, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let searchUrl = this.siteUrl + `/search.html?wd=${wd}`
|
||||
let html = await this.fetch(searchUrl, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
|
||||
let x = 0
|
||||
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let html = await this.fetch(id, null, this.getHeader())
|
||||
if (html !== null) {
|
||||
let matcher = Utils.getStrByRegex(/player_aaaa=(.*?)<\/script>/, html)
|
||||
let player = JSON.parse(matcher);
|
||||
try {
|
||||
this.playUrl = decodeURIComponent(Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(player["url"])))
|
||||
this.header = this.getHeader()
|
||||
} catch (e) {
|
||||
this.playUrl = player["url"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new JiuJiuLiuSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
308
cat/tjs/js/kankan70.js
Normal file
@ -0,0 +1,308 @@
|
||||
/*
|
||||
* @File : kankan70.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/29 15:33
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {_, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
function get_qp_name44(qp_type) {
|
||||
if (qp_type === 'zd') return '最大';
|
||||
if (qp_type === 'yj') return '永久';
|
||||
if (qp_type === 'hn') return '牛牛';
|
||||
if (qp_type === 'gs') return '光波';
|
||||
if (qp_type === 'sn') return '新朗';
|
||||
if (qp_type === 'wl') return '涡轮';
|
||||
if (qp_type === 'lz') return '良子';
|
||||
if (qp_type === 'fs') return 'F速';
|
||||
if (qp_type === 'ff') return '飞飞';
|
||||
if (qp_type === 'bd') return '百度';
|
||||
if (qp_type === 'uk') return '酷U';
|
||||
if (qp_type === 'wj') return '无天';
|
||||
if (qp_type === 'bj') return '八戒';
|
||||
if (qp_type === 'tk') return '天空';
|
||||
if (qp_type === 'ss') return '速速';
|
||||
if (qp_type === 'kb') return '酷播';
|
||||
if (qp_type === 'sd') return '闪电';
|
||||
if (qp_type === 'xk') return '看看';
|
||||
if (qp_type === 'tp') return '淘淘';
|
||||
if (qp_type === 'jy') return '精英';
|
||||
|
||||
return qp_type;
|
||||
}
|
||||
|
||||
|
||||
class Kankan70Spider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "http://cqdb6.com";
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "📺┃70看看┃📺"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "70看看"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "kankan70"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
paraseUrlObject(js_str) {
|
||||
let content_list = js_str.split(";")
|
||||
let urlObject = {}
|
||||
let js_name = ""
|
||||
let play_id = 0
|
||||
let pldy_id = 0
|
||||
let js_key = ""
|
||||
for (let i = 0; i < content_list.length; i++) {
|
||||
let content = content_list[i]
|
||||
if (content.indexOf("var lianzaijs") > -1) {
|
||||
js_name = content.split("=")[0].split(" ")[1]
|
||||
js_key = js_name.split("_")[1]
|
||||
} else if (content.indexOf("pl_id=") > -1) {
|
||||
play_id = content.split("=")[1]
|
||||
urlObject[js_name] = {"play_id": play_id, "list": [], "pl_dy": pldy_id}
|
||||
} else if (content.indexOf("var pl_dy") > -1) {
|
||||
pldy_id = content.split("=")[1]
|
||||
}
|
||||
if (content.indexOf(`playarr_${js_key}[`) > -1) {
|
||||
let play_url = content.split("=\"")[1].split(",")[0]
|
||||
urlObject[js_name]["list"].push(play_url)
|
||||
}
|
||||
}
|
||||
let play_url_list = [], play_format_list = [];
|
||||
for (const key of Object.keys(urlObject)) {
|
||||
if (key.indexOf("_") > -1) {
|
||||
let play_format_name = get_qp_name44(key.split("_")[1])
|
||||
play_format_list.push(play_format_name)
|
||||
let vodItems = []
|
||||
let index = 0
|
||||
for (const play_url of urlObject[key]["list"]) {
|
||||
index = index + 1
|
||||
vodItems.push("第" + index.toString() + "集" + "$" + play_url)
|
||||
}
|
||||
play_url_list.push(vodItems.join("#"))
|
||||
}
|
||||
}
|
||||
return {"play_format": play_format_list.join("$$$"), "play_url": play_url_list.join("$$$")}
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vod_elements = $("a.li-hv")
|
||||
for (const vod_element of vod_elements) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = "/" + vod_element.attribs["href"]
|
||||
vodShort.vod_name = vod_element.attribs["title"]
|
||||
vodShort.vod_pic = $(vod_element).find("img")[0].attribs["data-original"]
|
||||
let remarkEle = $(vod_element).find("p.bz")[0]
|
||||
if (remarkEle.length > 0) {
|
||||
vodShort.vod_remarks = remarkEle.children[0].data
|
||||
}
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
let infoElement = $("[class=info]")
|
||||
let dtElement = $(infoElement).find("dt.name")[0]
|
||||
vodDetail.vod_name = dtElement.children[0].data
|
||||
vodDetail.vod_remarks = dtElement.children[1].children[0].data
|
||||
let ddString = $(infoElement).find("dd").text()
|
||||
vodDetail.vod_area = Utils.getStrByRegex(/地区:(.*?) /, ddString)
|
||||
vodDetail.vod_year = Utils.getStrByRegex(/年代:(.*?)\n/, ddString)
|
||||
vodDetail.type_name = Utils.getStrByRegex(/类型:(.*?)\n/, ddString)
|
||||
vodDetail.vod_content = $(infoElement).find("[class=des2]").text().replaceAll("\n", "").replaceAll("剧情:", "")
|
||||
vodDetail.vod_pic = $("img.lazy")[0].attribs["data-original"]
|
||||
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const vod_object of obj) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = vod_object["url"]
|
||||
vodShort.vod_pic = vod_object["thumb"]
|
||||
vodShort.vod_remarks = vod_object["time"]
|
||||
vodShort.vod_name = vod_object["title"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
let elements = $("[class=index-list-l]")
|
||||
for (const element of elements) {
|
||||
let typeElement = $($(element).find("[class=\"h1 clearfix\"]")[0]).find("a")
|
||||
let type_id = typeElement[0].attribs["href"]
|
||||
let type_name = $(typeElement[1]).text()
|
||||
if (!_.isEmpty(type_name)) {
|
||||
this.classes.push({"type_id": type_id, "type_name": type_name})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter(type_id) {
|
||||
let url = this.siteUrl + type_id
|
||||
let html = await this.fetch(url, null, this.getHeader())
|
||||
let extend_list = []
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
let elements = $("[class=\"sy scon clearfix\"]").find("dl")
|
||||
let i = 0
|
||||
for (const element of elements) {
|
||||
let type_name = $($(element).find("dt")).text().replace("按", "").replace(":", "")
|
||||
let extend_dic = {
|
||||
"key": (i + 1).toString(), "name": type_name, "value": []
|
||||
}
|
||||
let type_elements = $(element).find("a")
|
||||
let index = 0
|
||||
if (type_name === "剧情") {
|
||||
index = 3
|
||||
} else if (type_name === "年代") {
|
||||
index = 2
|
||||
} else if (type_name === "地区") {
|
||||
index = 4
|
||||
}
|
||||
for (const type_element of type_elements) {
|
||||
let type_id_list = type_element.attribs["href"].split("/")
|
||||
extend_dic["value"].push({"n": $(type_element).text(), "v": type_id_list[index]})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const class_dic of this.classes) {
|
||||
let type_id = class_dic["type_id"]
|
||||
if (type_id !== "最近更新") {
|
||||
this.filterObj[type_id] = await this.getFilter(type_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let url = this.siteUrl + tid
|
||||
let html = await this.fetch(url, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let class_name = tid.split("/")[1]
|
||||
let id = tid.split("/")[2]
|
||||
let api_str = Utils.getStrByRegex(/var _yu_gda_s="(.*?)";/, html)
|
||||
let params = {
|
||||
"action": class_name,
|
||||
"page": parseInt(pg),
|
||||
"year": extend["2"] ?? "0",
|
||||
"area": extend["3"] ?? "all",
|
||||
"class": extend["1"] ?? "0",
|
||||
"dect": "",
|
||||
"id": id
|
||||
}
|
||||
let cate_html = await this.fetch(api_str, params, this.getHeader())
|
||||
if (cate_html !== null) {
|
||||
let $ = load(cate_html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let detailUrl = this.siteUrl + id
|
||||
let html = await this.fetch(detailUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
let mather = /<script type="text\/javascript" src="http:\/\/test.gqyy8.com:8077\/ne2(.*?)"><\/script>/g.exec(html)
|
||||
let js_url = "http://test.gqyy8.com:8077/ne2" + mather[1]
|
||||
let js_str = await this.fetch(js_url, null, this.getHeader())
|
||||
if (!_.isEmpty(js_str)) {
|
||||
let playObject = this.paraseUrlObject(js_str)
|
||||
this.vodDetail.vod_play_url = playObject["play_url"]
|
||||
this.vodDetail.vod_play_from = playObject["play_format"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = this.siteUrl + "/search.php"
|
||||
let html = await this.fetch(url, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let params = {
|
||||
"top": 10, "q": wd,
|
||||
}
|
||||
let api_url = Utils.getStrByRegex(/var my_search='(.*?)';/, html)
|
||||
let content = await this.fetch(api_url, params, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content.replaceAll("",""))
|
||||
this.vodList = await this.parseVodShortListFromJson(content_json)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new Kankan70Spider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
444
cat/tjs/js/kuaikan.js
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
* @File : kuaikan.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/19 11:12
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {jinja2, _, dayjs, Crypto} from "../lib/cat.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
const charStr = 'abacdefghjklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789';
|
||||
|
||||
function randStr(len, withNum) {
|
||||
let _str = '';
|
||||
let containsNum = withNum === undefined ? true : withNum;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let idx = _.random(0, containsNum ? charStr.length - 1 : charStr.length - 11);
|
||||
_str += charStr[idx];
|
||||
}
|
||||
return _str;
|
||||
}
|
||||
|
||||
function randDevice() {
|
||||
return {
|
||||
brand: 'Huawei',
|
||||
model: 'HUAWEI Mate 20',
|
||||
release: '10',
|
||||
buildId: randStr(3, false).toUpperCase() + _.random(11, 99) + randStr(1, false).toUpperCase(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function formatPlayUrl(src, name) {
|
||||
return name
|
||||
.trim()
|
||||
.replaceAll(src, '')
|
||||
.replace(/<|>|《|》/g, '')
|
||||
.replace(/\$|#/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function jsonParse(input, json) {
|
||||
try {
|
||||
let url = json.url ?? '';
|
||||
if (url.startsWith('//')) {
|
||||
url = 'https:' + url;
|
||||
}
|
||||
if (!url.startsWith('http')) {
|
||||
return {};
|
||||
}
|
||||
let headers = json['headers'] || {};
|
||||
let ua = (json['user-agent'] || '').trim();
|
||||
if (ua.length > 0) {
|
||||
headers['User-Agent'] = ua;
|
||||
}
|
||||
let referer = (json['referer'] || '').trim();
|
||||
if (referer.length > 0) {
|
||||
headers['Referer'] = referer;
|
||||
}
|
||||
_.keys(headers).forEach((hk) => {
|
||||
if (!headers[hk]) delete headers[hk];
|
||||
});
|
||||
return {
|
||||
header: headers, url: url,
|
||||
};
|
||||
} catch (error) {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
class KuaiKanSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = 'https://api1.baibaipei.com:8899';
|
||||
this.device = {}
|
||||
this.parse = []
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🛥︎┃快看视频┃🛥︎"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "快看视频"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "kuaikan"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
this.danmuStaus = true
|
||||
await this.setDevice();
|
||||
}
|
||||
|
||||
|
||||
async request(reqUrl, postData, agentSp, get) {
|
||||
let ts = dayjs().valueOf().toString();
|
||||
let rand = randStr(32);
|
||||
let sign = Crypto.enc.Hex.stringify(Crypto.MD5('H58d2%gLbeingX*%D4Y8!C!!@G_' + ts + '_' + rand))
|
||||
.toString()
|
||||
.toLowerCase();
|
||||
let headers = {
|
||||
'user-agent': agentSp || this.device.ua,
|
||||
};
|
||||
if (reqUrl.includes('baibaipei')) {
|
||||
headers['device-id'] = this.device.id;
|
||||
headers['sign'] = sign;
|
||||
headers['time'] = ts;
|
||||
headers['md5'] = rand;
|
||||
headers['version'] = '2.1.5';
|
||||
headers['system-model'] = this.device.model;
|
||||
headers['system-brand'] = this.device.brand;
|
||||
headers['system-version'] = this.device.release;
|
||||
headers["host"] = "api1.baibaipei.com:8899"
|
||||
}
|
||||
if (!get) {
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
let res = await req(reqUrl, {
|
||||
method: get ? 'get' : 'post', headers: headers, data: postData || {}, postType: 'form'
|
||||
});
|
||||
await this.jadeLog.debug(`URL:${reqUrl},headers:${JSON.stringify(headers)},data:${[JSON.stringify(postData)]}`)
|
||||
let content = res.content;
|
||||
try {
|
||||
let key = Crypto.enc.Utf8.parse('IjhHsCB2B5^#%0Ag');
|
||||
let iv = Crypto.enc.Utf8.parse('y8_m.3rauW/>j,}.');
|
||||
let src = Crypto.enc.Base64.parse(content);
|
||||
let dst = Crypto.AES.decrypt({ciphertext: src}, key, {iv: iv, padding: Crypto.pad.Pkcs7});
|
||||
dst = Crypto.enc.Utf8.stringify(dst);
|
||||
await this.jadeLog.debug(`response:${dst}`)
|
||||
return JSON.parse(dst);
|
||||
} catch (e) {
|
||||
return JSON.parse(content)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setDevice() {
|
||||
let deviceKey = 'device';
|
||||
let deviceInfo = await local.get(this.siteKey, deviceKey);
|
||||
if (deviceInfo.length > 0) {
|
||||
try {
|
||||
this.device = JSON.parse(deviceInfo);
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
if (_.isEmpty(this.device)) {
|
||||
this.device = randDevice();
|
||||
this.device.id = randStr(13).toLowerCase();
|
||||
this.device.ua = 'okhttp/3.14.9';
|
||||
await local.set(this.siteKey, deviceKey, JSON.stringify(this.device));
|
||||
}
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
await this.setDevice()
|
||||
let response = await this.request(this.siteUrl + '/api.php/Index/getTopVideoCategory');
|
||||
for (const type of response.data) {
|
||||
let typeName = type["nav_name"];
|
||||
if (typeName === '推荐') continue;
|
||||
let typeId = type["nav_type_id"].toString();
|
||||
this.classes.push({
|
||||
type_id: typeId, type_name: typeName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter(filterData) {
|
||||
await this.jadeLog.debug(JSON.stringify(filterData))
|
||||
let filterAll = []
|
||||
for (let key of Object.keys(filterData)) {
|
||||
let itemValues = filterData[key];
|
||||
if (key === 'plot') key = 'class';
|
||||
let typeExtendName = '';
|
||||
switch (key) {
|
||||
case 'class':
|
||||
typeExtendName = '类型';
|
||||
break;
|
||||
case 'area':
|
||||
typeExtendName = '地区';
|
||||
break;
|
||||
case 'lang':
|
||||
typeExtendName = '语言';
|
||||
break;
|
||||
case 'year':
|
||||
typeExtendName = '年代';
|
||||
break;
|
||||
case 'sort':
|
||||
typeExtendName = '排序';
|
||||
break;
|
||||
}
|
||||
if (typeExtendName.length === 0) continue;
|
||||
let newTypeExtend = {
|
||||
key: key, name: typeExtendName,
|
||||
};
|
||||
let newTypeExtendKV = [];
|
||||
for (let j = 0; j < itemValues.length; j++) {
|
||||
const name = itemValues[j];
|
||||
let value = key === 'sort' ? j + '' : name === '全部' ? '0' : name;
|
||||
newTypeExtendKV.push({n: name, v: value});
|
||||
}
|
||||
newTypeExtend['init'] = key === 'sort' ? '1' : newTypeExtendKV[0]['v'];
|
||||
newTypeExtend.value = newTypeExtendKV;
|
||||
filterAll.push(newTypeExtend);
|
||||
}
|
||||
return filterAll
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const typeDic of this.classes) {
|
||||
let typeId = typeDic["type_id"]
|
||||
if (typeId !== "最近更新") {
|
||||
let filterData = await this.request(this.siteUrl + '/api.php/Video/getFilterType', {type: typeId})
|
||||
this.filterObj[typeId] = await this.getFilter(filterData["data"])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async parseVodShortListFromJSONByHome(obj) {
|
||||
let vod_list = []
|
||||
for (const data of obj["video"]) {
|
||||
let video_vod_list = await this.parseVodShortListFromJson(data["list"])
|
||||
vod_list.push(...video_vod_list)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const data of obj) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = data["vod_id"]
|
||||
vodShort.vod_name = data["vod_name"]
|
||||
vodShort.vod_pic = data["vod_pic"]
|
||||
vodShort.vod_remarks = data["vod_remarks"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.load_dic(JSON.stringify(obj))
|
||||
vodDetail.vod_content = obj["vod_content"].trim()
|
||||
vodDetail.type_name = obj["vod_class"]
|
||||
let playlist = {};
|
||||
for (const item of obj["vod_play"]) {
|
||||
let from = item["playerForm"];
|
||||
if (from === 'jp' && this.catOpenStatus) continue;
|
||||
if (from === 'xg' && this.catOpenStatus) continue;
|
||||
let urls = [];
|
||||
for (const u of item.url) {
|
||||
urls.push(formatPlayUrl(vodDetail.vod_name, u.title) + '$' + u.play_url);
|
||||
}
|
||||
if (!playlist.hasOwnProperty(from) && urls.length > 0) {
|
||||
playlist[from] = urls;
|
||||
}
|
||||
}
|
||||
this.parse = obj.parse || [];
|
||||
vodDetail.vod_play_from = _.keys(playlist).join('$$$');
|
||||
let urls = _.values(playlist);
|
||||
let vod_play_url = [];
|
||||
for (const urlist of urls) {
|
||||
vod_play_url.push(urlist.join('#'));
|
||||
}
|
||||
vodDetail.vod_play_url = vod_play_url.join('$$$');
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let data = await this.request(this.siteUrl + "/api.php/Index/getHomePage", {"p": "1", "type": "1"})
|
||||
this.homeVodList = await this.parseVodShortListFromJSONByHome(data.data)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
if (pg === 0) pg = 1;
|
||||
let reqUrl = this.siteUrl + '/api.php/Video/getFilterVideoList';
|
||||
let formData = JSON.parse(jinja2(`{
|
||||
"type": "{{tid}}",
|
||||
"p": "{{pg}}",
|
||||
"area": "{{ext.area|default(0)}}",
|
||||
"year": "{{ext.year|default(0)}}",
|
||||
"sort": "{{ext.sort|default(0)}}",
|
||||
"class": "{{ext.class|default(0)}}"}`, {ext: extend, tid: tid, pg: pg}));
|
||||
console.log(formData);
|
||||
let data = await this.request(reqUrl, formData);
|
||||
this.vodList = await this.parseVodShortListFromJson(data["data"]["data"])
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let data = await this.request(this.siteUrl + '/api.php/Video/getVideoInfo', {video_id: id})
|
||||
this.vodDetail = await this.parseVodDetailfromJson(data["data"]["video"])
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
this.result.jx = 0
|
||||
try {
|
||||
if (id.indexOf('youku') >= 0 || id.indexOf('iqiyi') >= 0 || id.indexOf('v.qq.com') >= 0 || id.indexOf('pptv') >= 0 || id.indexOf('le.com') >= 0 || id.indexOf('1905.com') >= 0 || id.indexOf('mgtv') >= 0)
|
||||
{
|
||||
if (this.parse.length > 0) {
|
||||
for (let index = 0; index < this.parse.length; index++) {
|
||||
try {
|
||||
const p = this.parse[index];
|
||||
let res = await req(p + id, {
|
||||
headers: {'user-agent': 'okhttp/4.1.0'},
|
||||
});
|
||||
await this.jadeLog.debug(`解析连接结果为:${JSON.stringify(res)}`)
|
||||
let result = jsonParse(id, JSON.parse(res.content)["data"]);
|
||||
if (result.url){
|
||||
this.playUrl = result.url // 这里可以直接返回弹幕,无法进行快进操作
|
||||
this.danmuUrl = await this.danmuSpider.getVideoUrl(id,0)
|
||||
this.result.jx = 1
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (id.indexOf('jqq-') >= 0) {
|
||||
let jqqHeaders = await this.request(this.siteUrl + '/jqqheader.json', null, null, true);
|
||||
let ids = id.split('-');
|
||||
let jxJqq = await req('https://api.juquanquanapp.com/app/drama/detail?dramaId=' + ids[1] + '&episodeSid=' + ids[2] + '&quality=LD', {headers: jqqHeaders});
|
||||
let jqqInfo = JSON.parse(jxJqq.content);
|
||||
if (jqqInfo.data["playInfo"]["url"]) {
|
||||
this.playUrl = jqqInfo.data["playInfo"]["url"]
|
||||
}
|
||||
} else if (id.startsWith("ftp")) {
|
||||
this.playUrl = id
|
||||
} else {
|
||||
let res = await this.request(this.siteUrl + '/video.php', {url: id});
|
||||
let result = jsonParse(id, res.data);
|
||||
if (result.url) {
|
||||
if (result.url.indexOf("filename=1.mp4") > -1) {
|
||||
this.playUrl = result.url
|
||||
} else {
|
||||
this.playUrl = await js2Proxy(true, this.siteType, this.siteKey, 'lzm3u8/' + Utils.base64Encode(result.url), {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
await this.jadeLog.error(e)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let data = await this.request(this.siteUrl + '/api.php/Search/getSearch', {key: wd, type_id: 0, p: 1})
|
||||
this.vodList = await this.parseVodShortListFromJson(data["data"]["data"])
|
||||
}
|
||||
|
||||
async proxy(segments, headers) {
|
||||
let what = segments[0];
|
||||
let url = Utils.base64Decode(segments[1]);
|
||||
if (what === 'lzm3u8') {
|
||||
await this.jadeLog.debug(`使用代理播放,播放连接为:${url}`)
|
||||
const resp = await req(url, {});
|
||||
let hls = resp.content;
|
||||
const jsBase = await js2Proxy(false, this.siteType, this.siteKey, 'lzm3u8/', {});
|
||||
const baseUrl = url.substr(0, url.lastIndexOf('/') + 1);
|
||||
await this.jadeLog.debug(hls.length)
|
||||
hls = hls.replace(/#EXT-X-DISCONTINUITY\r*\n*#EXTINF:6.433333,[\s\S]*?#EXT-X-DISCONTINUITY/, '');
|
||||
await this.jadeLog.debug(hls.length)
|
||||
hls = hls.replace(/(#EXT-X-KEY\S+URI=")(\S+)("\S+)/g, function (match, p1, p2, p3) {
|
||||
let up = (!p2.startsWith('http') ? baseUrl : '') + p2;
|
||||
return p1 + up + p3;
|
||||
});
|
||||
hls = hls.replace(/(#EXT-X-STREAM-INF:.*\n)(.*)/g, function (match, p1, p2) {
|
||||
let up = (!p2.startsWith('http') ? baseUrl : '') + p2;
|
||||
return p1 + jsBase + Utils.base64Decode(up);
|
||||
});
|
||||
hls = hls.replace(/(#EXTINF:.*\n)(.*)/g, function (match, p1, p2) {
|
||||
let up = (!p2.startsWith('http') ? baseUrl : '') + p2;
|
||||
return p1 + up;
|
||||
});
|
||||
return JSON.stringify({
|
||||
code: resp.code, content: hls, headers: resp.headers,
|
||||
});
|
||||
}
|
||||
return JSON.stringify({
|
||||
code: 500, content: '',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
let spider = new KuaiKanSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
93
cat/tjs/js/liangzi.js
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* @File : liangzi.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/24 9:15
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 量子资源
|
||||
*/
|
||||
import {VodSpider} from "./vodSpider.js";
|
||||
|
||||
class LiangziSpider extends VodSpider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://cj.lzcaiji.com"
|
||||
this.remove18 = true
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "量子资源"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🐝┃量子资源┃🐝"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "liangzi"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async spiderInit(inReq) {
|
||||
await super.spiderInit(inReq);
|
||||
}
|
||||
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new LiangziSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
93
cat/tjs/js/liangzi18.js
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* @File : liangzi18.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/24 9:15
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 量子资源18
|
||||
*/
|
||||
import {VodSpider} from "./vodSpider.js";
|
||||
|
||||
class Liangzi18Spider extends VodSpider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://cj.lzcaiji.com"
|
||||
this.remove18 = false
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "量子资源18+"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🔞┃量子资源18+┃🔞"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "liangzi18"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async spiderInit(inReq) {
|
||||
await super.spiderInit(inReq);
|
||||
}
|
||||
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new Liangzi18Spider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
269
cat/tjs/js/mp4movie.js
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* @File : mp4movie.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/19 9:41
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
|
||||
class DyttSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.mp4us.com"
|
||||
this.apiUrl = "https://m.mp4us.com"
|
||||
this.dyttReconnectTimes = 0
|
||||
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🍚┃Mp4电影┃🍚"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "Mp4电影"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "mp4movie"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
// getHeader() {
|
||||
// return {"User-Agent": Utils.CHROME, "Referer": this.siteUrl + "/","Connection":"keep-alive"};
|
||||
// }
|
||||
|
||||
async getFilter() {
|
||||
let extend_list = []
|
||||
let $ = await this.getHtml()
|
||||
let suoyinElement = $("[class=\"nav navbar-nav\"]").find("li").slice(-1)[0]
|
||||
let souyinUrl = $(suoyinElement).find("a")[0].attribs.href
|
||||
let souyin$ = await this.getHtml(this.siteUrl + souyinUrl)
|
||||
let sortElements = souyin$("[class=\"sort-box\"]").find("[class=\"sort-list\"]").slice(1,-1)
|
||||
for (const sortElement of sortElements){
|
||||
let name = $($(sortElement).find("h5")).text().replace(":","")
|
||||
let extend_dic = {"key": name, "name":name, "value": []}
|
||||
for (const ele of $(sortElement).find("a")) {
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": ele.attribs.data.split("-")[1]})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
// async setFilterObj() {
|
||||
// for (const type_dic of this.classes) {
|
||||
// let type_id = type_dic["type_id"]
|
||||
// if (type_id !== "最近更新") {
|
||||
// this.filterObj[type_id] = await this.getFilter()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let suoyinElement = $("[class=\"nav navbar-nav\"]").find("li").slice(-1)[0]
|
||||
let souyinUrl = $(suoyinElement).find("a")[0].attribs.href
|
||||
let souyin$ = await this.getHtml(this.siteUrl + souyinUrl)
|
||||
let sortElements = souyin$("[class=\"sort-box\"]").find("[class=\"sort-list\"]")
|
||||
let classElements = $(sortElements[0]).find("li")
|
||||
for (const classElement of classElements) {
|
||||
let type_name = $($(classElement).find("a")).text()
|
||||
let type_id = $(classElement).find("a")[0].attribs.data.replaceAll("id-", "")
|
||||
if (type_name !== "全部") {
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"index_today cclear\"]").find("a")
|
||||
for (const vodElement of vodElements){
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_name = vodElement.attribs.title
|
||||
vodShort.vod_id = vodElement.attribs.href
|
||||
vodShort.vod_pic = this.detailProxy + Utils.base64Encode(vodShort.vod_id)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
getSearchHeader() {
|
||||
let headers = this.getHeader()
|
||||
headers["Cookie"] = "Hm_lvt_d8c8eecfb13fe991855f511a6e30c3d2=1708243467,1708325624,1708326536;Hm_lpvt_d8c8eecfb13fe991855f511a6e30c3d2;1708326536"
|
||||
return headers
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
let html = $.html()
|
||||
let detailRootElement = $("[class=\"article-header\"]")
|
||||
let detailElements = $(detailRootElement).find("p")
|
||||
let content = ""
|
||||
for (const detailElement of detailElements){
|
||||
content = content + $(detailElement).text() + "\n"
|
||||
}
|
||||
vodDetail.type_name = $($($(detailRootElement).find("[class=\"post-meta\"]")).find("span")[0]).text()
|
||||
vodDetail.vod_pic = $(detailRootElement).find("img")[0].attribs.src
|
||||
vodDetail.vod_name = Utils.getStrByRegex(/名称:(.*?)\n/,content)
|
||||
vodDetail.vod_actor = Utils.getStrByRegex(/主演:(.*?)\n/,content)
|
||||
vodDetail.vod_director = Utils.getStrByRegex(/导演:(.*?)\n/,content)
|
||||
vodDetail.vod_area = Utils.getStrByRegex(/地区:(.*?)\n/,content)
|
||||
vodDetail.vod_year = Utils.getStrByRegex(/年份:(.*?)\n/,content)
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/更新:(.*?)\n/,content)
|
||||
let contentElement = $("[class=\"article-related info\"]").find("p")
|
||||
vodDetail.vod_content = $(contentElement).text()
|
||||
let downloadElements = $("[class=\"article-related download_url\"]")
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
for (let i = 0; i < downloadElements.length; i++) {
|
||||
let playFormatElement = downloadElements[i]
|
||||
let format_name = $($(playFormatElement).find("h2")).text().replaceAll(vodDetail.vod_name,"")
|
||||
vod_play_from_list.push(format_name.replaceAll("下载","播放"))
|
||||
let vodItems = []
|
||||
for (const playUrlElement of $(downloadElements[i]).find("a")) {
|
||||
let episodeName = $(playUrlElement).text().replaceAll("磁力链下载","").replaceAll(".mp4","")
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
let $ = load(obj["ajaxtxt"])
|
||||
let vodElements = $($("ul")).find("li");
|
||||
for (const vodElement of vodElements){
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_pic = $(vodElement).find("img")[0].attribs["data-original"]
|
||||
vodShort.vod_name = Utils.getStrByRegex(/《(.*?)》/,$(vodElement).find("img")[0].attribs.alt)
|
||||
vodShort.vod_id = $(vodElement).find("a")[0].attribs.href
|
||||
vodShort.vod_remarks = "评分:"+ $($(vodElement).find("[class=\"rate badge\"]")).text()
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let vodElements = $($("[id=\"list_all\"]").find("ul")).find("li")
|
||||
for (const vodElement of vodElements){
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = $(vodElement).find("a")[0].attribs.href
|
||||
vodShort.vod_name = Utils.getStrByRegex(/《(.*?)》/,$($($(vodElement).find("[class=\"text_info\"]")).find("a")[0]).text())
|
||||
vodShort.vod_pic = $(vodElement).find("img")[0].attribs["data-original"]
|
||||
vodShort.vod_remarks = $($(vodElement).find("[class=\"update_time\"]")).text()
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml();
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
return this.vodDetail
|
||||
}
|
||||
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
// let url = this.apiUrl + `/list-index-id-${tid}`
|
||||
// let area = extend["地区"] ?? ""
|
||||
// let year = extend["年代"] ?? ""
|
||||
// let tag = extend["标签"] ?? ""
|
||||
// if (parseInt(pg) > 1){
|
||||
// url = url + `-p-${pg}`
|
||||
// }
|
||||
// if (!_.isEmpty(area) && area !== "0"){
|
||||
// url = url + `-area-${area}`
|
||||
// }
|
||||
// if (!_.isEmpty(year) && year !== "0"){
|
||||
// url = url + `-year-${year}`
|
||||
// }
|
||||
// if (!_.isEmpty(tag) && tag !== "0"){
|
||||
// url = url + `-wd-${tag}`
|
||||
// }
|
||||
// let resp = await this.fetch(url + ".html",null,this.getHeader())
|
||||
// this.vodList = await this.parseVodShortListFromJson(JSON.parse(resp))
|
||||
let url = this.siteUrl + `/list/${tid}-${pg}.html`
|
||||
let $ = await this.getHtml(url)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = this.siteUrl + "/search/"
|
||||
let params = {"wd":wd,"p":"1","t":"j/tNgwBS2e8O4x9TuIkYuQ=="}
|
||||
let html = await this.post(url,params,this.getSearchHeader())
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new DyttSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
429
cat/tjs/js/mxanime.js
Normal file
@ -0,0 +1,429 @@
|
||||
/*
|
||||
* @File : mxanime.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/30 14:13
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : MX动漫
|
||||
*/
|
||||
import {Spider} from "./spider.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {_, load} from "../lib/cat.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
|
||||
class MxAnimeSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.mxdm6.com/"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🍒┃MX动漫┃🍒"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "MX动漫"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "mxanime"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let navElements = $($("[class=\"nav-menu-items\"]")[0]).find("[class=\"nav-menu-item \"]")
|
||||
for (const navElement of navElements) {
|
||||
let element = $(navElement).find("a")[0]
|
||||
let type_name = element.attribs.title
|
||||
let type_id = element.attribs.href
|
||||
if (type_name !== "萌图") {
|
||||
this.classes.push(this.getTypeDic(type_name, Utils.getStrByRegex(/type\/(.*?).html/, type_id)))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let elements = $("[class=\"library-box scroll-box\"]")
|
||||
let extend_list = []
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let extend_dic = {"key": (i + 1).toString(), "name": "", "value": []}
|
||||
if (i < elements.length - 1) {
|
||||
extend_dic["name"] = $($(elements[i]).find("a")[0]).text()
|
||||
extend_dic["value"].push({"n": "全部", "v": "0"})
|
||||
for (const ele of $(elements[i]).find("a").slice(1)) {
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": $(ele).text()})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
} else {
|
||||
extend_dic["name"] = $($(elements[i]).find("a")[0]).text()
|
||||
extend_dic["value"] = [{"n": "全部", "v": "0"}, {
|
||||
"n": $($(elements[i]).find("a")[1]).text(),
|
||||
"v": "hits"
|
||||
}, {"n": $($(elements[i]).find("a")[2]).text(), "v": "score"}]
|
||||
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
|
||||
}
|
||||
return extend_list
|
||||
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const type_dic of this.classes) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "最近更新") {
|
||||
let $ = await this.getHtml(this.siteUrl + `/type/${type_id}.html`)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseVodShortFromElement($, vodElement) {
|
||||
let vodShort = new VodShort()
|
||||
let element = $($(vodElement).find("[class=\"module-item-titlebox\"]")).find("a")[0]
|
||||
vodShort.vod_id = element.attribs.href
|
||||
vodShort.vod_name = element.attribs.title
|
||||
vodShort.vod_pic = $($(vodElement).find("[class=\"module-item-pic\"]")).find("img")[0].attribs["data-src"]
|
||||
vodShort.vod_remarks = $($(vodElement).find("[class=\"module-item-text\"]")).text()
|
||||
return vodShort
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"module-list module-lines-list mxone-box\"]").find("[class=\"module-item\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = await this.parseVodShortFromElement($, vodElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"module-item\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = await this.parseVodShortFromElement($, vodElement)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"module-search-item\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = new VodShort()
|
||||
let element = $(vodElement).find("[class=\"video-serial\"]")[0]
|
||||
vodShort.vod_id = element.attribs.href
|
||||
vodShort.vod_name = element.attribs.title
|
||||
vodShort.vod_pic = $($(vodElement).find("[class=\"module-item-pic\"]")).find("img")[0].attribs["data-src"]
|
||||
let remarkElements = $($(vodElement).find("[class=\"video-info-item video-info-actor\"]").slice(-1)[0]).find("a")
|
||||
let remark_list = []
|
||||
for (const remarkElement of remarkElements){
|
||||
let remark = remarkElement.children[0].data
|
||||
remark_list.push(remark)
|
||||
}
|
||||
vodShort.vod_remarks = remark_list.join("*")
|
||||
vod_list.push(vodShort)
|
||||
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
let vodElement = $("[class=\"video-info\"]")
|
||||
vodDetail.vod_pic = $("[class=\"module-item-pic\"]").find("img")[0].attribs["data-src"]
|
||||
vodDetail.vod_name = $($(vodElement).find("[class=\"page-title\"]")).text()
|
||||
let classElements = $(vodElement).find("[class=\"video-info-items\"]")
|
||||
for (const classElement of classElements){
|
||||
let text = $(classElement).text().replaceAll("\n","").replaceAll("\t","").replaceAll(" ","") + "end"
|
||||
if (text.indexOf("年份") > -1){
|
||||
vodDetail.vod_year = Utils.getStrByRegex(/年份:(.*?)end/,text).replaceAll("/","")
|
||||
}
|
||||
if(text.indexOf("备注") > -1){
|
||||
let x = Utils.getStrByRegex(/备注:(.*?)end/,text)
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/备注:\/(.*?)end/,text)
|
||||
}
|
||||
if (text.indexOf("标签") > -1){
|
||||
vodDetail.type_name = Utils.getStrByRegex(/标签:(.*?)end/,text)
|
||||
}
|
||||
if (text.indexOf("剧情") > -1){
|
||||
vodDetail.vod_content = Utils.getStrByRegex(/剧情:(.*?)end/,text)
|
||||
}
|
||||
}
|
||||
let playFormatElemets = $("[class=\"module-tab-item tab-item\"]")
|
||||
let playUrlElements = $("[class=\"scroll-content\"]")
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
for (let i = 0; i < playFormatElemets.length; i++) {
|
||||
let playFormatElement = playFormatElemets[i]
|
||||
vod_play_from_list.push(playFormatElement.attribs["data-dropdown-value"])
|
||||
let vodItems = []
|
||||
for (const playUrlElement of $(playUrlElements[i]).find("a")) {
|
||||
let episodeName = $(playUrlElement).text()
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
getExtendValue(extent, key) {
|
||||
if (extent[key] === undefined) {
|
||||
return ""
|
||||
} else {
|
||||
if (extent[key] === "0") {
|
||||
return ""
|
||||
} else {
|
||||
return extent[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
await this.jadeLog.debug(`extend:${JSON.stringify(extend)}`)
|
||||
let type = this.getExtendValue(extend, "1")
|
||||
let time = this.getExtendValue(extend, "2")
|
||||
let word = this.getExtendValue(extend, "3")
|
||||
let sort = this.getExtendValue(extend, "4")
|
||||
let urlParams = [tid.toString(), "", sort, type, "", word, "", "", pg.toString(), "", "", time]
|
||||
let url = this.siteUrl + "/show/" + urlParams.join("-") + ".html"
|
||||
let $ = await this.getHtml(url)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
async setSearch(wd, quick) {
|
||||
let url = this.siteUrl + `/search/${wd}-------------.html`
|
||||
let $ = await this.getHtml(url)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
let playerConfig = JSON.parse(Utils.getStrByRegex(/var player_aaaa=(.*?)<\/script>/,$.html()))
|
||||
const m3mu8_url = "https://danmu.yhdmjx.com/m3u8.php?url=" + playerConfig["url"]
|
||||
const m3u8_res = await (await this.fetch(m3mu8_url,null,this.getHeader()));
|
||||
const m3u8_result = m3u8_res.match(/"url": getVideoInfo\("(.*?)"\),/)[1];
|
||||
const bt_token = m3u8_res.match(/<script>var bt_token = "(.*?)"/)[1];
|
||||
let m3u8_token_key = await (await this.fetch("https://danmu.yhdmjx.com/js/play.js",null,this.getHeader()));
|
||||
m3u8_token_key = m3u8_token_key.match(/var _token_key=CryptoJS\['enc'\]\['Utf8'\]\[_0x17f1\('67','qETJ'\)\]\((.*?\))/)[1];
|
||||
m3u8_token_key = this.decrypt_token_key(m3u8_token_key);
|
||||
this.playUrl = await this.getVideoInfo(m3u8_result, m3u8_token_key, bt_token);
|
||||
}
|
||||
|
||||
decrypt_token_key(toekn_key){
|
||||
var _0xod4 = 'jsjiami.com.v6',
|
||||
_0x175e = [_0xod4, 'JMOsw6omwoDCmw==', 'wp3DkSx5Eg==', 'HB7CscOJfS3DuUjDv2bDjsOmwr3Cm8KcwoI=', 'fR/Dqg==', 'ShRGTcKa', 'w5Y8VBs=', 'esKYKQ==', 'FgIdwrPDnMKOw7k=', 'HhXCmA==', 'woNrRsKSwpnDvcKfw4g=', 'ezBn', 'w43DkcK5w4MaJiE=', 'w44Ob8KjwrjCrMKtUA==', 'HwtswqI=', 'YsKnwrRawro=', 'Sm/CpQXCjz4RH8ORSXw=', 'IsO6w64=', 'T8OeAQ==', 'VcK4Hg==', 'csOmfBJ4', 'd8OAcA5L', 'Tn4RL2s=', 'w7goGizCmw==', 'w6XDlcOGwpoY', 'TsK7wpNPwrg=', 'w7J1CzLCnsO+HA==', 'w4XDkcK0w5YXPg==', 'S8KoCcKS', 'PcKWHcK/Eg==', 'Z2oMJ3rCiw==', 'YsKMG8KMwo0=', 'QsOecgRIwp4=', 'dFzDkUUxw48Qw7nCmX3CicODCMKnw74IOg==', 'acO2KU1B', 'wrAnw6DDrg==', 'w5MsScKwwoA=', 'wohZG8KhBg==', 'b8OieSpZ', 'w4ZmEsORw6I=', 'w7jDhxvCh8KY', 'w7wQa8KFwr0=', 'IMObw4E3wqU=', 'JsOjw5Erwrg=', 'w6MwcsKOwqU=', 'b8KIwqF0wrs=', 'XhXDvT52', 'wrDCmirChSE=', 'w5t1wpvDuwE=', 'XA7CtsKeEA==', 'wonCvVthw78=', 'U8KPP8KMwq4=', 'wp7DhCxjGTU=', 'woPDoBdiEQ==', 'HjzCrE/Dvg==', 'SsOQOWHCgg==', 'w6NdwoXCkMOx', 'w6shYWQ/', 'eE/Cgg==', 'XW/Csj7CmDoA', 'w680OynCgcK5BA8=', 'w4PDoMOTwrog', 'w5R7wqbCpMOPwrMxUcOiM8OuMVLCisKKFsOXAcOWY8O6w5hM', 'WwjDoht0', 'PzXCiHHDiA==', 'QMOFaMKcfQ==', 'bz9bVsK+', 'w7Npwp/DsB9ONw==', 'EcOPBQ3Cig==', 'woHDnzk=', 'DsKQLxtd', 'R8OCJmk=', 'wp55O8On', 'bkXCmhTCqg==', 'w7vDgx3Cg8KU', 'w6nDo8KdAn8=', 'O8KwP8KEDw==', 'wqzCtCHCtCg=', 'w5nDgmYhw5c=', 'wp0OZsK+w4A=', 'wrFxe8KFwp/DvcKew4EbN8K7BMORMx3DuxtOVELChsOEIQ==', 'BMKWRDc=', 'IS5dw6nDhQ==', 'w6geeG8t', 'SsK/wohywpc=', 'LBlnw7jDkA==', 'wodiLsOg', 'Ig5Ow7/Dlw==', 'TMOOewRZwoQ=', 'LjnClV7DvA==', 'woZsO8KcPQ==', 'eWvClMKeKQ==', 'wq1KWsKswr8=', 'w4p+AGFa', 'C8O7F8K2CQcKFxxgwo5sfh3DpAFV', 'w6XDqMKoCw==', 'w6Z8wqnCpMOe', 'w6x/CHRhXhV/w7I=', 'wrvClndGw5Y=', 'VsKcwo5RwoA=', 'ZcK2BMKmLQ==', 'BCcPwofDvQ==', 'eGEsF20=', 'eMOcUglq', 'w4U9XEAMw4/Dm3rCuzpxTg/DvyDDvMONH8OTwpTCtsKbw6k=', 'VwXDkTxG', 'CzHCrl3DkQ==', 'w7PDkVovw4o=', 'dsONYDdj', 'w79pwr3Duh4=', 'w55kwoLCpMOR', 'w6gbc8KGwpw=', 'bCnCtMKCCg==', 'Php5w64=', 'w7x8wpfDtiQ=', 'w4TDk2AB', 'w7lowoHDtgQ=', 'DB/CsnjDuQ==', 'woTDl8K3wp4S', 'wr0Ewq7Dq8Oh', 'GmHCpGxN', 'w5/Dm2XDpw==', 'w5bDpsOCwqoA', 'YMKowphwwqc=', 'ahFuVsK9', 'w57DtEsbw7k=', 'w6LDo8Kq', 'UsKzE8KO', 'w6xlBFl9', 'WMOgIXbCiw==', 'DBhjw5fDhQ==', 'w6pUwoHCnsOs', 'ZF7CtsKiHg==', 'wos4w7wCwr0c', 'JlXCjlpyw5pMw6s=', 'TcO1N8K4wqE=', 'SMOzTcKYRw==', 'w4HDosOzwrUc', 'wqxdwqE2wrY=', 'A8OPGQjChw==', 'wpkxasOxBQ==', 'wpIZSA==', 'VsOnEEvChg==', 'bMOSZy93', 'c8OwZsKnRQ==', 'SsK/wpBHwoQ=', 'ccOsHVFf', 'w6ZKYMO8w6rDr8OrCHxdwqtdwrDDksOMccKZwobDpQPDgsOrAMKXF8OMPcOySsKuL8KLwq8vwr/DkjbDqsOCbRrCkcOTTFfDlsKkw7PChCM1wqTDisKawpHDt8KCQRY3w6DDgMKVw6M=', 'w7hYwq3ClsOa', 'Fic5wqLDiw==', 'KcKqHCVV', 'wqvDucKSwrs/', 'YcOfQcKPSQ==', 'wpjClmNRw7U=', 'wpHClSnCizg=', 'w4PDh8Kw', 'JsOYGwnCpw==', 'w5vDksK8w6sl', 'U8ORCHt+Rw==', 'woVxO8OSJQ==', 'b37Chw/Clw==', 'w6ViBXZlQg==', 'UwPCucKQCgU=', 'UMOeZgs=', 'MTLCtW7Dq2o=', 'wqAQwr/DqcOi', 'DiLDtsOOZsKEbsOrGm03EcKEwr/CplQ=', 'dzPDnRhP', 'wplgT8KBwow=', 'XUzCg8KF', 'K8KTCcKrCQ==', 'w6ZKJMOw', 'LxTCmn/DsA==', 'wooObcKBw6g=', 'w5djwofDoTQ=', 'VMOHeAts', 'Kw96w7bDig==', 'wogpw6crwps=', 'PDzDn8OcWQ==', 'wrfCoWxfw4E=', 'wrrDv8KDwoI+', 'w7hnwrjCssOq', 'w4LDrMOwwpMp', 'S2vCvyjCjic=', 'w6DDoT7CtcKNwoo=', 'wp1Lw5zCpj4=', 'RsOEGcKVwps=', 'woxlwo0twoM=', 'MAZYw7TDmg==', 'w7ENEC7CgA==', 'wolVw4jCryEqVMOYdg==', 'wp45w6cCwqY=', 'w6UhVHs1', 'QBZFW8KH', 'Ozhvw7vDkQ==', 'E8O4DAXCnA==', 'AcOXR8OZ', 'w4NhwqbCk8O6', 'f8OyScKpRRJTBQd9w4t3PgzCtRMQwrjDrUZ9Z8Od', 'A8OHw7Inwoc=', 'wqlQE8KHDg==', 'wqYbZMOVMg==', 'w57DuVwAw60=', 'W8OyQMKtVAk=', 'w60PBwXCng==', 'w4h7w70gw55WbHfDuR0VQ8K+asOxwoJn', 'w5rDhMK2w5gX', 'N0HCklc=', 'w58TTsK4wrg=', 'w7dXNMOhw79pwqBMw70=', 'SsKQCcKf', 'KSXCumrDug==', 'woc1w7gUwrEEw7s=', 'wplowqIvwoFGMQ==', 'acO9DcK+', 'UMKBwpJNwp1ZfA==', 'wpV+NsKCLQ==', 'w5PDrMOrwo4iwrcU', 'XHjCoyPCkw==', 'w5kOUsKkwqXCqcKm', 'QMKRAcKDDMK+AA==', 'bMKoNcKgwrc=', 'w6k0HTPClw==', 'wpUYYcOcBA==', 'w6k6BjnCnMK9', 'w5HDnncPw60=', 'jEsjiamLiI.coRVzmBz.v6gWBKxrg=='];
|
||||
(function (_0x2410c8, _0x4731c1, _0x21bfda) {
|
||||
var _0x12d6b4 = function (_0x56792e, _0x26ff38, _0x14a967, _0x1f013c, _0x21c988) {
|
||||
_0x26ff38 = _0x26ff38 >> 0x8,
|
||||
_0x21c988 = 'po';
|
||||
var _0x10be12 = 'shift',
|
||||
_0x47704d = 'push';
|
||||
if (_0x26ff38 < _0x56792e) {
|
||||
while (--_0x56792e) {
|
||||
_0x1f013c = _0x2410c8[_0x10be12]();
|
||||
if (_0x26ff38 === _0x56792e) {
|
||||
_0x26ff38 = _0x1f013c;
|
||||
_0x14a967 = _0x2410c8[_0x21c988 + 'p']();
|
||||
} else if (_0x26ff38 && _0x14a967['replace'](/[ELIRVzBzgWBKxrg=]/g, '') === _0x26ff38) {
|
||||
_0x2410c8[_0x47704d](_0x1f013c);
|
||||
}
|
||||
}
|
||||
_0x2410c8[_0x47704d](_0x2410c8[_0x10be12]());
|
||||
}
|
||||
return 0xa44cc;
|
||||
};
|
||||
return _0x12d6b4(++_0x4731c1, _0x21bfda) >> _0x4731c1 ^ _0x21bfda;
|
||||
}(_0x175e, 0x15c, 0x15c00));
|
||||
|
||||
var _0x17f1 = function (_0x3abb24, _0x9f8a97) {
|
||||
_0x3abb24 = ~~'0x' ['concat'](_0x3abb24);
|
||||
var _0x411694 = _0x175e[_0x3abb24];
|
||||
if (_0x17f1['nIHPps'] === undefined) {
|
||||
(function () {
|
||||
var _0x264909 = typeof window !== 'undefined' ? window : typeof process === 'object' && typeof require === 'function' && typeof global === 'object' ? global : this;
|
||||
var _0x52b78f = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
// _0x264909['atob'] || (_0x264909['atob'] = function (_0x202c2d) {
|
||||
// var _0x2e97e4 = String(_0x202c2d)['replace'](/=+$/, '');
|
||||
// for (var _0x488147 = 0x0,
|
||||
// _0x1702c1, _0x5d977a, _0x1a87f9 = 0x0,
|
||||
// _0x2378be = ''; _0x5d977a = _0x2e97e4['charAt'](_0x1a87f9++); ~_0x5d977a && (_0x1702c1 = _0x488147 % 0x4 ? _0x1702c1 * 0x40 + _0x5d977a : _0x5d977a, _0x488147++ % 0x4) ? _0x2378be += String['fromCharCode'](0xff & _0x1702c1 >> (-0x2 * _0x488147 & 0x6)) : 0x0) {
|
||||
// _0x5d977a = _0x52b78f['indexOf'](_0x5d977a);
|
||||
// }
|
||||
// return _0x2378be;
|
||||
// });
|
||||
}());
|
||||
var _0x5cedb3 = function (_0x19b8a7, _0x9f8a97) {
|
||||
var _0x2b0e6a = [],
|
||||
_0x4f25fe = 0x0,
|
||||
_0x29acd8,
|
||||
_0x2643f2 = '',
|
||||
_0x58e8bd = '';
|
||||
_0x19b8a7 = atob(_0x19b8a7);
|
||||
for (var _0x1ac59f = 0x0,
|
||||
_0x3346bd = _0x19b8a7['length']; _0x1ac59f < _0x3346bd; _0x1ac59f++) {
|
||||
_0x58e8bd += '%' + ('00' + _0x19b8a7['charCodeAt'](_0x1ac59f)['toString'](0x10))['slice'](-0x2);
|
||||
}
|
||||
_0x19b8a7 = decodeURIComponent(_0x58e8bd);
|
||||
for (var _0xb0c930 = 0x0; _0xb0c930 < 0x100; _0xb0c930++) {
|
||||
_0x2b0e6a[_0xb0c930] = _0xb0c930;
|
||||
}
|
||||
for (_0xb0c930 = 0x0; _0xb0c930 < 0x100; _0xb0c930++) {
|
||||
_0x4f25fe = (_0x4f25fe + _0x2b0e6a[_0xb0c930] + _0x9f8a97['charCodeAt'](_0xb0c930 % _0x9f8a97['length'])) % 0x100;
|
||||
_0x29acd8 = _0x2b0e6a[_0xb0c930];
|
||||
_0x2b0e6a[_0xb0c930] = _0x2b0e6a[_0x4f25fe];
|
||||
_0x2b0e6a[_0x4f25fe] = _0x29acd8;
|
||||
}
|
||||
_0xb0c930 = 0x0;
|
||||
_0x4f25fe = 0x0;
|
||||
for (var _0x4f50dd = 0x0; _0x4f50dd < _0x19b8a7['length']; _0x4f50dd++) {
|
||||
_0xb0c930 = (_0xb0c930 + 0x1) % 0x100;
|
||||
_0x4f25fe = (_0x4f25fe + _0x2b0e6a[_0xb0c930]) % 0x100;
|
||||
_0x29acd8 = _0x2b0e6a[_0xb0c930];
|
||||
_0x2b0e6a[_0xb0c930] = _0x2b0e6a[_0x4f25fe];
|
||||
_0x2b0e6a[_0x4f25fe] = _0x29acd8;
|
||||
_0x2643f2 += String['fromCharCode'](_0x19b8a7['charCodeAt'](_0x4f50dd) ^ _0x2b0e6a[(_0x2b0e6a[_0xb0c930] + _0x2b0e6a[_0x4f25fe]) % 0x100]);
|
||||
}
|
||||
return _0x2643f2;
|
||||
};
|
||||
_0x17f1['RtnfNa'] = _0x5cedb3;
|
||||
_0x17f1['afYpDj'] = {};
|
||||
_0x17f1['nIHPps'] = !![];
|
||||
}
|
||||
var _0x5dc739 = _0x17f1['afYpDj'][_0x3abb24];
|
||||
if (_0x5dc739 === undefined) {
|
||||
if (_0x17f1['OqAPEJ'] === undefined) {
|
||||
_0x17f1['OqAPEJ'] = !![];
|
||||
}
|
||||
_0x411694 = _0x17f1['RtnfNa'](_0x411694, _0x9f8a97);
|
||||
_0x17f1['afYpDj'][_0x3abb24] = _0x411694;
|
||||
} else {
|
||||
_0x411694 = _0x5dc739;
|
||||
}
|
||||
return _0x411694;
|
||||
};
|
||||
return eval(toekn_key)
|
||||
}
|
||||
|
||||
async getVideoInfo(data,key,iv) {
|
||||
/*
|
||||
Crypto v3.1.2
|
||||
code.google.com/p/crypto-js
|
||||
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||
code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
var Crypto=Crypto||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
|
||||
r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
|
||||
32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,
|
||||
2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},
|
||||
q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);
|
||||
a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,
|
||||
e)).finalize(b)}}});var n=d.algo={};return d}(Math);
|
||||
(function(){var u=Crypto,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<
|
||||
l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
|
||||
(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=Crypto,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},
|
||||
_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),
|
||||
f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,
|
||||
m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,
|
||||
E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/
|
||||
4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
|
||||
(function(){var u=Crypto,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,
|
||||
l)}})();
|
||||
Crypto.lib.Cipher||function(u){var p=Crypto,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},
|
||||
finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=
|
||||
c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,
|
||||
e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,
|
||||
this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,
|
||||
1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},
|
||||
decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,
|
||||
b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();
|
||||
(function(){for(var u=Crypto,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,
|
||||
16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>
|
||||
8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=
|
||||
d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();
|
||||
|
||||
iv = Crypto.enc.Utf8.parse(iv);
|
||||
key = Crypto.enc.Utf8.parse(key);
|
||||
|
||||
let result = Crypto.AES.decrypt(data, key,
|
||||
{
|
||||
iv : iv,
|
||||
mode : Crypto.mode.CBC,
|
||||
})
|
||||
return Crypto.enc.Utf8.stringify(result).toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
let spider = new MxAnimeSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
362
cat/tjs/js/nangua.js
Normal file
@ -0,0 +1,362 @@
|
||||
/*
|
||||
* @File : nangua.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/18 10:54
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 南瓜影视
|
||||
*/
|
||||
import {Crypto, jinja2, _} from "../lib/cat.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
|
||||
function stripHtmlTag(src) {
|
||||
return src
|
||||
.replace(/<\/?[^>]+(>|$)/g, '')
|
||||
.replace(/&.{1,5};/g, '')
|
||||
.replace(/\s{2,}/g, ' ');
|
||||
}
|
||||
|
||||
function formatPlayUrl(src, name) {
|
||||
return name
|
||||
.trim()
|
||||
.replaceAll(src, '')
|
||||
.replace(/<|>|《|》/g, '')
|
||||
.replace(/\$|#/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
|
||||
class NanGuaSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = 'http://ys.changmengyun.com';
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🎃┃南瓜影视┃🎃"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "南瓜影视"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "nangua"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
this.danmuStaus = true
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
let t = new Date().getTime().toString();
|
||||
return {
|
||||
'version_name': '1.0.6',
|
||||
'version_code': '6',
|
||||
'package_name': 'com.app.nanguatv',
|
||||
'sign': Crypto.MD5('c431ea542cee9679#uBFszdEM0oL0JRn@' + t).toString().toLowerCase(),
|
||||
'imei': 'c431ea542cee9679',
|
||||
'timeMillis': t,
|
||||
'User-Agent': 'okhttp/4.6.0'
|
||||
}
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let data = JSON.parse(await this.fetch(this.siteUrl + '/api.php/provide/home_nav', null, this.getHeader()));
|
||||
for (const key in data) {
|
||||
if (data[key].name !== '精选') this.classes.push({
|
||||
type_id: data[key].id, type_name: data[key].name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
this.filterObj = {
|
||||
"2": [{
|
||||
"key": "class",
|
||||
"name": "类型",
|
||||
"value": [{"n": "全部", "v": "类型"}, {"n": "国产剧", "v": "国产剧"}, {"n": "港台剧", "v": "港台剧"}]
|
||||
}, {
|
||||
"key": "area", "name": "地区", "value": [{"n": "全部", "v": "地区"}, {"n": "内地", "v": "内地"}, {
|
||||
"n": "香港地区", "v": "香港地区"
|
||||
}, {"n": "台湾地区", "v": "台湾地区"}]
|
||||
}, {
|
||||
"key": "year",
|
||||
"name": "年份",
|
||||
"value": [{"n": "全部", "v": "年份"}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {
|
||||
"n": "2017", "v": "2017"
|
||||
}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "10年代", "v": "10年代"}, {
|
||||
"n": "00年代", "v": "00年代"
|
||||
}, {"n": "90年代", "v": "90年代"}, {"n": "80年代", "v": "80年代"}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{"n": "热播榜", "v": "热播榜"}, {"n": "好评榜", "v": "好评榜"}, {
|
||||
"n": "新上线", "v": "新上线"
|
||||
}]
|
||||
}], "1": [{
|
||||
"key": "class", "name": "类型", "value": [{"n": "全部", "v": "类型"}, {"n": "动作片", "v": "动作片"}, {
|
||||
"n": "喜剧片", "v": "喜剧片"
|
||||
}, {"n": "爱情片", "v": "爱情片"}, {"n": "科幻片", "v": "科幻片"}, {
|
||||
"n": "恐怖片", "v": "恐怖片"
|
||||
}, {"n": "剧情片", "v": "剧情片"}, {"n": "战争片", "v": "战争片"}, {"n": "惊悚片", "v": "惊悚片"}]
|
||||
}, {
|
||||
"key": "area", "name": "地区", "value": [{"n": "全部", "v": "地区"}, {"n": "华语", "v": "华语"}, {
|
||||
"n": "香港地区", "v": "香港地区"
|
||||
}, {"n": "美国", "v": "美国"}, {"n": "欧洲", "v": "欧洲"}, {"n": "韩国", "v": "韩国"}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {"n": "台湾地区", "v": "台湾地区"}, {"n": "泰国", "v": "泰国"}, {
|
||||
"n": "台湾地区", "v": "台湾地区"
|
||||
}, {"n": "印度", "v": "印度"}, {"n": "其它", "v": "其它"}]
|
||||
}, {
|
||||
"key": "year",
|
||||
"name": "年份",
|
||||
"value": [{"n": "全部", "v": "年份"}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {
|
||||
"n": "2017", "v": "2017"
|
||||
}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "10年代", "v": "10年代"}, {
|
||||
"n": "00年代", "v": "00年代"
|
||||
}, {"n": "90年代", "v": "90年代"}, {"n": "80年代", "v": "80年代"}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{"n": "热播榜", "v": "热播榜"}, {"n": "好评榜", "v": "好评榜"}, {
|
||||
"n": "新上线", "v": "新上线"
|
||||
}]
|
||||
}], "4": [{
|
||||
"key": "class", "name": "类型", "value": [{"n": "全部", "v": "类型"}, {"n": "国产漫", "v": "国产漫"}, {
|
||||
"n": "欧美漫", "v": "欧美漫"
|
||||
}, {"n": "日韩漫", "v": "日韩漫"}, {"n": "港台漫", "v": "港台漫"}]
|
||||
}, {
|
||||
"key": "area",
|
||||
"name": "地区",
|
||||
"value": [{"n": "全部", "v": "地区"}, {"n": "中国大陆", "v": "中国大陆"}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {"n": "韩国", "v": "韩国"}, {"n": "欧美", "v": "欧美"}, {"n": "其它", "v": "其它"}]
|
||||
}, {
|
||||
"key": "year",
|
||||
"name": "年份",
|
||||
"value": [{"n": "全部", "v": "年份"}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {
|
||||
"n": "2017", "v": "2017"
|
||||
}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "10年代", "v": "10年代"}, {
|
||||
"n": "00年代", "v": "00年代"
|
||||
}, {"n": "90年代", "v": "90年代"}, {"n": "80年代", "v": "80年代"}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{"n": "热播榜", "v": "热播榜"}, {"n": "新上线", "v": "新上线"}]
|
||||
}, {
|
||||
"key": "total",
|
||||
"name": "状态",
|
||||
"value": [{"n": "全部", "v": "状态"}, {"n": "连载", "v": "连载"}, {"n": "完结", "v": "完结"}]
|
||||
}], "3": [{
|
||||
"key": "class",
|
||||
"name": "类型",
|
||||
"value": [{"n": "全部", "v": "类型"}, {"n": "大陆", "v": "大陆"}, {"n": "港台", "v": "港台"}, {
|
||||
"n": "日韩", "v": "日韩"
|
||||
}, {"n": "欧美", "v": "欧美"}]
|
||||
}, {
|
||||
"key": "area",
|
||||
"name": "地区",
|
||||
"value": [{"n": "全部", "v": "地区"}, {"n": "内地", "v": "内地"}, {"n": "港台", "v": "港台"}, {
|
||||
"n": "日韩", "v": "日韩"
|
||||
}, {"n": "欧美", "v": "欧美"}, {"n": "其它", "v": "其它"}]
|
||||
}, {
|
||||
"key": "year",
|
||||
"name": "年份",
|
||||
"value": [{"n": "全部", "v": "年份"}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {
|
||||
"n": "2017", "v": "2017"
|
||||
}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "10年代", "v": "10年代"}, {
|
||||
"n": "00年代", "v": "00年代"
|
||||
}, {"n": "90年代", "v": "90年代"}, {"n": "80年代", "v": "80年代"}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{"n": "热播榜", "v": "热播榜"}, {"n": "新上线", "v": "新上线"}]
|
||||
}], "46": [{
|
||||
"key": "class", "name": "类型", "value": [{"n": "全部", "v": "类型"}, {"n": "日韩剧", "v": "日韩剧"}, {
|
||||
"n": "欧美剧", "v": "欧美剧"
|
||||
}, {"n": "海外剧", "v": "海外剧"}]
|
||||
}, {
|
||||
"key": "area",
|
||||
"name": "地区",
|
||||
"value": [{"n": "全部", "v": "地区"}, {"n": "韩国", "v": "韩国"}, {"n": "美剧", "v": "美剧"}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {"n": "泰国", "v": "泰国"}, {"n": "英国", "v": "英国"}, {"n": "新加坡", "v": "新加坡"}, {
|
||||
"n": "其他", "v": "其他"
|
||||
}]
|
||||
}, {
|
||||
"key": "year",
|
||||
"name": "年份",
|
||||
"value": [{"n": "全部", "v": "年份"}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {
|
||||
"n": "2017", "v": "2017"
|
||||
}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "10年代", "v": "10年代"}, {
|
||||
"n": "00年代", "v": "00年代"
|
||||
}, {"n": "90年代", "v": "90年代"}, {"n": "80年代", "v": "80年代"}]
|
||||
}, {
|
||||
"key": "by", "name": "排序", "value": [{"n": "热播榜", "v": "热播榜"}, {"n": "好评榜", "v": "好评榜"}, {
|
||||
"n": "新上线", "v": "新上线"
|
||||
}]
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
async parseVodShortListFromJSONByHome(obj){
|
||||
let vod_list = []
|
||||
for (const data of obj["video"]){
|
||||
let video_vod_list = await this.parseVodShortListFromJson(data["data"])
|
||||
vod_list.push(...video_vod_list)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const data of obj){
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = data["id"]
|
||||
vodShort.vod_name = data["name"]
|
||||
vodShort.vod_pic = data["img"]
|
||||
vodShort.vod_remarks = data["remarks"]
|
||||
if (_.isEmpty(vodShort.vod_remarks)){
|
||||
vodShort.vod_remarks = data["msg"]
|
||||
}
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromJsonBySearch(obj) {
|
||||
let videos = [];
|
||||
obj.forEach(function (it) {
|
||||
videos.push({
|
||||
vod_id: it.id, vod_name: it["video_name"], vod_pic: it.img, vod_remarks: it["qingxidu"] + '/' + it.category,
|
||||
});
|
||||
});
|
||||
return videos
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_id = obj["id"]
|
||||
vodDetail.vod_name = obj["name"]
|
||||
vodDetail.vod_pic = obj["img"]
|
||||
vodDetail.type_name = obj["type"]
|
||||
vodDetail.vod_year = obj["year"]
|
||||
vodDetail.vod_content = stripHtmlTag(obj["info"])
|
||||
vodDetail.vod_remarks = '更新至: ' + obj["msg"] + ' / 评分: ' + obj["score"]
|
||||
let episodes = obj["player_info"];
|
||||
let playlist = {};
|
||||
episodes.forEach(function (it) {
|
||||
let playurls = it["video_info"];
|
||||
playurls.forEach(function (playurl) {
|
||||
let source = it.show;
|
||||
let t = formatPlayUrl(vodDetail.vod_name, playurl.name);
|
||||
if (t.length === 0) t = playurl.name.trim();
|
||||
if (!playlist.hasOwnProperty(source)) {
|
||||
playlist[source] = [];
|
||||
}
|
||||
playlist[source].push(t + '$' + playurl.url);
|
||||
});
|
||||
});
|
||||
vodDetail.vod_play_from = _.keys(playlist).join('$$$');
|
||||
let urls = _.values(playlist);
|
||||
let vod_play_url = [];
|
||||
urls.forEach(function (it) {
|
||||
vod_play_url.push(it.join('#'));
|
||||
});
|
||||
vodDetail.vod_play_url = vod_play_url.join('$$$');
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let data = JSON.parse(await this.fetch(this.siteUrl + '/api.php/provide/home_data?app=ylys&devices=android&imei=c431ea542cee9679&deviceModel=Subsystem%20for%20Android(TM)&deviceVersion=33&appVersionName=1.0.9&deviceScreen=427*250&appVersionCode=9&deviceBrand=Windows', null, this.getHeader()));
|
||||
this.homeVodList = await this.parseVodShortListFromJSONByHome(data)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
if (pg <= 0 || typeof (pg) == 'undefined') pg = 1;
|
||||
let reqUrl = this.siteUrl + '/api.php/provide/vod_list?app=ylys&id=' + tid + '&page=' + pg + '&imei=c431ea542cee9679&';
|
||||
reqUrl += jinja2('area={{ext.area}}&year={{ext.year}}&type={{ext.class}}&total={{ext.total}}&order={{ext.by}}', {ext: extend});
|
||||
let data = JSON.parse(await this.fetch(reqUrl, null, this.getHeader())).list;
|
||||
this.vodList = await this.parseVodShortListFromJson(data)
|
||||
let pgChk = JSON.parse(await this.fetch(this.siteUrl + '/api.php/provide/vod_list?app=ylys&id=' + tid + '&page=' + (parseInt(pg) + 1) + '&imei=c431ea542cee9679&', null, this.getHeader())).msg;
|
||||
this.count = (pgChk === 'ok') ? parseInt(pg) + 1 : parseInt(pg);
|
||||
this.limit = 20
|
||||
this.total = this.limit * this.count
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let data = JSON.parse(await this.fetch(this.siteUrl + '/api.php/provide/vod_detail?app=ylys&imei=c431ea542cee9679&id=' + id, null, this.getHeader())).data;
|
||||
this.vodDetail = await this.parseVodDetailfromJson(data)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let data = JSON.parse(await this.fetch(this.siteUrl + '/api.php/provide/search_result_more?app=ylys&video_name=' + wd + '&pageSize=20&tid=0&imei=c431ea542cee9679&page=0', null, this.getHeader())).data;
|
||||
this.vodList = await this.parseVodShortListFromJsonBySearch(data)
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
try {
|
||||
if (id.indexOf('m3u8') !== -1) {
|
||||
this.playUrl = id.split('url=')[1]
|
||||
} else if (id.indexOf(',') !== -1) {
|
||||
let mjurl = id.split(',')[1]
|
||||
let jData = JSON.parse(await this.fetch(mjurl, null, this.getHeader()));
|
||||
this.playUrl = jData["data"]["url"]
|
||||
} else {
|
||||
let jData = JSON.parse(await this.fetch(id, null, this.getHeader()));
|
||||
this.playUrl = jData["data"]["url"]
|
||||
}
|
||||
} catch (e) {
|
||||
await this.jadeLog.error("播放失败")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
let spider = new NanGuaSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
261
cat/tjs/js/newvision.js
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* @File : newvision.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/20 14:14
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 新视觉影院
|
||||
*/
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {Crypto} from "../lib/cat.js";
|
||||
|
||||
|
||||
class NewVisionSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.6080yy3.com"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "新视觉影院"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🐼┃新视觉影院┃🐼"
|
||||
}
|
||||
getJSName() {
|
||||
return "newvision"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let navElements = $($("[class=\"nav-menu-items\"]")[0]).find("a")
|
||||
for (const navElement of navElements) {
|
||||
let type_id = Utils.getStrByRegex(/\/vodtype\/(.*?).html/, navElement.attribs.href)
|
||||
let type_name = navElement.attribs.title
|
||||
if (Utils.isNumeric(type_id)) {
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let elements = $("[class='scroll-content']").slice(1)
|
||||
let extend_list = []
|
||||
let type_key_list = [3, 1, 11, 2]
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let name = $($(elements[i]).find("a")[0]).text()
|
||||
let extend_dic = {"key": name, "name": name, "value": []}
|
||||
extend_dic["name"] = name
|
||||
extend_dic["value"].push({"n": "全部", "v": "0"})
|
||||
for (const ele of $(elements[i]).find("a").slice(1)) {
|
||||
let type_id_list = Utils.getStrByRegex(/\/vodshow\/(.*?).html/, ele.attribs.href).split("-")
|
||||
extend_dic["value"].push({
|
||||
"n": $(ele).text(), "v": decodeURIComponent(type_id_list[type_key_list[i]])
|
||||
})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const type_dic of this.classes) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "最近更新") {
|
||||
let url = this.siteUrl + `/vodshow/${type_id}-----------.html`
|
||||
let $ = await this.getHtml(url)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let items = $('.module-item');
|
||||
let vod_list = [];
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort()
|
||||
let oneA = $(item).find('.module-item-cover .module-item-pic a').first();
|
||||
vodShort.vod_id = oneA.attr('href');
|
||||
vodShort.vod_name = oneA.attr('title');
|
||||
vodShort.vod_pic = $(item).find('.module-item-cover .module-item-pic img').first().attr('data-src');
|
||||
if (vodShort.vod_pic.indexOf("img.php?url=") > 0) {
|
||||
vodShort.vod_pic = vodShort.vod_pic.split("img.php?url=")[1]
|
||||
}
|
||||
vodShort.vod_remarks = $(item).find('.module-item-text').first().text();
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const result of obj["Data"]["result"]){
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = result["vod_url"].replaceAll(this.siteUrl,"")
|
||||
vodShort.vod_pic = result["vod_pic"]
|
||||
vodShort.vod_name = result["vod_name"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let html = $.html()
|
||||
let vodDetail = new VodDetail()
|
||||
let vodDetailElement = $("[class=\"box view-heading\"]")
|
||||
vodDetail.vod_name = $($(vodDetailElement).find("[class=\"page-title\"]")).text()
|
||||
let typeElements = $($(vodDetailElement).find("[class=\"tag-link\"]").find("a"))
|
||||
vodDetail.vod_area = $($(vodDetailElement).find("[class=\"tag-link\"]").slice(-1)[0]).text()
|
||||
let type_list = []
|
||||
for (const typeElement of typeElements) {
|
||||
type_list.push($(typeElement).text())
|
||||
}
|
||||
vodDetail.type_name = type_list.join("/")
|
||||
let itemElements = $(vodDetailElement).find("[class=\"video-info-items\"]")
|
||||
vodDetail.vod_director = $($(itemElements[0]).find("a")).text()
|
||||
let actor_list = []
|
||||
for (const actorElement of $(itemElements[1]).find("a")) {
|
||||
actor_list.push($(actorElement).text())
|
||||
}
|
||||
vodDetail.vod_pic = $($(vodDetailElement).find("[class=\"module-item-pic\"]")).find("img")[0].attribs["data-src"]
|
||||
vodDetail.vod_actor = actor_list.join("/")
|
||||
vodDetail.vod_year = $($(itemElements[2]).find("[class=\"video-info-item\"]")).text()
|
||||
vodDetail.vod_remarks = $($(itemElements[3]).find("[class=\"video-info-item\"]")).text()
|
||||
vodDetail.vod_content = $($(itemElements[5]).find("[class=\"video-info-item video-info-content vod_content\"]")).text().replaceAll("\n", "\t").replaceAll("\t收起", "")
|
||||
let playerformatElements = $("[class=\"module-tab-item tab-item\"]")
|
||||
let playUrlElements = $("[class=\"scroll-content\"]")
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
for (let i = 0; i < playerformatElements.length; i++) {
|
||||
let playFormatElement = playerformatElements[i]
|
||||
let format_name = playFormatElement.attribs["data-dropdown-value"]
|
||||
if (format_name.indexOf("夸克") === -1) {
|
||||
vod_play_from_list.push(format_name)
|
||||
let vodItems = []
|
||||
for (const playUrlElement of $(playUrlElements[i]).find("a")) {
|
||||
let episodeName = $(playUrlElement).text()
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
|
||||
}
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let urlParams = [tid.toString(), "", "", "", "", "", "", "", pg.toString(), "", "", ""]
|
||||
let type_key_list = [3, 11, 1, 2]
|
||||
if (extend["全部剧情"] !== undefined && extend["全部剧情"] !== "0") {
|
||||
urlParams[type_key_list[0]] = extend["全部剧情"]
|
||||
}
|
||||
if (extend["全部时间"] !== undefined && extend["全部时间"] !== "0") {
|
||||
urlParams[type_key_list[1]] = extend["全部时间"]
|
||||
}
|
||||
if (extend["全部地区"] !== undefined && extend["全部地区"] !== "0") {
|
||||
urlParams[type_key_list[2]] = extend["全部地区"]
|
||||
}
|
||||
if (extend["时间排序"] !== undefined && extend["时间排序"] !== "0") {
|
||||
urlParams[type_key_list[3]] = extend["时间排序"]
|
||||
}
|
||||
let reqUrl = this.siteUrl + '/index.php/vodshow/' + urlParams.join("-") + '.html';
|
||||
let $ = await this.getHtml(reqUrl)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
uic(url,uid){
|
||||
let ut = Crypto.enc.Utf8.parse('2890'+uid+'tB959C')
|
||||
let mm = Crypto.enc.Utf8.parse("2F131BE91247866E")
|
||||
let decrypted = Crypto.AES.decrypt(url, ut, {iv: mm, mode: Crypto.mode.CBC, padding: Crypto.pad.Pkcs7});
|
||||
return Crypto.enc.Utf8.stringify(decrypted);
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
let playUrl = $("[id=\"bfurl\"]")[0].attribs.href
|
||||
if (playUrl.indexOf("http") > -1){
|
||||
this.playUrl = playUrl
|
||||
}else{
|
||||
//需要解析URL,支持弹幕
|
||||
let newUrl = "https://jiexi.xn--1lq90i13mxk5bolhm8k.xn--fiqs8s/player/ec.php?code=ak&if=1&url=" + playUrl
|
||||
let play$ = await this.getHtml(newUrl)
|
||||
let playHtml = play$.html()
|
||||
let playConfig = JSON.parse(Utils.getStrByRegex(/let ConFig = (.*?),box = /,playHtml))
|
||||
this.playUrl = this.uic(playConfig["url"],playConfig["config"]["uid"])
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = `http://123.207.150.253/zxapi/public/?service=App.F.Fetch&req_p=${wd}&type=6080`
|
||||
let content = await this.fetch(url,null,this.getHeader())
|
||||
this.vodList = await this.parseVodShortListFromJson(JSON.parse(content))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new NewVisionSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
243
cat/tjs/js/nivod.js
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* @File : nivod.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/19 14:23
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {getHeader, createSign, desDecrypt, ChannelResponse, getVod} from "../lib/nivid_object.js"
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
class NivodSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://api.nivodz.com"
|
||||
|
||||
}
|
||||
getName() {
|
||||
return "👑┃泥视频┃墙👑"
|
||||
}
|
||||
getAppName() {
|
||||
return "泥视频"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "nivod"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
this.Remove18ChannelCode = parseInt(this.cfgObj["code"])
|
||||
let url = this.siteUrl + "/show/channel/list/WEB/3.2" + await createSign()
|
||||
let content = desDecrypt(await this.post(url, null, getHeader()))
|
||||
if (content !== null) {
|
||||
let channelResponse = new ChannelResponse()
|
||||
channelResponse.fromJsonString(content, this.Remove18ChannelCode)
|
||||
let filterUrl = this.siteUrl + "/show/filter/condition/WEB/3.2" + await createSign()
|
||||
let filterContent = desDecrypt(await this.post(filterUrl, null, getHeader()))
|
||||
if (filterContent !== null) {
|
||||
channelResponse.setChannelFilters(filterContent)
|
||||
this.classes = channelResponse.getClassList()
|
||||
this.filterObj = channelResponse.getFilters()
|
||||
}
|
||||
}
|
||||
}
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const cate_dic of obj) {
|
||||
for (const row of cate_dic.rows) {
|
||||
for (const cells of row.cells) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = cells.show["showIdCode"]
|
||||
vodShort.vod_pic = cells.img
|
||||
vodShort.vod_name = cells.title
|
||||
vodShort.vod_remarks = this.getVodRemarks(cells.show["hot"], cells.show["playResolutions"])
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(vod_dic) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_id = vod_dic["showIdCode"]
|
||||
vodDetail.vod_name = vod_dic["showTitle"]
|
||||
vodDetail.vod_remarks = this.getVodRemarks(vod_dic["hot"], vod_dic["playResolutions"])
|
||||
vodDetail.vod_pic = vod_dic["showImg"]
|
||||
vodDetail.vod_director = vod_dic["director"]
|
||||
vodDetail.vod_actor = vod_dic["actors"]
|
||||
vodDetail.vod_year = vod_dic["postYear"]
|
||||
vodDetail.vod_content = vod_dic["showDesc"]
|
||||
vodDetail.type_name = vod_dic["showTypeName"]
|
||||
vodDetail.vod_area = vod_dic["regionName"]
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
|
||||
getVodRemarks(hot, playResolutions) {
|
||||
let vod_remarks
|
||||
if (this.catOpenStatus) {
|
||||
vod_remarks = `清晰度:${playResolutions[0]}`
|
||||
} else {
|
||||
vod_remarks = `清晰度:${playResolutions[0]},热度:${(Math.floor(parseInt(hot) / 1000)).toString()}k`
|
||||
}
|
||||
return vod_remarks
|
||||
}
|
||||
|
||||
getExtendDic(extend, params) {
|
||||
if (extend["5"] === undefined) {
|
||||
delete params.year_range
|
||||
} else {
|
||||
if (extend["5"] === "0") {
|
||||
delete params.year_range
|
||||
} else {
|
||||
params.year_range = extend["5"]
|
||||
}
|
||||
}
|
||||
if (extend["1"] !== undefined) {
|
||||
params.sort_by = extend["1"]
|
||||
}
|
||||
if (extend["2"] !== undefined) {
|
||||
params.show_type_id = extend["2"]
|
||||
}
|
||||
if (extend["3"] !== undefined) {
|
||||
params.region_id = extend["3"]
|
||||
}
|
||||
if (extend["4"] !== undefined) {
|
||||
params.lang_id = extend["4"]
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let url = this.siteUrl + "/index/mobile/WAP/3.0" + await createSign()
|
||||
let content = desDecrypt(await this.post(url, null, getHeader()))
|
||||
if (content !== null) {
|
||||
let content_json = JSON.parse(content)
|
||||
let cate_list = content_json.list
|
||||
for (const cate_dic of cate_list) {
|
||||
for (const row of cate_dic.rows) {
|
||||
for (const cells of row.cells) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = cells.show["showIdCode"]
|
||||
vodShort.vod_pic = cells.img
|
||||
vodShort.vod_name = cells.title
|
||||
vodShort.vod_remarks = this.getVodRemarks(cells.show["hot"], cells.show["playResolutions"])
|
||||
this.homeVodList.push(vodShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let params = {
|
||||
"sort_by": "0",
|
||||
"channel_id": tid.toString(),
|
||||
"show_type_id": "0",
|
||||
"region_id": "0",
|
||||
"lang_id": "0",
|
||||
"year_range": "2023",
|
||||
"start": ((parseInt(pg) - 1) * 20).toString()
|
||||
}
|
||||
this.limit = 20;
|
||||
params = this.getExtendDic(extend, params)
|
||||
let url = this.siteUrl + "/show/filter/WEB/3.2" + await createSign(params)
|
||||
let content = desDecrypt(await this.post(url, params, getHeader()))
|
||||
if (content != null) {
|
||||
let content_json = JSON.parse(content)
|
||||
for (const vod_dic of content_json["list"]) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = vod_dic["showIdCode"]
|
||||
vodShort.vod_name = vod_dic["showTitle"]
|
||||
vodShort.vod_pic = vod_dic["showImg"]
|
||||
vodShort.vod_remarks = this.getVodRemarks(vod_dic["hot"], vod_dic["playResolutions"])
|
||||
this.vodList.push(vodShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let params = {
|
||||
"show_id_code": id.toString()
|
||||
}
|
||||
let url = this.siteUrl + "/show/detail/WEB/3.2" + await createSign(params)
|
||||
let content = desDecrypt(await this.post(url, params, getHeader()))
|
||||
if (content != null) {
|
||||
let content_json = JSON.parse(content)
|
||||
let vod_dic = content_json["entity"]
|
||||
this.vodDetail = await this.parseVodDetailfromJson(vod_dic)
|
||||
let niBaVodDetail = getVod(vod_dic["plays"], ["原画"], id.toString())
|
||||
this.vodDetail.vod_play_from = niBaVodDetail.vod_play_from
|
||||
this.vodDetail.vod_play_url = niBaVodDetail.vod_play_url
|
||||
}
|
||||
}
|
||||
async setSearch(wd, quick) {
|
||||
let params = {"cat_id": "1", "keyword": wd, "keyword_type": "0", "start": "0"}
|
||||
let url = this.siteUrl + "/show/search/WEB/3.2" + await createSign(params)
|
||||
let content = desDecrypt(await this.post(url, params, getHeader()))
|
||||
if (content != null) {
|
||||
let content_json = JSON.parse(content)
|
||||
for (const vod_dic of content_json["list"]) {
|
||||
let vod_detail = await this.parseVodDetailfromJson(vod_dic)
|
||||
this.vodList.push(vod_detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
async setPlay(flag, id, flags) {
|
||||
let playId = id.split("@")[0]
|
||||
let showId = id.split("@")[1]
|
||||
let params = {
|
||||
"show_id_code": showId,
|
||||
"play_id_code": playId
|
||||
}
|
||||
let url = this.siteUrl + "/show/play/info/WEB/3.2" + await createSign(params)
|
||||
let content = desDecrypt(await this.post(url, params,getHeader()))
|
||||
if (content != null) {
|
||||
let content_json = JSON.parse(content)
|
||||
this.playUrl = content_json["entity"]["playUrl"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new NivodSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
242
cat/tjs/js/nivod_18.js
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* @File : nivod18.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/19 14:23
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {getHeader, createSign, desDecrypt, ChannelResponse, getVod} from "../lib/nivid_object.js"
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
class Nivod18Spider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://api.nivodz.com"
|
||||
|
||||
}
|
||||
getName() {
|
||||
return "🔞┃泥视频18+┃🔞"
|
||||
}
|
||||
getAppName() {
|
||||
return "泥视频18+"
|
||||
}
|
||||
getJSName() {
|
||||
return "nivod_18"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
|
||||
async setClasses() {
|
||||
let url = this.siteUrl + "/show/channel/list/WEB/3.2" + await createSign()
|
||||
let content = desDecrypt(await this.post(url, null, getHeader()))
|
||||
if (content !== null) {
|
||||
let channelResponse = new ChannelResponse()
|
||||
channelResponse.fromJsonString(content, 2)
|
||||
let filterUrl = this.siteUrl + "/show/filter/condition/WEB/3.2" + await createSign()
|
||||
let filterContent = desDecrypt(await this.post(filterUrl, null, getHeader()))
|
||||
if (filterContent !== null) {
|
||||
channelResponse.setChannelFilters(filterContent)
|
||||
this.classes = channelResponse.getClassList()
|
||||
this.filterObj = channelResponse.getFilters()
|
||||
}
|
||||
}
|
||||
}
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const cate_dic of obj) {
|
||||
for (const row of cate_dic.rows) {
|
||||
for (const cells of row.cells) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = cells.show["showIdCode"]
|
||||
vodShort.vod_pic = cells.img
|
||||
vodShort.vod_name = cells.title
|
||||
vodShort.vod_remarks = this.getVodRemarks(cells.show["hot"], cells.show["playResolutions"])
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(vod_dic) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_id = vod_dic["showIdCode"]
|
||||
vodDetail.vod_name = vod_dic["showTitle"]
|
||||
vodDetail.vod_remarks = this.getVodRemarks(vod_dic["hot"], vod_dic["playResolutions"])
|
||||
vodDetail.vod_pic = vod_dic["showImg"]
|
||||
vodDetail.vod_director = vod_dic["director"]
|
||||
vodDetail.vod_actor = vod_dic["actors"]
|
||||
vodDetail.vod_year = vod_dic["postYear"]
|
||||
vodDetail.vod_content = vod_dic["showDesc"]
|
||||
vodDetail.type_name = vod_dic["showTypeName"]
|
||||
vodDetail.vod_area = vod_dic["regionName"]
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
|
||||
getVodRemarks(hot, playResolutions) {
|
||||
let vod_remarks
|
||||
if (this.catOpenStatus) {
|
||||
vod_remarks = `清晰度:${playResolutions[0]}`
|
||||
} else {
|
||||
vod_remarks = `清晰度:${playResolutions[0]},热度:${(Math.floor(parseInt(hot) / 1000)).toString()}k`
|
||||
}
|
||||
return vod_remarks
|
||||
}
|
||||
|
||||
getExtendDic(extend, params) {
|
||||
if (extend["5"] === undefined) {
|
||||
delete params.year_range
|
||||
} else {
|
||||
if (extend["5"] === "0") {
|
||||
delete params.year_range
|
||||
} else {
|
||||
params.year_range = extend["5"]
|
||||
}
|
||||
}
|
||||
if (extend["1"] !== undefined) {
|
||||
params.sort_by = extend["1"]
|
||||
}
|
||||
if (extend["2"] !== undefined) {
|
||||
params.show_type_id = extend["2"]
|
||||
}
|
||||
if (extend["3"] !== undefined) {
|
||||
params.region_id = extend["3"]
|
||||
}
|
||||
if (extend["4"] !== undefined) {
|
||||
params.lang_id = extend["4"]
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let url = this.siteUrl + "/index/mobile/WAP/3.0" + await createSign()
|
||||
let content = desDecrypt(await this.post(url, null, getHeader()))
|
||||
if (content !== null) {
|
||||
let content_json = JSON.parse(content)
|
||||
let cate_list = content_json.list
|
||||
for (const cate_dic of cate_list) {
|
||||
for (const row of cate_dic.rows) {
|
||||
for (const cells of row.cells) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = cells.show["showIdCode"]
|
||||
vodShort.vod_pic = cells.img
|
||||
vodShort.vod_name = cells.title
|
||||
vodShort.vod_remarks = this.getVodRemarks(cells.show["hot"], cells.show["playResolutions"])
|
||||
this.homeVodList.push(vodShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let params = {
|
||||
"sort_by": "0",
|
||||
"channel_id": tid.toString(),
|
||||
"show_type_id": "0",
|
||||
"region_id": "0",
|
||||
"lang_id": "0",
|
||||
"year_range": "2023",
|
||||
"start": ((parseInt(pg) - 1) * 20).toString()
|
||||
}
|
||||
this.limit = 20;
|
||||
params = this.getExtendDic(extend, params)
|
||||
let url = this.siteUrl + "/show/filter/WEB/3.2" + await createSign(params)
|
||||
let content = desDecrypt(await this.post(url, params, getHeader()))
|
||||
if (content != null) {
|
||||
let content_json = JSON.parse(content)
|
||||
for (const vod_dic of content_json["list"]) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = vod_dic["showIdCode"]
|
||||
vodShort.vod_name = vod_dic["showTitle"]
|
||||
vodShort.vod_pic = vod_dic["showImg"]
|
||||
vodShort.vod_remarks = this.getVodRemarks(vod_dic["hot"], vod_dic["playResolutions"])
|
||||
this.vodList.push(vodShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let params = {
|
||||
"show_id_code": id.toString()
|
||||
}
|
||||
let url = this.siteUrl + "/show/detail/WEB/3.2" + await createSign(params)
|
||||
let content = desDecrypt(await this.post(url, params, getHeader()))
|
||||
if (content != null) {
|
||||
let content_json = JSON.parse(content)
|
||||
let vod_dic = content_json["entity"]
|
||||
this.vodDetail = await this.parseVodDetailfromJson(vod_dic)
|
||||
let niBaVodDetail = getVod(vod_dic["plays"], ["原画"], id.toString())
|
||||
this.vodDetail.vod_play_from = niBaVodDetail.vod_play_from
|
||||
this.vodDetail.vod_play_url = niBaVodDetail.vod_play_url
|
||||
}
|
||||
}
|
||||
async setSearch(wd, quick) {
|
||||
let params = {"cat_id": "1", "keyword": wd, "keyword_type": "0", "start": "0"}
|
||||
let url = this.siteUrl + "/show/search/WEB/3.2" + await createSign(params)
|
||||
let content = desDecrypt(await this.post(url, params, getHeader()))
|
||||
if (content != null) {
|
||||
let content_json = JSON.parse(content)
|
||||
for (const vod_dic of content_json["list"]) {
|
||||
let vod_detail = await this.parseVodDetailfromJson(vod_dic)
|
||||
this.vodList.push(vod_detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
async setPlay(flag, id, flags) {
|
||||
let playId = id.split("@")[0]
|
||||
let showId = id.split("@")[1]
|
||||
let params = {
|
||||
"show_id_code": showId,
|
||||
"play_id_code": playId
|
||||
}
|
||||
let url = this.siteUrl + "/show/play/info/WEB/3.2" + await createSign(params)
|
||||
let content = desDecrypt(await this.post(url, params,getHeader()))
|
||||
if (content != null) {
|
||||
let content_json = JSON.parse(content)
|
||||
this.playUrl = content_json["entity"]["playUrl"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new Nivod18Spider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
152
cat/tjs/js/pan_search.js
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* @File : pan_search.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/25 17:18
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 阿里盘搜(仅支持搜搜)
|
||||
*/
|
||||
import {_, load} from "../lib/cat.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {detailContent, initAli, playContent} from "../lib/ali.js";
|
||||
|
||||
class PanSearchSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.pansearch.me/"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🗂️┃阿里盘搜┃🗂️"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "阿里盘搜"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "pan_search"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
getSearchHeader() {
|
||||
let headers = this.getHeader();
|
||||
headers["x-nextjs-data"] = "1";
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await initAli(this.cfgObj["token"]);
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let item = JSON.parse(obj)
|
||||
let vodDetail = new VodDetail();
|
||||
let splitList = item["content"].split("\n");
|
||||
vodDetail.vod_name = splitList[0].replaceAll(/<\\?[^>]+>/g, "").replace("名称:", "");
|
||||
let date = new Date(item["time"])
|
||||
vodDetail.vod_remarks = date.toLocaleDateString().replace(/\//g, "-") + " " + date.toTimeString().substr(0, 8)
|
||||
vodDetail.vod_pic = item["image"]
|
||||
let share_url = ""
|
||||
for (const content of splitList) {
|
||||
if (content.indexOf("描述") > -1) {
|
||||
vodDetail.vod_content = content.replace("描述:", "").replaceAll(/<\\?[^>]+>/g, "")
|
||||
}
|
||||
if (content.indexOf("标签:") > -1) {
|
||||
vodDetail.type_name = content.replace("🏷 标签:", "")
|
||||
}
|
||||
if (content.indexOf("链接:") > -1) {
|
||||
share_url = content.replaceAll(/<\\?[^>]+>/g, "").replace("链接:", "");
|
||||
}
|
||||
}
|
||||
try {
|
||||
let aliVodDetail = await detailContent([share_url])
|
||||
vodDetail.vod_play_url = aliVodDetail.vod_play_url
|
||||
vodDetail.vod_play_from = aliVodDetail.vod_play_from
|
||||
} catch (e) {
|
||||
await this.jadeLog.error(`获取阿里视频播放失败,失败原因为:${e}`)
|
||||
}
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($, wd) {
|
||||
let vod_list = []
|
||||
let buildId = JSON.parse($("script[id=__NEXT_DATA__]")[0].children[0].data)["buildId"]
|
||||
let url = this.siteUrl + "_next/data/" + buildId + "/search.json?keyword=" + encodeURIComponent(wd) + "&pan=aliyundrive";
|
||||
let aliContent = await this.fetch(url, null, this.getSearchHeader())
|
||||
if (!_.isEmpty(aliContent)) {
|
||||
let items = JSON.parse(aliContent)["pageProps"]["data"]["data"]
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = JSON.stringify(item)
|
||||
let splitList = item["content"].split("\n");
|
||||
vodShort.vod_name = splitList[0].replaceAll(/<\\?[^>]+>/g, "").replace("名称:", "");
|
||||
let date = new Date(item["time"])
|
||||
vodShort.vod_remarks = date.toLocaleDateString().replace(/\//g, "-") + " " + date.toTimeString().substr(0, 8)
|
||||
vodShort.vod_pic = item["image"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
} else {
|
||||
await this.jadeLog.error("搜索页面解析失败", true)
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
this.vodDetail = await this.parseVodDetailfromJson(id)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($, wd)
|
||||
}
|
||||
}
|
||||
async play(flag, id, flags) {
|
||||
return await playContent(flag, id, flags);
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new PanSearchSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
459
cat/tjs/js/pipixia.js
Normal file
@ -0,0 +1,459 @@
|
||||
/*
|
||||
* @File : pipixia.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/2 13:33
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 完成所有的功能开发(已失效)
|
||||
*/
|
||||
import {_, Crypto, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {pipixiaMd5} from "../lib/pipiXiaObject.js"
|
||||
|
||||
class PiPiXiaSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "http://aikun.tv/"
|
||||
this.pipixiaReconnectTimes = 0
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
let headers = super.getHeader();
|
||||
headers["Connection"] = "keep-alive"
|
||||
headers["Host"] = "pipixia.vip"
|
||||
return headers
|
||||
}
|
||||
|
||||
|
||||
getName() {
|
||||
return `🦐┃皮皮虾影视┃🦐`
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return `皮皮虾影视`
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "pipixia"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodElements = $($("[class=\"wow fadeInUp animated\"]")).find("[class=\"public-list-box public-pic-b\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = Utils.getStrByRegex(/v\/(.*?).html/, $(vodElement).find("a")[0].attribs.href)
|
||||
vodShort.vod_name = $(vodElement).find("a")[0].attribs.title
|
||||
vodShort.vod_pic = this.baseProxy + Utils.base64Encode(this.siteUrl + "/" + $(vodElement).find("[class=\"lazy gen-movie-img mask-1\"]")[0].attribs["data-original"])
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let vod_list = []
|
||||
let vodElements = $("[class=\"row-right hide\"]").find("[class=\"search-box flex rel\"]")
|
||||
for (const vodElement of vodElements) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_pic = this.baseProxy + Utils.base64Encode(this.siteUrl + "/" + Utils.getStrByRegex(/url\((.*?)\);/, $(vodElement).find("[class=\"cover\"]")[0].attribs.style))
|
||||
vodShort.vod_remarks = $($(vodElement).find("[class=\"public-list-prb hide ft2\"]")).html()
|
||||
vodShort.vod_name = $($(vodElement).find("[class=\"thumb-txt cor4 hide\"]")).html()
|
||||
vodShort.vod_id = Utils.getStrByRegex(/v\/(.*?).html/, $(vodElement).find("[class=\"button\"]")[0].attribs.href)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const vod_json of obj["list"]) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_name = vod_json["vod_name"]
|
||||
vodShort.vod_id = vod_json["vod_id"]
|
||||
vodShort.vod_pic = this.baseProxy + Utils.base64Encode(this.siteUrl + "/" + vod_json["vod_pic"])
|
||||
vodShort.vod_remarks = vod_json["vod_remarks"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail();
|
||||
let detailElement = $("[class=\"vod-detail style-detail rel box cor1\"]")
|
||||
vodDetail.vod_name = $($(detailElement).find("[class=\"slide-info-title hide\"]")).text()
|
||||
vodDetail.vod_pic = this.siteUrl + $(detailElement).find("[class=\"detail-pic lazy mask-1\"]")[0].attribs["data-original"]
|
||||
vodDetail.vod_remarks = $($($(detailElement).find("[class=\"slide-info hide\"]")[0]).find("[class=\"slide-info-remarks\"]")[0]).text()
|
||||
vodDetail.vod_year = $($($(detailElement).find("[class=\"slide-info hide\"]")[0]).find("[class=\"slide-info-remarks\"]")[1]).text()
|
||||
vodDetail.vod_area = $($($(detailElement).find("[class=\"slide-info hide\"]")[0]).find("[class=\"slide-info-remarks\"]")[2]).text()
|
||||
|
||||
vodDetail.vod_director = $($($(detailElement).find("[class=\"slide-info hide\"]")[1]).find("a")).text()
|
||||
vodDetail.vod_actor = $($($(detailElement).find("[class=\"slide-info hide\"]")[2]).find("a")).text()
|
||||
let type_list = []
|
||||
for (const typeEle of $($(detailElement).find("[class=\"slide-info hide\"]")[3]).find("a")) {
|
||||
type_list.push($(typeEle).text())
|
||||
}
|
||||
vodDetail.type_name = type_list.join("/")
|
||||
vodDetail.vod_content = $($("[class=\"check text selected cor3\"]")).text()
|
||||
let playElemet = $("[class=\"anthology wow fadeInUp animated\"]")
|
||||
let playFormatElemets = $(playElemet).find("[class=\"swiper-slide\"]")
|
||||
let playUrlElements = $(playElemet).find("[class=\"anthology-list-play size\"]")
|
||||
let vod_play_from_list = []
|
||||
let vod_play_list = []
|
||||
for (let i = 0; i < playFormatElemets.length; i++) {
|
||||
let playFormatElement = playFormatElemets[i]
|
||||
vod_play_from_list.push(playFormatElement.children[1].data)
|
||||
let vodItems = []
|
||||
for (const playUrlElement of $(playUrlElements[i]).find("a")) {
|
||||
let episodeName = $(playUrlElement).text()
|
||||
let episodeUrl = playUrlElement.attribs.href
|
||||
vodItems.push(episodeName + "$" + episodeUrl)
|
||||
}
|
||||
vod_play_list.push(vodItems.join("#"))
|
||||
}
|
||||
vodDetail.vod_play_from = vod_play_from_list.join("$$$")
|
||||
vodDetail.vod_play_url = vod_play_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
|
||||
async getHtml(url = this.siteUrl, headers = this.getHeader()) {
|
||||
try {
|
||||
let html = await this.fetch(url, null, headers)
|
||||
if (!_.isEmpty(html) && html.indexOf("江苏反诈公益宣传") === -1) {
|
||||
return load(html)
|
||||
} else {
|
||||
if (this.pipixiaReconnectTimes < this.maxReconnectTimes) {
|
||||
Utils.sleep(2)
|
||||
this.pipixiaReconnectTimes = this.pipixiaReconnectTimes + 1
|
||||
return await this.getHtml(url, headers)
|
||||
} else {
|
||||
await this.jadeLog.error(`html获取失败`, true)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
await this.jadeLog.error(`获取html出错,出错原因为${e}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
this.classes = [this.getTypeDic("首页", "最近更新")]
|
||||
let $2 = await this.getHtml(this.siteUrl + "/s/1.html")
|
||||
let classElemets = $2("[class=\"nav-swiper rel\"]")[0]
|
||||
for (const classElement of $(classElemets).find("a")) {
|
||||
let type_id = Utils.getStrByRegex(/\/s\/(.*?).html/, classElement.attribs.href)
|
||||
let type_name = $(classElement).text()
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let elements = $("[class=\"nav-swiper rel\"]")
|
||||
let extend_list = []
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let element = elements[i]
|
||||
let name = $($($(element).find("[class=\"filter-text bj cor5\"]")).find("span")).html()
|
||||
if (name !== "频道") {
|
||||
let extend_dic = {"key": (i + 1).toString(), "name": name, "value": []}
|
||||
for (const ele of $(element).find("a")) {
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": $(ele).text()})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const type_dic of this.classes) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (Utils.isNumeric(type_id)) {
|
||||
let url = this.siteUrl + `/s/${type_id}.html`
|
||||
let $ = await this.getHtml(url)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml(this.siteUrl + "/map.html")
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
getExtend(extend, key) {
|
||||
if (extend[key] !== undefined && extend[key] !== "全部") {
|
||||
return extend[key]
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
getExtendDic(params, extend) {
|
||||
let class_value = this.getExtend(extend, "2")
|
||||
if (class_value !== null) {
|
||||
params["class"] = class_value
|
||||
}
|
||||
let area_value = this.getExtend(extend, "3")
|
||||
if (area_value !== null) {
|
||||
params["area"] = area_value
|
||||
}
|
||||
let year_value = this.getExtend(extend, "4")
|
||||
if (year_value !== null) {
|
||||
params["year"] = year_value
|
||||
}
|
||||
let lang_value = this.getExtend(extend, "5")
|
||||
if (lang_value !== null) {
|
||||
params["lang"] = lang_value
|
||||
}
|
||||
let letter_value = this.getExtend(extend, "6")
|
||||
if (letter_value !== null) {
|
||||
params["letter"] = letter_value
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
if (Utils.isNumeric(tid)) {
|
||||
let url = this.siteUrl + "/index.php/api/vod"
|
||||
let time_1 = Math.floor(new Date().getTime() / 1000)
|
||||
let key_1 = pipixiaMd5(time_1)
|
||||
let params = {
|
||||
"type": tid, "page": pg, "time": time_1.toString(), "key": key_1
|
||||
}
|
||||
params = this.getExtendDic(params, extend)
|
||||
let content = await this.post(url, params, this.getHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
if (content_json["code"] === 1) {
|
||||
this.vodList = await this.parseVodShortListFromJson(content_json)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + `/v/${id}.html`)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async getPlayConfig(element) {
|
||||
// let playJSUrl = this.siteUrl + element.attribs.src
|
||||
// let jsContent = await this.fetch(playJSUrl,null,null)
|
||||
// let playListConfig = JSON.parse(Utils.getStrByRegex(/MacPlayerConfig.player_list=(.*?),MacPlayerConfig/,jsContent))
|
||||
//
|
||||
let playListConfig = {
|
||||
"qq": {
|
||||
"show": "QQ虾线",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "qiyi": {
|
||||
"show": "QY虾线",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qiyi&if=1&url="
|
||||
}, "youku": {
|
||||
"show": "YK虾线",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=youku&if=1&url="
|
||||
}, "mgtv": {
|
||||
"show": "MG虾线",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=mgtv&if=1&url="
|
||||
}, "NBY": {
|
||||
"show": "极速线路",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "SLNB": {
|
||||
"show": "三路极速",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "FYNB": {
|
||||
"show": "APP专享线路",
|
||||
"des": "",
|
||||
"ps": "0",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "SPA": {
|
||||
"show": "极速A",
|
||||
"des": "",
|
||||
"ps": "0",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "SPB": {
|
||||
"show": "极速B",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "kyB": {
|
||||
"show": "极速直连",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "JMZN": {
|
||||
"show": "极速直连",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "ZNJSON": {
|
||||
"show": "极速直连",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "znkan": {
|
||||
"show": "极速直连",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "bilibili": {
|
||||
"show": "BLBL虾线",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "pptv": {
|
||||
"show": "PP虾线", "des": "", "ps": "1", "parse": "http://play.shijie.chat/player/?url="
|
||||
}, "letv": {
|
||||
"show": "LE虾线", "des": "", "ps": "1", "parse": "http://play.shijie.chat/player/?url="
|
||||
}, "sohu": {
|
||||
"show": "SH虾线", "des": "", "ps": "1", "parse": "http://play.shijie.chat/player/?url="
|
||||
}, "DJMP4": {
|
||||
"show": "短剧专用",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "CLDJ": {
|
||||
"show": "短剧①",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "ChenXi": {
|
||||
"show": "短剧专用2",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "HT-": {
|
||||
"show": "自营线",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}, "htys": {
|
||||
"show": "解说线路", "des": "", "ps": "1", "parse": "http://play.shijie.chat/player/?url="
|
||||
}, "sgdj": {
|
||||
"show": "短剧③",
|
||||
"des": "",
|
||||
"ps": "1",
|
||||
"parse": "http://play.shijie.chat/player/ec.php?code=qq&if=1&url="
|
||||
}
|
||||
}
|
||||
return playListConfig
|
||||
}
|
||||
|
||||
uic(url, uid) {
|
||||
let ut = Crypto.enc.Utf8.parse('2890' + uid + 'tB959C');
|
||||
let mm = Crypto.enc.Utf8.parse("2F131BE91247866E");
|
||||
let decrypted = Crypto.AES.decrypt(url, ut, {iv: mm, mode: Crypto.mode.CBC, padding: Crypto.pad.Pkcs7});
|
||||
return Crypto.enc.Utf8.stringify(decrypted);
|
||||
}
|
||||
|
||||
async setVideoProxy(playUrl){
|
||||
let urls = []
|
||||
urls.push('proxy');
|
||||
urls.push(playUrl);
|
||||
const pUrls = urls
|
||||
for (let index = 1; index < pUrls.length; index += 2) {
|
||||
pUrls[index] = js2Proxy(false, this.siteType, this.siteKey, 'hls/' + encodeURIComponent(pUrls[index]), {});
|
||||
}
|
||||
pUrls.push('original');
|
||||
pUrls.push(playUrl);
|
||||
return pUrls
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let $ = await this.getHtml(this.siteUrl + id)
|
||||
let playElements = $("[class=\"player-left\"]")
|
||||
let scriptElements = $(playElements).find("script")
|
||||
await this.jadeLog.debug($(scriptElements[0]).html())
|
||||
let playConfig = JSON.parse($(scriptElements[0]).html().replaceAll("var player_aaaa=", ""))
|
||||
let playListConfig = await this.getPlayConfig(scriptElements[1])
|
||||
let jiexiUrl = playListConfig[playConfig["from"]]["parse"] + playConfig["url"]
|
||||
let jiexi$ = await this.getHtml(jiexiUrl, {"User-Agent": Utils.CHROME})
|
||||
let ConFig = JSON.parse(Utils.getStrByRegex(/let ConFig = (.*?),box = /, jiexi$.html()))
|
||||
let playUrl = this.uic(ConFig["url"], ConFig.config.uid)
|
||||
await this.jadeLog.debug(`播放链接为:${playUrl}`)
|
||||
if (flag.indexOf("极速") > -1) {
|
||||
this.playUrl = playUrl
|
||||
} else {
|
||||
if (this.catOpenStatus) {
|
||||
this.playUrl = await this.setVideoProxy(playUrl)
|
||||
} else {
|
||||
this.playUrl = playUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let $ = await this.getHtml(this.siteUrl + `/vodsearch.html?wd=${decodeURI(wd)}`)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new PiPiXiaSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
proxy: proxy,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
108
cat/tjs/js/push_agent.js
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* @File : push_agent.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/6 9:30
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {Spider} from "./spider.js";
|
||||
import {VodDetail} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {detailContent, initAli, playContent} from "../lib/ali.js";
|
||||
|
||||
class PushSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "┃推送┃"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "推送"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "push"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 4
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
try {
|
||||
this.cfgObj = await this.SpiderInit(cfg)
|
||||
this.catOpenStatus = this.cfgObj.CatOpenStatus
|
||||
await initAli(this.cfgObj["token"]);
|
||||
} catch (e) {
|
||||
await this.jadeLog.error(`初始化失败,失败原因为:${e}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async check(args){
|
||||
// CatVodOpen目前支持http链接和https链接
|
||||
await spider.jadeLog.debug(`剪切板输入内容为:${args}`)
|
||||
if (this.catOpenStatus){
|
||||
return !!args.startsWith("http");
|
||||
}else{
|
||||
// TV目前支持http链接和https链接和Ftp和magnet等格式
|
||||
return !!(args.startsWith("http") || args.startsWith("ftp") || args.startsWith("magnet"));
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(id) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_pic = Utils.RESOURCEURL + "/resources/push.jpg"
|
||||
let mather = Utils.patternAli.exec(id)
|
||||
if (mather !== null && mather.length > 0) {
|
||||
let aliVodDetail = await detailContent([id])
|
||||
vodDetail.vod_play_url = aliVodDetail.vod_play_url
|
||||
vodDetail.vod_play_from = aliVodDetail.vod_play_from
|
||||
} else {
|
||||
vodDetail.vod_play_from = '推送';
|
||||
vodDetail.vod_play_url = '推送$' + id;
|
||||
}
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
this.vodDetail = await this.parseVodDetailfromJson(id)
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
if (flag === "推送"){
|
||||
this.playUrl = id
|
||||
}else{
|
||||
this.playUrl = JSON.parse(await playContent(flag, id, flags))["url"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new PushSpider()
|
||||
|
||||
async function check(args) {
|
||||
return await spider.check(args)
|
||||
}
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
support: check, init: init, detail: detail, play: play,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
251
cat/tjs/js/sehuatang.js
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* @File : sehuatang
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/24 16:47
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 色花堂BT
|
||||
*/
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {_} from "../lib/cat.js";
|
||||
|
||||
class SHTSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.sehuatang.net"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "色花堂BT"
|
||||
}
|
||||
getName() {
|
||||
return "🔞┃色花堂BT┃🔞"
|
||||
}
|
||||
getJSName() {
|
||||
return "sehuatang"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
this.jsBaseDetail = await js2Proxy(true, this.siteType, this.siteKey, 'detail/', {});
|
||||
}
|
||||
|
||||
|
||||
|
||||
getHeader() {
|
||||
return {
|
||||
"User-Agent": "PostmanRuntime/7.36.1",
|
||||
"Host": "www.sehuatang.net",
|
||||
"Cookie": "cPNj_2132_saltkey=Q4BKEOEC; cf_clearance=6Gz2tvOXPkkJP2UhLnSsN4s0RrnDUy0jBN0kUvC5FNQ-1706109144-1-AebvwBnAURwWWQhj0QRBrRPku2n8xI73PIeuZVj2ckqY9zjQ7zFzDviX7Gkex1P1bUw9SXHGEYnkBB9nmWe6Nhk=; _safe=vqd37pjm4p5uodq339yzk6b7jdt6oich",
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vod_list = []
|
||||
let vodShortElements = $("[id=\"portal_block_43_content\"]").find("li")
|
||||
for (const vodShortElement of vodShortElements) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_remarks = $($(vodShortElement).find("a")[1]).text()
|
||||
vodShort.vod_id = $(vodShortElement).find("a")[2].attribs["href"]
|
||||
vodShort.vod_name = $(vodShortElement).find("a")[2].attribs["title"]
|
||||
vodShort.vod_pic = this.jsBaseDetail + Utils.base64Encode(vodShort.vod_id)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
let vod_list = []
|
||||
let vodElements = $($("[class=\"bm_c\"]")[0]).find("tbody")
|
||||
for (const vodElement of vodElements) {
|
||||
let user_name = $($($(vodElement).find("cite")).find("a")[0]).text()
|
||||
if (user_name !== "admin" && user_name !== undefined && !_.isEmpty(user_name)) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = $(vodElement).find("a")[0].attribs["href"]
|
||||
vodShort.vod_remarks = $($(vodElement).find("a")[2]).text()
|
||||
vodShort.vod_name = $($(vodElement).find("a")[3]).text()
|
||||
vodShort.vod_pic = this.jsBaseDetail + Utils.base64Encode(vodShort.vod_id)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail();
|
||||
let vodElement = $("[class=\"t_f\"]")[0]
|
||||
let content = $(vodElement).text().replaceAll(":", ":").replaceAll("【", "").replaceAll("】", "")
|
||||
vodDetail.vod_pic = $(vodElement).find("img")[0].attribs["file"]
|
||||
vodDetail.vod_name = Utils.getStrByRegex(/影片名称(.*?)\n/, content).replaceAll(":", "").replaceAll("\n", "")
|
||||
vodDetail.vod_actor = Utils.getStrByRegex(/出演女优(.*?)\n/, content).replaceAll(":", "").replaceAll("\n", "")
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/是否有码(.*?)\n/, content).replaceAll(":", "").replaceAll("\n", "")
|
||||
vodDetail.vod_play_from = "BT"
|
||||
vodDetail.vod_play_url = vodDetail.vod_name + "$" + Utils.getStrByRegex(/磁力链接: (.*)复制代码/, content)
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let tagElements = $("[id=\"category_1\"]").find("tr").slice(0, -1)
|
||||
for (const tagElement of tagElements) {
|
||||
let classElements = $($(tagElement).find("[class=\"fl_icn_g\"]")).find("a")
|
||||
for (const classElement of classElements) {
|
||||
let type_id = classElement.attribs["href"]
|
||||
let type_name = $(classElement).find("img")[0].attribs["alt"]
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
|
||||
let extend_list = []
|
||||
let extend_dic1 = {"key": 1, "name": "类型", "value": []}
|
||||
let typeElements = $("[id=\"thread_types\"]").find("a")
|
||||
for (const typeElement of typeElements){
|
||||
let type_name = ""
|
||||
if (typeElement.children.length > 1){
|
||||
type_name = typeElement.children[0].data + ":" + $(typeElement.children[1]).text()
|
||||
}else{
|
||||
type_name = typeElement.children[0].data
|
||||
}
|
||||
extend_dic1["value"].push({"n":type_name,"v":typeElement.attribs["href"]})
|
||||
}
|
||||
extend_list.push(extend_dic1)
|
||||
let extend_dic2 = {"key": 1, "name": "主题", "value": []}
|
||||
let themeElements = $("[class=\"tf\"]").find("a")
|
||||
for (const themeElement of themeElements){
|
||||
let type_name = $(themeElement).text()
|
||||
if (type_name !== "更多" && type_name !== "显示置顶"){
|
||||
extend_dic2["value"].push({"n":$(themeElement).text(),"v":themeElement.attribs["href"]})
|
||||
}
|
||||
}
|
||||
extend_list.push(extend_dic2)
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const class_dic of this.classes){
|
||||
let type_name = class_dic["type_name"]
|
||||
let type_id = class_dic["type_id"]
|
||||
if (type_name !== "最近更新"){
|
||||
let $ = await this.getHtml(this.siteUrl + '/' + type_id)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let $ = await this.getHtml()
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
if (extend["1"]!==undefined && extend[1] ==="javascript:;"){
|
||||
}else{
|
||||
tid = extend["1"] ?? tid
|
||||
}
|
||||
let cateUrl
|
||||
let tid_list = tid.split(".")[0].split("-")
|
||||
if (tid_list.length > 2){
|
||||
tid_list[2] = pg
|
||||
cateUrl = this.siteUrl + "/" + tid_list.join("-") + ".html"
|
||||
}else{
|
||||
cateUrl = this.siteUrl + "/" + tid + "&page=" + pg
|
||||
}
|
||||
|
||||
let $ = await this.getHtml(cateUrl)
|
||||
this.vodList = await this.parseVodShortListFromDocByCategory($)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let $ = await this.getHtml(this.siteUrl + "/" + id)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
|
||||
async proxy(segments, headers) {
|
||||
await this.jadeLog.debug(`正在设置反向代理 segments = ${segments.join(",")},headers = ${JSON.stringify(headers)}`)
|
||||
let what = segments[0];
|
||||
let url = Utils.base64Decode(segments[1]);
|
||||
if (what === 'detail') {
|
||||
await this.jadeLog.debug(`反向代理ID为:${url}`)
|
||||
let $ = await this.getHtml(this.siteUrl + "/" + url)
|
||||
let vodDetail = await this.parseVodDetailFromDoc($)
|
||||
await this.jadeLog.debug(`图片地址为:${vodDetail.vod_pic}`)
|
||||
let resp;
|
||||
if (!_.isEmpty(headers)) {
|
||||
resp = await req(vodDetail.vod_pic, {
|
||||
buffer: 2, headers: headers
|
||||
});
|
||||
} else {
|
||||
resp = await req(vodDetail.vod_pic, {
|
||||
buffer: 2, headers: {
|
||||
Referer: url, 'User-Agent': Utils.CHROME,
|
||||
},
|
||||
});
|
||||
}
|
||||
return JSON.stringify({
|
||||
code: resp.code, buffer: 2, content: resp.content, headers: resp.headers,
|
||||
});
|
||||
}
|
||||
return JSON.stringify({
|
||||
code: 500, content: '',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new SHTSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
672
cat/tjs/js/sp360.js
Normal file
@ -0,0 +1,672 @@
|
||||
/*
|
||||
* @File : sp360.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/21 11:18
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : SP360(需要用到解析)
|
||||
*/
|
||||
import {Spider} from "./spider.js";
|
||||
import {_, Crypto, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
class Sp360Spider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://api.web.360kan.com"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🥎┃360影视┃🥎"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "360"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "sp360"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
this.danmuUrl = true
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
this.classes = [this.getTypeDic("最近更新", "最近更新"), this.getTypeDic("电影", "1"), this.getTypeDic("剧集", "2"), this.getTypeDic("综艺", "3"), this.getTypeDic("动漫", "4")]
|
||||
}
|
||||
|
||||
|
||||
async setFilterObj() {
|
||||
this.filterObj = {
|
||||
"1": [{
|
||||
"key": "cat", "name": "类型", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "喜剧", "v": "喜剧"
|
||||
}, {
|
||||
"n": "爱情", "v": "爱情"
|
||||
}, {
|
||||
"n": "动作", "v": "动作"
|
||||
}, {
|
||||
"n": "恐怖", "v": "恐怖"
|
||||
}, {
|
||||
"n": "科幻", "v": "科幻"
|
||||
}, {
|
||||
"n": "剧情", "v": "剧情"
|
||||
}, {
|
||||
"n": "犯罪", "v": "犯罪"
|
||||
}, {
|
||||
"n": "奇幻", "v": "奇幻"
|
||||
}, {
|
||||
"n": "战争", "v": "战争"
|
||||
}, {
|
||||
"n": "悬疑", "v": "悬疑"
|
||||
}, {
|
||||
"n": "动画", "v": "动画"
|
||||
}, {
|
||||
"n": "文艺", "v": "文艺"
|
||||
}, {
|
||||
"n": "纪录", "v": "纪录"
|
||||
}, {
|
||||
"n": "传记", "v": "传记"
|
||||
}, {
|
||||
"n": "歌舞", "v": "歌舞"
|
||||
}, {
|
||||
"n": "古装", "v": "古装"
|
||||
}, {
|
||||
"n": "历史", "v": "历史"
|
||||
}, {
|
||||
"n": "惊悚", "v": "惊悚"
|
||||
}, {
|
||||
"n": "伦理", "v": "伦理"
|
||||
}, {
|
||||
"n": "其他", "v": "其他"
|
||||
}]
|
||||
}, {
|
||||
"key": "year", "name": "年代", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "2024", "v": "2024"
|
||||
}, {
|
||||
"n": "2023", "v": "2023"
|
||||
}, {
|
||||
"n": "2022", "v": "2022"
|
||||
}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {
|
||||
"n": "2020", "v": "2020"
|
||||
}, {
|
||||
"n": "2019", "v": "2019"
|
||||
}, {
|
||||
"n": "2018", "v": "2018"
|
||||
}, {
|
||||
"n": "2017", "v": "2017"
|
||||
}, {
|
||||
"n": "2016", "v": "2016"
|
||||
}, {
|
||||
"n": "2015", "v": "2015"
|
||||
}, {
|
||||
"n": "2014", "v": "2014"
|
||||
}, {
|
||||
"n": "2013", "v": "2013"
|
||||
}, {
|
||||
"n": "2012", "v": "2012"
|
||||
}, {
|
||||
"n": "2010", "v": "2010"
|
||||
}, {
|
||||
"n": "2009", "v": "2009"
|
||||
}, {
|
||||
"n": "2008", "v": "2008"
|
||||
}, {
|
||||
"n": "2007", "v": "2007"
|
||||
}, {
|
||||
"n": "更早", "v": "lt_year"
|
||||
}]
|
||||
}, {
|
||||
"key": "area", "name": "地区", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "内地", "v": "大陆"
|
||||
}, {
|
||||
"n": "中国香港", "v": "香港"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "台湾"
|
||||
}, {
|
||||
"n": "泰国", "v": "泰国"
|
||||
}, {
|
||||
"n": "美国", "v": "美国"
|
||||
}, {
|
||||
"n": "韩国", "v": "韩国"
|
||||
}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {
|
||||
"n": "法国", "v": "法国"
|
||||
}, {
|
||||
"n": "英国", "v": "英国"
|
||||
}, {
|
||||
"n": "德国", "v": "德国"
|
||||
}, {
|
||||
"n": "印度", "v": "印度"
|
||||
}, {
|
||||
"n": "其他", "v": "其他"
|
||||
}]
|
||||
}, {
|
||||
"key": "rank", "name": "排序", "value": [{
|
||||
"n": "最近热映", "v": "rankhot"
|
||||
}, {
|
||||
"n": "最近上映", "v": "ranklatest"
|
||||
}, {
|
||||
"n": "最受好评", "v": "rankpoint"
|
||||
}]
|
||||
}], "2": [{
|
||||
"key": "cat", "name": "类型", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "言情", "v": "言情"
|
||||
}, {
|
||||
"n": "剧情", "v": "剧情"
|
||||
}, {
|
||||
"n": "伦理", "v": "伦理"
|
||||
}, {
|
||||
"n": "喜剧", "v": "喜剧"
|
||||
}, {
|
||||
"n": "悬疑", "v": "悬疑"
|
||||
}, {
|
||||
"n": "都市", "v": "都市"
|
||||
}, {
|
||||
"n": "偶像", "v": "偶像"
|
||||
}, {
|
||||
"n": "古装", "v": "古装"
|
||||
}, {
|
||||
"n": "军事", "v": "军事"
|
||||
}, {
|
||||
"n": "警匪", "v": "警匪"
|
||||
}, {
|
||||
"n": "历史", "v": "历史"
|
||||
}, {
|
||||
"n": "励志", "v": "励志"
|
||||
}, {
|
||||
"n": "神话", "v": "神话"
|
||||
}, {
|
||||
"n": "谍战", "v": "谍战"
|
||||
}, {
|
||||
"n": "青春", "v": "青春剧"
|
||||
}, {
|
||||
"n": "家庭", "v": "家庭剧"
|
||||
}, {
|
||||
"n": "动作", "v": "动作"
|
||||
}, {
|
||||
"n": "情景", "v": "情景"
|
||||
}, {
|
||||
"n": "武侠", "v": "武侠"
|
||||
}, {
|
||||
"n": "科幻", "v": "科幻"
|
||||
}, {
|
||||
"n": "其他", "v": "其他"
|
||||
}, {
|
||||
"n": "全部", "v": ""
|
||||
}]
|
||||
}, {
|
||||
"key": "year", "name": "年代", "value": [{
|
||||
"n": "2024", "v": "2024"
|
||||
}, {
|
||||
"n": "2023", "v": "2023"
|
||||
}, {
|
||||
"n": "2022", "v": "2022"
|
||||
}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {
|
||||
"n": "2020", "v": "2020"
|
||||
}, {
|
||||
"n": "2019", "v": "2019"
|
||||
}, {
|
||||
"n": "2018", "v": "2018"
|
||||
}, {
|
||||
"n": "2017", "v": "2017"
|
||||
}, {
|
||||
"n": "2016", "v": "2016"
|
||||
}, {
|
||||
"n": "2015", "v": "2015"
|
||||
}, {
|
||||
"n": "2014", "v": "2014"
|
||||
}, {
|
||||
"n": "2013", "v": "2013"
|
||||
}, {
|
||||
"n": "2012", "v": "2012"
|
||||
}, {
|
||||
"n": "2010", "v": "2010"
|
||||
}, {
|
||||
"n": "2009", "v": "2009"
|
||||
}, {
|
||||
"n": "2008", "v": "2008"
|
||||
}, {
|
||||
"n": "2007", "v": "2007"
|
||||
}, {
|
||||
"n": "更早", "v": "lt_year"
|
||||
}]
|
||||
}, {
|
||||
"key": "area", "name": "地区", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "内地", "v": "内地"
|
||||
}, {
|
||||
"n": "中国香港", "v": "香港"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "台湾"
|
||||
}, {
|
||||
"n": "泰国", "v": "泰国"
|
||||
}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {
|
||||
"n": "韩国", "v": "韩国"
|
||||
}, {
|
||||
"n": "美国", "v": "美国"
|
||||
}, {
|
||||
"n": "英国", "v": "英国"
|
||||
}, {
|
||||
"n": "新加坡", "v": "新加坡"
|
||||
}]
|
||||
}, {
|
||||
"key": "rank", "name": "排序", "value": [{
|
||||
"n": "最近热映", "v": "rankhot"
|
||||
}, {
|
||||
"n": "最近上映", "v": "ranklatest"
|
||||
}, {
|
||||
"n": "最受好评", "v": "rankpoint"
|
||||
}]
|
||||
}], "3": [{
|
||||
"key": "cat", "name": "类型", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "脱口秀", "v": "脱口秀"
|
||||
}, {
|
||||
"n": "真人秀", "v": "真人秀"
|
||||
}, {
|
||||
"n": "搞笑", "v": "搞笑"
|
||||
}, {
|
||||
"n": "选秀", "v": "选秀"
|
||||
}, {
|
||||
"n": "八卦", "v": "八卦"
|
||||
}, {
|
||||
"n": "访谈", "v": "访谈"
|
||||
}, {
|
||||
"n": "情感", "v": "情感"
|
||||
}, {
|
||||
"n": "生活", "v": "生活"
|
||||
}, {
|
||||
"n": "晚会", "v": "晚会"
|
||||
}, {
|
||||
"n": "音乐", "v": "音乐"
|
||||
}, {
|
||||
"n": "职场", "v": "职场"
|
||||
}, {
|
||||
"n": "美食", "v": "美食"
|
||||
}, {
|
||||
"n": "时尚", "v": "时尚"
|
||||
}, {
|
||||
"n": "游戏", "v": "游戏"
|
||||
}, {
|
||||
"n": "少儿", "v": "少儿"
|
||||
}, {
|
||||
"n": "体育", "v": "体育"
|
||||
}, {
|
||||
"n": "纪实", "v": "纪实"
|
||||
}, {
|
||||
"n": "科教", "v": "科教"
|
||||
}, {
|
||||
"n": "曲艺", "v": "曲艺"
|
||||
}, {
|
||||
"n": "歌舞", "v": "歌舞"
|
||||
}, {
|
||||
"n": "财经", "v": "财经"
|
||||
}, {
|
||||
"n": "汽车", "v": "汽车"
|
||||
}, {
|
||||
"n": "播报", "v": "播报"
|
||||
}, {
|
||||
"n": "其他", "v": "其他"
|
||||
}]
|
||||
}, {
|
||||
"key": "area", "name": "地区", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "内地", "v": "大陆"
|
||||
}, {
|
||||
"n": "中国香港", "v": "香港"
|
||||
}, {
|
||||
"n": "中国台湾", "v": "台湾"
|
||||
}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {
|
||||
"n": "欧美", "v": "欧美"
|
||||
}]
|
||||
}, {
|
||||
"key": "rank", "name": "排序", "value": [{
|
||||
"n": "最近热映", "v": "rankhot"
|
||||
}, {
|
||||
"n": "最近上映", "v": "ranklatest"
|
||||
}]
|
||||
}], "4": [{
|
||||
"key": "cat", "name": "类型", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "热血", "v": "热血"
|
||||
}, {
|
||||
"n": "科幻", "v": "科幻"
|
||||
}, {
|
||||
"n": "美少女", "v": "美少女"
|
||||
}, {
|
||||
"n": "魔幻", "v": "魔幻"
|
||||
}, {
|
||||
"n": "经典", "v": "经典"
|
||||
}, {
|
||||
"n": "励志", "v": "励志"
|
||||
}, {
|
||||
"n": "少儿", "v": "少儿"
|
||||
}, {
|
||||
"n": "冒险", "v": "冒险"
|
||||
}, {
|
||||
"n": "搞笑", "v": "搞笑"
|
||||
}, {
|
||||
"n": "推理", "v": "推理"
|
||||
}, {
|
||||
"n": "恋爱", "v": "恋爱"
|
||||
}, {
|
||||
"n": "治愈", "v": "治愈"
|
||||
}, {
|
||||
"n": "幻想", "v": "幻想"
|
||||
}, {
|
||||
"n": "校园", "v": "校园"
|
||||
}, {
|
||||
"n": "动物", "v": "动物"
|
||||
}, {
|
||||
"n": "机战", "v": "机战"
|
||||
}, {
|
||||
"n": "亲子", "v": "亲子"
|
||||
}, {
|
||||
"n": "儿歌", "v": "儿歌"
|
||||
}, {
|
||||
"n": "运动", "v": "运动"
|
||||
}, {
|
||||
"n": "悬疑", "v": "悬疑"
|
||||
}, {
|
||||
"n": "怪物", "v": "怪物"
|
||||
}, {
|
||||
"n": "战争", "v": "战争"
|
||||
}, {
|
||||
"n": "益智", "v": "益智"
|
||||
}, {
|
||||
"n": "青春", "v": "青春"
|
||||
}, {
|
||||
"n": "童话", "v": "童话"
|
||||
}, {
|
||||
"n": "竞技", "v": "竞技"
|
||||
}, {
|
||||
"n": "动作", "v": "动作"
|
||||
}, {
|
||||
"n": "社会", "v": "社会"
|
||||
}, {
|
||||
"n": "友情", "v": "友情"
|
||||
}, {
|
||||
"n": "真人版", "v": "真人版"
|
||||
}, {
|
||||
"n": "电影版", "v": "电影版"
|
||||
}, {
|
||||
"n": "OVA版", "v": "OVA版"
|
||||
}, {
|
||||
"n": "TV版", "v": "TV版"
|
||||
}, {
|
||||
"n": "新番动画", "v": "新番动画"
|
||||
}, {
|
||||
"n": "完结动画", "v": "完结动画"
|
||||
}]
|
||||
}, {
|
||||
"key": "year", "name": "年代", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "2024", "v": "2024"
|
||||
}, {
|
||||
"n": "2023", "v": "2023"
|
||||
}, {
|
||||
"n": "2022", "v": "2022"
|
||||
}, {
|
||||
"n": "2021", "v": "2021"
|
||||
}, {
|
||||
"n": "2020", "v": "2020"
|
||||
}, {
|
||||
"n": "2019", "v": "2019"
|
||||
}, {
|
||||
"n": "2018", "v": "2018"
|
||||
}, {
|
||||
"n": "2017", "v": "2017"
|
||||
}, {
|
||||
"n": "2016", "v": "2016"
|
||||
}, {
|
||||
"n": "2015", "v": "2015"
|
||||
}, {
|
||||
"n": "2014", "v": "2014"
|
||||
}, {
|
||||
"n": "2013", "v": "2013"
|
||||
}, {
|
||||
"n": "2012", "v": "2012"
|
||||
}, {
|
||||
"n": "2011", "v": "2011"
|
||||
}, {
|
||||
"n": "2010", "v": "2010"
|
||||
}, {
|
||||
"n": "2009", "v": "2009"
|
||||
}, {
|
||||
"n": "2008", "v": "2008"
|
||||
}, {
|
||||
"n": "2007", "v": "2007"
|
||||
}, {
|
||||
"n": "2006", "v": "2006"
|
||||
}, {
|
||||
"n": "2005", "v": "2005"
|
||||
}, {
|
||||
"n": "2004", "v": "2004"
|
||||
}, {
|
||||
"n": "更早", "v": "更早"
|
||||
}]
|
||||
}, {
|
||||
"key": "area", "name": "地区", "value": [{
|
||||
"n": "全部", "v": ""
|
||||
}, {
|
||||
"n": "内地", "v": "大陆"
|
||||
}, {
|
||||
"n": "日本", "v": "日本"
|
||||
}, {
|
||||
"n": "美国", "v": "美国"
|
||||
}]
|
||||
}, {
|
||||
"key": "rank", "name": "排序", "value": [{
|
||||
"n": "最近热映", "v": "rankhot"
|
||||
}, {
|
||||
"n": "最近上映", "v": "ranklatest"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const data of obj["data"]) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = data["ent_id"] + "+" + data["cat"]
|
||||
if (!data["cover"].startsWith("http")) {
|
||||
vodShort.vod_pic = "https:" + data["cover"]
|
||||
} else {
|
||||
vodShort.vod_pic = data["cover"]
|
||||
}
|
||||
vodShort.vod_name = data["title"]
|
||||
vodShort.vod_remarks = data["upinfo"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromJsonByCategory(obj, tid) {
|
||||
let vod_list = []
|
||||
for (const data of obj["data"]["movies"]) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = data["id"] + "+" + tid
|
||||
if (!data["cover"].startsWith("http")) {
|
||||
vodShort.vod_pic = "https:" + data["cdncover"]
|
||||
} else {
|
||||
vodShort.vod_pic = data["cdncover"]
|
||||
}
|
||||
vodShort.vod_name = data["title"]
|
||||
vodShort.vod_remarks = data["tag"]
|
||||
if (!_.isEmpty(data["doubanscore"])) {
|
||||
vodShort.vod_remarks = "豆瓣评分:" + data["doubanscore"]
|
||||
} else {
|
||||
if (_.isEmpty(vodShort.vod_remarks)) {
|
||||
vodShort.vod_remarks = data["pubdate"]
|
||||
}
|
||||
}
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let vodDetail = new VodDetail()
|
||||
let data = obj["data"]
|
||||
vodDetail.vod_name = data["title"]
|
||||
vodDetail.vod_pic = data["cdncover"]
|
||||
vodDetail.vod_remarks = data["pubdate"]
|
||||
vodDetail.vod_actor = data["actor"].join("*")
|
||||
vodDetail.vod_director = data["director"].join("*")
|
||||
vodDetail.type_name = data["moviecategory"].join("*")
|
||||
vodDetail.vod_year = data["pubdate"]
|
||||
vodDetail.vod_area = data["area"].join("*")
|
||||
vodDetail.vod_content = data["description"]
|
||||
|
||||
let playlist = {}
|
||||
for (const playFormat of data["playlink_sites"]) {
|
||||
let vodItems = []
|
||||
if (!_.isEmpty(data["allepidetail"])) {
|
||||
if (data["allepidetail"][playFormat] !== undefined) {
|
||||
for (const items of data["allepidetail"][playFormat]) {
|
||||
let episodeUrl = items["url"]
|
||||
let episodeName = items["playlink_num"]
|
||||
vodItems.push(episodeName + "$" + episodeUrl);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let items = data["playlinksdetail"][playFormat]
|
||||
let episodeUrl = items["default_url"]
|
||||
let episodeName = items["quality"]
|
||||
vodItems.push(episodeName + "$" + episodeUrl);
|
||||
}
|
||||
if (vodItems.length > 0){
|
||||
playlist[playFormat] = vodItems.join("#")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vodDetail.vod_play_url = _.values(playlist).join('$$$');
|
||||
vodDetail.vod_play_from = _.keys(playlist).join('$$$');
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodShortListFromJsonBySearch(obj) {
|
||||
let vod_list = []
|
||||
for (const data of obj["data"]["longData"]["rows"]) {
|
||||
let vodShort = new VodShort();
|
||||
vodShort.vod_id = data["en_id"] + "+" + data["cat_id"]
|
||||
if (!data["cover"].startsWith("http")) {
|
||||
vodShort.vod_pic = "https:" + data["cover"]
|
||||
} else {
|
||||
vodShort.vod_pic = data["cover"]
|
||||
}
|
||||
vodShort.vod_name = data["titleTxt"]
|
||||
vodShort.vod_remarks = data["coverInfo"]["txt"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let response = await this.fetch(this.siteUrl + "/v1/rank?cat=1", null, this.getHeader())
|
||||
this.homeVodList = await this.parseVodShortListFromJson(JSON.parse(response))
|
||||
}
|
||||
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let rank = extend["by"] ?? "rankhot"
|
||||
let year = extend["year"] ?? ""
|
||||
let cat = extend["cat"] ?? ""
|
||||
let area = extend["area"] ?? ""
|
||||
let url = this.siteUrl + `/v1/filter/list?catid=${tid}&rank=${rank}&cat=${cat}&year=${year}&area=${area}&act=&size=35&pageno=${pg}&callback=`
|
||||
let response = await this.fetch(url, null, this.getHeader())
|
||||
this.vodList = await this.parseVodShortListFromJsonByCategory(JSON.parse(response), tid)
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let tid_list = id.split("+")
|
||||
let url = this.siteUrl + `/v1/detail?cat=${tid_list[1]}&id=${tid_list[0]}`
|
||||
let response = await this.fetch(url, null, this.getHeader())
|
||||
this.vodDetail = await this.parseVodDetailfromJson(JSON.parse(response))
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = `https://api.so.360kan.com/index?force_v=1&kw=${wd}&from=&pageno=1&v_ap=1&tab=all`
|
||||
let response = await this.fetch(url, null, this.getHeader())
|
||||
this.vodList = await this.parseVodShortListFromJsonBySearch(JSON.parse(response))
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
if (this.danmuUrl) {
|
||||
this.danmuUrl = await this.danmuSpider.getVideoUrl(id, 0)
|
||||
}
|
||||
this.result.parse = 1 //启用自动解析
|
||||
this.result.jx = 1
|
||||
this.playUrl = id
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new Sp360Spider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
886
cat/tjs/js/spider.js
Normal file
@ -0,0 +1,886 @@
|
||||
/*
|
||||
* @File : spider.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/25 17:19
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
|
||||
import {JadeLogging} from "../lib/log.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {_, load, Uri} from "../lib/cat.js";
|
||||
import * as HLS from "../lib/hls.js";
|
||||
import {hlsCache, tsCache} from "../lib/ffm3u8_open.js";
|
||||
import {DanmuSpider} from "../lib/danmuSpider.js";
|
||||
import {initAli} from "../lib/ali.js";
|
||||
|
||||
class Result {
|
||||
constructor() {
|
||||
this.class = []
|
||||
this.list = []
|
||||
this.filters = []
|
||||
this.header = {"User-Agent": Utils.CHROME};
|
||||
this.format = "";
|
||||
this.danmaku = "";
|
||||
this.url = "";
|
||||
this.subs = [];
|
||||
this.parse = 0
|
||||
this.jx = 0;
|
||||
this.page = 0
|
||||
this.pagecount = 0
|
||||
this.limit = 0;
|
||||
this.total = 0;
|
||||
this.extra = {}
|
||||
|
||||
}
|
||||
|
||||
get() {
|
||||
return new Result()
|
||||
}
|
||||
|
||||
home(classes, list, filters) {
|
||||
return JSON.stringify({
|
||||
"class": classes, "list": list, "filters": filters
|
||||
})
|
||||
}
|
||||
|
||||
homeVod(vod_list) {
|
||||
return JSON.stringify({"page": this.page, "list": vod_list, "pagecount": this.page, "total": this.page})
|
||||
}
|
||||
|
||||
category(vod_list, page, count, limit, total) {
|
||||
return JSON.stringify({
|
||||
page: parseInt(page), pagecount: count, limit: limit, total: total, list: vod_list,
|
||||
});
|
||||
}
|
||||
|
||||
search(vod_list) {
|
||||
return JSON.stringify({"list": vod_list})
|
||||
}
|
||||
|
||||
detail(vod_detail) {
|
||||
return JSON.stringify({"list": [vod_detail]})
|
||||
}
|
||||
|
||||
play(url) {
|
||||
if (!_.isEmpty(this.danmaku)) {
|
||||
return JSON.stringify({
|
||||
"url": url,
|
||||
"parse": this.parse,
|
||||
"header": this.header,
|
||||
"format": this.format,
|
||||
"subs": this.subs,
|
||||
"danmaku": this.danmaku,
|
||||
"extra":this.extra,
|
||||
"jx": this.jx
|
||||
})
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
"url": url,
|
||||
"parse": this.parse,
|
||||
"header": this.header,
|
||||
"format": this.format,
|
||||
"subs": this.subs,
|
||||
"extra":this.extra,
|
||||
"jx": this.jx
|
||||
})
|
||||
}
|
||||
}
|
||||
playTxt(url){
|
||||
return url
|
||||
}
|
||||
errorCategory(error_message) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_name = "错误:打开无效"
|
||||
vodShort.vod_id = "error"
|
||||
vodShort.vod_pic = Utils.RESOURCEURL + "/resources/error.png"
|
||||
vodShort.vod_remarks = error_message
|
||||
return JSON.stringify({
|
||||
page: parseInt(0), pagecount: 0, limit: 0, total: 0, list: [vodShort],
|
||||
})
|
||||
}
|
||||
|
||||
setClass(classes) {
|
||||
this.class = classes;
|
||||
return this;
|
||||
}
|
||||
|
||||
setVod(list) {
|
||||
if (typeof list === "object" && Array.isArray(list)) {
|
||||
this.list = list;
|
||||
} else if (list !== undefined) {
|
||||
this.list = [list]
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
setFilters(filters) {
|
||||
this.filters = filters;
|
||||
return this;
|
||||
}
|
||||
|
||||
setHeader(header) {
|
||||
this.header = header;
|
||||
return this;
|
||||
}
|
||||
|
||||
setParse(parse) {
|
||||
this.parse = parse;
|
||||
return this;
|
||||
}
|
||||
|
||||
setJx() {
|
||||
this.jx = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
setUrl(url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
danmu(danmaku) {
|
||||
this.danmaku = danmaku;
|
||||
return this;
|
||||
}
|
||||
|
||||
setFormat(format) {
|
||||
this.format = format;
|
||||
return this;
|
||||
}
|
||||
|
||||
setSubs(subs) {
|
||||
this.subs = subs;
|
||||
return this;
|
||||
}
|
||||
|
||||
dash() {
|
||||
this.format = "application/dash+xml";
|
||||
return this;
|
||||
}
|
||||
|
||||
m3u8() {
|
||||
this.format = "application/x-mpegURL";
|
||||
return this;
|
||||
}
|
||||
|
||||
rtsp() {
|
||||
this.format = "application/x-rtsp";
|
||||
return this;
|
||||
}
|
||||
|
||||
octet() {
|
||||
this.format = "application/octet-stream";
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
setPage(page, count, limit, total) {
|
||||
this.page = page
|
||||
this.limit = limit
|
||||
this.total = total
|
||||
this.pagecount = count
|
||||
return this;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
}
|
||||
|
||||
class Spider {
|
||||
constructor() {
|
||||
this.siteKey = ""
|
||||
this.siteType = 0
|
||||
this.jadeLog = new JadeLogging(this.getAppName(), "DEBUG")
|
||||
this.classes = []
|
||||
this.filterObj = {}
|
||||
this.result = new Result()
|
||||
this.catOpenStatus = true
|
||||
this.danmuStaus = false
|
||||
this.reconnectTimes = 0
|
||||
this.maxReconnectTimes = 5
|
||||
this.siteUrl = ""
|
||||
this.vodList = []
|
||||
this.homeVodList = []
|
||||
this.count = 0
|
||||
this.limit = 0
|
||||
this.total = 0
|
||||
this.page = 0
|
||||
this.vodDetail = new VodDetail()
|
||||
this.playUrl = ""
|
||||
this.header = {}
|
||||
this.remove18 = false
|
||||
this.type_id_18 = 0
|
||||
this.type_name_18 = "伦理片"
|
||||
this.episodeObj = {}
|
||||
this.danmuUrl = ""
|
||||
this.cfgObj = {}
|
||||
|
||||
}
|
||||
|
||||
async reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer) {
|
||||
await this.jadeLog.error("请求失败,请检查url:" + reqUrl + ",两秒后重试")
|
||||
Utils.sleep(2)
|
||||
if (this.reconnectTimes < this.maxReconnectTimes) {
|
||||
this.reconnectTimes = this.reconnectTimes + 1
|
||||
return await this.fetch(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||||
} else {
|
||||
await this.jadeLog.error("请求失败,重连失败")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
getClassIdList() {
|
||||
let class_id_list = []
|
||||
for (const class_dic of this.classes) {
|
||||
class_id_list.push(class_dic["type_id"])
|
||||
}
|
||||
return class_id_list
|
||||
}
|
||||
|
||||
getTypeDic(type_name, type_id) {
|
||||
return {"type_name": type_name, "type_id": type_id}
|
||||
}
|
||||
|
||||
async getHtml(url = this.siteUrl, headers = this.getHeader()) {
|
||||
let html = await this.fetch(url, null, headers)
|
||||
if (!_.isEmpty(html)) {
|
||||
return load(html)
|
||||
} else {
|
||||
await this.jadeLog.error(`html获取失败`, true)
|
||||
}
|
||||
}
|
||||
|
||||
getClassNameList() {
|
||||
let class_name_list = []
|
||||
for (const class_dic of this.classes) {
|
||||
class_name_list.push(class_dic["type_name"])
|
||||
}
|
||||
return class_name_list
|
||||
}
|
||||
|
||||
async postReconnect(reqUrl, params, headers) {
|
||||
await this.jadeLog.error("请求失败,请检查url:" + reqUrl + ",两秒后重试")
|
||||
Utils.sleep(2)
|
||||
if (this.reconnectTimes < this.maxReconnectTimes) {
|
||||
this.reconnectTimes = this.reconnectTimes + 1
|
||||
return await this.post(reqUrl, params, headers)
|
||||
} else {
|
||||
await this.jadeLog.error("请求失败,重连失败")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
return {"User-Agent": Utils.CHROME, "Referer": this.siteUrl + "/"};
|
||||
}
|
||||
|
||||
async getResponse(reqUrl, params, headers, redirect_url, return_cookie, buffer, response) {
|
||||
{
|
||||
if (response.headers["location"] !== undefined) {
|
||||
if (redirect_url) {
|
||||
await this.jadeLog.debug(`返回重定向连接:${response.headers["location"]}`)
|
||||
return response.headers["location"]
|
||||
} else {
|
||||
return this.fetch(response.headers["location"], params, headers, redirect_url, return_cookie, buffer)
|
||||
}
|
||||
} else if (response.content.length > 0) {
|
||||
this.reconnectTimes = 0
|
||||
if (return_cookie) {
|
||||
return {"cookie": response.headers["set-cookie"], "content": response.content}
|
||||
} else {
|
||||
return response.content
|
||||
}
|
||||
} else if (buffer === 1) {
|
||||
this.reconnectTimes = 0
|
||||
return response.content
|
||||
} else {
|
||||
await this.jadeLog.error(`请求失败,请求url为:${reqUrl},回复内容为:${JSON.stringify(response)}`)
|
||||
return await this.reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async fetch(reqUrl, params, headers, redirect_url = false, return_cookie = false, buffer = 0) {
|
||||
let data = Utils.objectToStr(params)
|
||||
let url = reqUrl
|
||||
if (!_.isEmpty(data)) {
|
||||
url = reqUrl + "?" + data
|
||||
}
|
||||
let uri = new Uri(url);
|
||||
let response;
|
||||
if (redirect_url) {
|
||||
response = await req(uri.toString(), {
|
||||
method: "get", headers: headers, buffer: buffer, data: null, redirect: 2
|
||||
})
|
||||
} else {
|
||||
response = await req(uri.toString(), {method: "get", headers: headers, buffer: buffer, data: null});
|
||||
}
|
||||
if (response.code === 200 || response.code === 302 || response.code === 301 || return_cookie) {
|
||||
return await this.getResponse(reqUrl, params, headers, redirect_url, return_cookie, buffer, response)
|
||||
} else {
|
||||
await this.jadeLog.error(`请求失败,失败原因为:状态码出错,请求url为:${uri},回复内容为:${JSON.stringify(response)}`)
|
||||
return await this.reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
async redirect(response) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
async post(reqUrl, params, headers, postType = "form") {
|
||||
let uri = new Uri(reqUrl);
|
||||
let response = await req(uri.toString(), {
|
||||
method: "post", headers: headers, data: params, postType: postType
|
||||
});
|
||||
if (response.code === 200 || response.code === undefined || response.code === 302) {
|
||||
// 重定向
|
||||
if (response.headers["location"] !== undefined) {
|
||||
return await this.redirect(response)
|
||||
} else if (!_.isEmpty(response.content)) {
|
||||
this.reconnectTimes = 0
|
||||
return response.content
|
||||
} else {
|
||||
return await this.postReconnect(reqUrl, params, headers)
|
||||
}
|
||||
} else {
|
||||
await this.jadeLog.error(`请求失败,请求url为:${reqUrl},回复内容为${JSON.stringify(response)}`)
|
||||
return await this.postReconnect(reqUrl, params, headers)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getName() {
|
||||
return `🍥┃基础┃🍥`
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return `基础`
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "base"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
|
||||
}
|
||||
|
||||
parseVodShortFromElement($, element) {
|
||||
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocByCategory($) {
|
||||
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
return []
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
async parseVodPlayFromUrl(flag, play_url) {
|
||||
|
||||
}
|
||||
|
||||
async parseVodPlayFromDoc(flag, $) {
|
||||
|
||||
}
|
||||
|
||||
async SpiderInit(cfg) {
|
||||
try {
|
||||
this.siteKey = cfg["skey"]
|
||||
this.siteType = parseInt(cfg["stype"].toString())
|
||||
let extObj = null;
|
||||
if (typeof cfg.ext === "string") {
|
||||
await this.jadeLog.info(`读取配置文件,ext为:${cfg.ext}`)
|
||||
extObj = JSON.parse(cfg.ext)
|
||||
|
||||
} else if (typeof cfg.ext === "object") {
|
||||
await this.jadeLog.info(`读取配置文件,所有参数为:${JSON.stringify(cfg)}`)
|
||||
await this.jadeLog.info(`读取配置文件,ext为:${JSON.stringify(cfg.ext)}`)
|
||||
extObj = cfg.ext
|
||||
} else {
|
||||
await this.jadeLog.error(`不支持的数据类型,数据类型为${typeof cfg.ext}`)
|
||||
}
|
||||
let boxType = extObj["box"]
|
||||
extObj["CatOpenStatus"] = boxType === "CatOpen";
|
||||
return extObj
|
||||
} catch (e) {
|
||||
await this.jadeLog.error("初始化失败,失败原因为:" + e.message)
|
||||
return {"token": null, "CatOpenStatus": false, "code": 0}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async initAli(token, db = null) {
|
||||
await initAli(token, db)
|
||||
}
|
||||
|
||||
async spiderInit() {
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
this.danmuSpider = new DanmuSpider()
|
||||
this.cfgObj = await this.SpiderInit(cfg)
|
||||
await this.jadeLog.debug(`初始化参数为:${JSON.stringify(cfg)}`)
|
||||
this.catOpenStatus = this.cfgObj.CatOpenStatus
|
||||
this.danmuStaus = this.cfgObj["danmu"] ?? this.danmuStaus
|
||||
try {
|
||||
if (await this.loadFilterAndClasses()) {
|
||||
await this.jadeLog.debug(`读取缓存列表和二级菜单成功`)
|
||||
} else {
|
||||
await this.jadeLog.warning(`读取缓存列表和二级菜单失败`)
|
||||
await this.writeFilterAndClasses()
|
||||
}
|
||||
} catch (e) {
|
||||
await local.set(this.siteKey, "classes", JSON.stringify([]));
|
||||
await local.set(this.siteKey, "filterObj", JSON.stringify({}));
|
||||
await this.jadeLog.error("读取缓存失败,失败原因为:" + e)
|
||||
}
|
||||
this.jsBase = await js2Proxy(true, this.siteType, this.siteKey, 'img/', {});
|
||||
this.douBanjsBase = await js2Proxy(true, this.siteType, this.siteKey, 'douban/', {});
|
||||
this.baseProxy = await js2Proxy(true, this.siteType, this.siteKey, 'img/', this.getHeader());
|
||||
this.videoProxy = await js2Proxy(true, this.siteType, this.siteKey, 'm3u8/', {});
|
||||
this.detailProxy = await js2Proxy(true, this.siteType, this.siteKey, 'detail/', this.getHeader());
|
||||
|
||||
}
|
||||
|
||||
async loadFilterAndClasses() {
|
||||
// 强制清空
|
||||
// await local.set(this.siteKey, "classes", JSON.stringify([]));
|
||||
// await local.set(this.siteKey, "filterObj", JSON.stringify({}));
|
||||
this.classes = await this.getClassesCache()
|
||||
this.filterObj = await this.getFiletObjCache()
|
||||
if (this.classes.length > 0) {
|
||||
return true
|
||||
} else {
|
||||
await local.set(this.siteKey, "classes", JSON.stringify([]));
|
||||
await local.set(this.siteKey, "filterObj", JSON.stringify({}));
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async writeFilterAndClasses() {
|
||||
if (this.catOpenStatus) {
|
||||
this.classes.push({"type_name": "最近更新", "type_id": "最近更新"})
|
||||
}
|
||||
await this.setClasses()
|
||||
await this.setFilterObj()
|
||||
await local.set(this.siteKey, "classes", JSON.stringify(this.classes));
|
||||
await local.set(this.siteKey, "filterObj", JSON.stringify(this.filterObj));
|
||||
}
|
||||
|
||||
async getClassesCache() {
|
||||
let cacheClasses = await local.get(this.siteKey, "classes")
|
||||
if (!_.isEmpty(cacheClasses)) {
|
||||
return JSON.parse(cacheClasses)
|
||||
} else {
|
||||
return this.classes
|
||||
}
|
||||
}
|
||||
|
||||
async getFiletObjCache() {
|
||||
let cacheFilterObj = await local.get(this.siteKey, "filterObj")
|
||||
if (!_.isEmpty(cacheFilterObj)) {
|
||||
return JSON.parse(cacheFilterObj)
|
||||
} else {
|
||||
return this.filterObj
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setHome(filter) {
|
||||
}
|
||||
|
||||
async home(filter) {
|
||||
this.vodList = []
|
||||
await this.jadeLog.info("正在解析首页类别", true)
|
||||
await this.setHome(filter)
|
||||
await this.jadeLog.debug(`首页类别内容为:${this.result.home(this.classes, [], this.filterObj)}`)
|
||||
await this.jadeLog.info("首页类别解析完成", true)
|
||||
return this.result.home(this.classes, [], this.filterObj)
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
|
||||
}
|
||||
|
||||
async homeVod() {
|
||||
await this.jadeLog.info("正在解析首页内容", true)
|
||||
await this.setHomeVod()
|
||||
await this.jadeLog.debug(`首页内容为:${this.result.homeVod(this.homeVodList)}`)
|
||||
await this.jadeLog.info("首页内容解析完成", true)
|
||||
return this.result.homeVod(this.homeVodList)
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
|
||||
}
|
||||
|
||||
async category(tid, pg, filter, extend) {
|
||||
this.page = parseInt(pg)
|
||||
await this.jadeLog.info(`正在解析分类页面,tid = ${tid},pg = ${pg},filter = ${filter},extend = ${JSON.stringify(extend)}`)
|
||||
if (tid === "最近更新") {
|
||||
this.page = 0
|
||||
return await this.homeVod()
|
||||
} else {
|
||||
try {
|
||||
this.vodList = []
|
||||
await this.setCategory(tid, pg, filter, extend)
|
||||
await this.jadeLog.debug(`分类页面内容为:${this.result.category(this.vodList, this.page, this.count, this.limit, this.total)}`)
|
||||
await this.jadeLog.info("分类页面解析完成", true)
|
||||
return this.result.category(this.vodList, this.page, this.count, this.limit, this.total)
|
||||
} catch (e) {
|
||||
await this.jadeLog.error(`分类页解析失败,失败原因为:${e}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
setEpisodeCache() {
|
||||
// 记录每个播放链接的集数
|
||||
let episodeObj = {
|
||||
"vodDetail": this.vodDetail.to_dict(),
|
||||
}
|
||||
let vod_url_channels_list = this.vodDetail.vod_play_url.split("$$$")
|
||||
for (const vodItemsStr of vod_url_channels_list) {
|
||||
let vodItems = vodItemsStr.split("#")
|
||||
for (const vodItem of vodItems) {
|
||||
let episodeName = vodItem.split("$")[0].split(" ")[0]
|
||||
let episodeUrl = vodItem.split("$")[1]
|
||||
let matchers = episodeName.match(/\d+/g)
|
||||
if (matchers !== null && matchers.length > 0) {
|
||||
episodeName = matchers[0]
|
||||
}
|
||||
episodeObj[episodeUrl] = {"episodeName": episodeName, "episodeId": episodeName}
|
||||
}
|
||||
}
|
||||
return episodeObj
|
||||
}
|
||||
|
||||
async detail(id) {
|
||||
this.vodDetail = new VodDetail();
|
||||
await this.jadeLog.info(`正在获取详情页面,id为:${id}`)
|
||||
try {
|
||||
await this.setDetail(id)
|
||||
await this.jadeLog.debug(`详情页面内容为:${this.result.detail(this.vodDetail)}`)
|
||||
await this.jadeLog.info("详情页面解析完成", true)
|
||||
this.vodDetail.vod_id = id
|
||||
if (this.siteType === 3) {
|
||||
this.episodeObj = this.setEpisodeCache()
|
||||
}
|
||||
|
||||
return this.result.detail(this.vodDetail)
|
||||
} catch (e) {
|
||||
await this.jadeLog.error("详情界面获取失败,失败原因为:" + e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
this.playUrl = id
|
||||
}
|
||||
|
||||
async setDanmu(id) {
|
||||
await this.jadeLog.debug(`${JSON.stringify(this.episodeObj)}`)
|
||||
let episodeId = this.episodeObj[id]
|
||||
let vodDetail = JSON.parse(this.episodeObj["vodDetail"])
|
||||
delete vodDetail.vod_content;
|
||||
delete vodDetail.vod_play_from;
|
||||
delete vodDetail.vod_play_url;
|
||||
delete vodDetail.vod_pic;
|
||||
await this.jadeLog.debug(`正在加载弹幕,视频详情为:${JSON.stringify(vodDetail)},集数:${JSON.stringify(this.episodeObj[id])}`)
|
||||
//区分电影还是电视剧
|
||||
return await this.danmuSpider.getDammu(vodDetail, episodeId)
|
||||
}
|
||||
|
||||
async play(flag, id, flags) {
|
||||
await this.jadeLog.info(`正在解析播放页面,flag:${flag},id:${id},flags:${flags}`, true)
|
||||
try {
|
||||
let return_result;
|
||||
await this.setPlay(flag, id, flags)
|
||||
if (this.playUrl["content"] !== undefined) {
|
||||
return_result = this.result.playTxt(this.playUrl)
|
||||
} else {
|
||||
if (this.danmuStaus && !this.catOpenStatus) {
|
||||
if (!_.isEmpty(this.danmuUrl)) {
|
||||
await this.jadeLog.debug("播放详情页面有弹幕,所以不需要再查找弹幕")
|
||||
return_result = this.result.setHeader(this.header).danmu(this.danmuUrl).play(this.playUrl)
|
||||
} else {
|
||||
let danmuUrl;
|
||||
try {
|
||||
danmuUrl = await this.setDanmu(id)
|
||||
} catch (e) {
|
||||
await this.jadeLog.error(`弹幕加载失败,失败原因为:${e}`)
|
||||
}
|
||||
return_result = this.result.setHeader(this.header).danmu(danmuUrl).play(this.playUrl)
|
||||
}
|
||||
|
||||
} else {
|
||||
await this.jadeLog.debug("不需要加载弹幕", true)
|
||||
return_result = this.result.setHeader(this.header).play(this.playUrl)
|
||||
}
|
||||
}
|
||||
await this.jadeLog.info("播放页面解析完成", true)
|
||||
await this.jadeLog.debug(`播放页面内容为:${return_result}`)
|
||||
return return_result;
|
||||
|
||||
} catch (e) {
|
||||
await this.jadeLog.error("解析播放页面出错,失败原因为:" + e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
|
||||
}
|
||||
|
||||
async search(wd, quick) {
|
||||
this.vodList = []
|
||||
await this.jadeLog.info(`正在解析搜索页面,关键词为 = ${wd},quick = ${quick}`)
|
||||
await this.setSearch(wd, quick)
|
||||
if (this.vodList.length === 0) {
|
||||
if (wd.indexOf(" ") > -1) {
|
||||
await this.jadeLog.debug(`搜索关键词为:${wd},其中有空格,去除空格在搜索一次`)
|
||||
await this.search(wd.replaceAll(" ", "").replaceAll("", ""), quick)
|
||||
}
|
||||
}
|
||||
await this.jadeLog.debug(`搜索页面内容为:${this.result.search(this.vodList)}`)
|
||||
await this.jadeLog.info("搜索页面解析完成", true)
|
||||
return this.result.search(this.vodList)
|
||||
}
|
||||
|
||||
async getImg(url, headers) {
|
||||
let resp;
|
||||
if (_.isEmpty(headers)) {
|
||||
headers = {Referer: url, 'User-Agent': Utils.CHROME}
|
||||
}
|
||||
resp = await req(url, {buffer: 2, headers: headers});
|
||||
try {
|
||||
Utils.base64Decode(resp.content)
|
||||
await this.jadeLog.error(`图片代理获取失败,重连失败`, true)
|
||||
this.reconnectTimes = 0
|
||||
return {"code": 500, "headers": headers, "content": "加载失败"}
|
||||
} catch (e) {
|
||||
await this.jadeLog.debug("图片代理成功", true)
|
||||
this.reconnectTimes = 0
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
||||
async proxy(segments, headers) {
|
||||
await this.jadeLog.debug(`正在设置反向代理 segments = ${segments.join(",")},headers = ${JSON.stringify(headers)}`)
|
||||
let what = segments[0];
|
||||
let url = Utils.base64Decode(segments[1]);
|
||||
await this.jadeLog.debug(`反向代理参数为:${url}`)
|
||||
if (what === 'img') {
|
||||
await this.jadeLog.debug("通过代理获取图片", true)
|
||||
let resp = await this.getImg(url, headers)
|
||||
return JSON.stringify({
|
||||
code: resp.code, buffer: 2, content: resp.content, headers: resp.headers,
|
||||
});
|
||||
} else if (what === "douban") {
|
||||
let vod_list = await this.doubanSearch(url)
|
||||
if (vod_list !== null) {
|
||||
let vod_pic = vod_list[0].vod_pic
|
||||
let resp;
|
||||
if (!_.isEmpty(headers)) {
|
||||
resp = await req(vod_pic, {
|
||||
buffer: 2, headers: headers
|
||||
});
|
||||
} else {
|
||||
resp = await req(vod_pic, {
|
||||
buffer: 2, headers: {
|
||||
Referer: vod_pic, 'User-Agent': Utils.CHROME,
|
||||
},
|
||||
});
|
||||
}
|
||||
return JSON.stringify({
|
||||
code: resp.code, buffer: 2, content: resp.content, headers: resp.headers,
|
||||
});
|
||||
}
|
||||
} else if (what === "m3u8") {
|
||||
let content;
|
||||
|
||||
if (!_.isEmpty(headers)) {
|
||||
content = await this.fetch(url, null, headers, false, false, 2)
|
||||
} else {
|
||||
content = await this.fetch(url, null, {"Referer": url, 'User-Agent': Utils.CHROME}, false, false, 2)
|
||||
}
|
||||
await this.jadeLog.debug(`m3u8返回内容为:${Utils.base64Decode(content)}`)
|
||||
if (!_.isEmpty(content)) {
|
||||
return JSON.stringify({
|
||||
code: 200, buffer: 2, content: content, headers: {},
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
code: 500, buffer: 2, content: content, headers: {},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
} else if (what === 'hls') {
|
||||
function hlsHeader(data, hls) {
|
||||
let hlsHeaders = {};
|
||||
if (data.headers['content-length']) {
|
||||
Object.assign(hlsHeaders, data.headers, {'content-length': hls.length.toString()});
|
||||
} else {
|
||||
Object.assign(hlsHeaders, data.headers);
|
||||
}
|
||||
delete hlsHeaders['transfer-encoding'];
|
||||
if (hlsHeaders['content-encoding'] == 'gzip') {
|
||||
delete hlsHeaders['content-encoding'];
|
||||
}
|
||||
return hlsHeaders;
|
||||
}
|
||||
|
||||
const hlsData = await hlsCache(url, headers);
|
||||
if (hlsData.variants) {
|
||||
// variants -> variants -> .... ignore
|
||||
const hls = HLS.stringify(hlsData.plist);
|
||||
return {
|
||||
code: hlsData.code, content: hls, headers: hlsHeader(hlsData, hls),
|
||||
};
|
||||
} else {
|
||||
const hls = HLS.stringify(hlsData.plist, (segment) => {
|
||||
return js2Proxy(false, this.siteType, this.siteKey, 'ts/' + encodeURIComponent(hlsData.key + '/' + segment.mediaSequenceNumber.toString()), headers);
|
||||
});
|
||||
return {
|
||||
code: hlsData.code, content: hls, headers: hlsHeader(hlsData, hls),
|
||||
};
|
||||
}
|
||||
} else if (what === 'ts') {
|
||||
const info = url.split('/');
|
||||
const hlsKey = info[0];
|
||||
const segIdx = parseInt(info[1]);
|
||||
return await tsCache(hlsKey, segIdx, headers);
|
||||
} else if (what === "detail") {
|
||||
let $ = await this.getHtml(this.siteUrl + url)
|
||||
let vodDetail = await this.parseVodDetailFromDoc($)
|
||||
let resp = await this.getImg(vodDetail.vod_pic, headers)
|
||||
return JSON.stringify({
|
||||
code: resp.code, buffer: 2, content: resp.content, headers: resp.headers,
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
code: 500, content: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getSearchHeader() {
|
||||
const UserAgents = ["api-client/1 com.douban.frodo/7.22.0.beta9(231) Android/23 product/Mate 40 vendor/HUAWEI model/Mate 40 brand/HUAWEI rom/android network/wifi platform/AndroidPad", "api-client/1 com.douban.frodo/7.18.0(230) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1", "api-client/1 com.douban.frodo/7.1.0(205) Android/29 product/perseus vendor/Xiaomi model/Mi MIX 3 rom/miui6 network/wifi platform/mobile nd/1", "api-client/1 com.douban.frodo/7.3.0(207) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1"]
|
||||
let randomNumber = Math.floor(Math.random() * UserAgents.length); // 生成一个介于0到9之间的随机整数
|
||||
return {
|
||||
'User-Agent': UserAgents[randomNumber]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async parseDoubanVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const item of obj) {
|
||||
let vod_short = new VodShort()
|
||||
vod_short.vod_id = "msearch:" + item["id"]
|
||||
if (item["title"] === undefined) {
|
||||
vod_short.vod_name = item["target"]["title"]
|
||||
} else {
|
||||
vod_short.vod_name = item["title"]
|
||||
}
|
||||
if (item["pic"] === undefined) {
|
||||
vod_short.vod_pic = item["target"]["cover_url"]
|
||||
} else {
|
||||
vod_short.vod_pic = item["pic"]["normal"]
|
||||
}
|
||||
if (item["rating"] === undefined) {
|
||||
vod_short.vod_remarks = "评分:" + item["target"]["rating"]["value"].toString()
|
||||
} else {
|
||||
vod_short.vod_remarks = "评分:" + item["rating"]["value"].toString()
|
||||
}
|
||||
vod_list.push(vod_short);
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
sign(url, ts, method = 'GET') {
|
||||
let _api_secret_key = "bf7dddc7c9cfe6f7"
|
||||
let url_path = "%2F" + url.split("/").slice(3).join("%2F")
|
||||
let raw_sign = [method.toLocaleUpperCase(), url_path, ts.toString()].join("&")
|
||||
return CryptoJS.HmacSHA1(raw_sign, _api_secret_key).toString(CryptoJS.enc.Base64)
|
||||
}
|
||||
|
||||
async doubanSearch(wd) {
|
||||
try {
|
||||
let _api_url = "https://frodo.douban.com/api/v2"
|
||||
let _api_key = "0dad551ec0f84ed02907ff5c42e8ec70"
|
||||
let url = _api_url + "/search/movie"
|
||||
let date = new Date()
|
||||
let ts = date.getFullYear().toString() + (date.getMonth() + 1).toString() + date.getDate().toString()
|
||||
let params = {
|
||||
'_sig': this.sign(url, ts),
|
||||
'_ts': ts,
|
||||
'apiKey': _api_key,
|
||||
'count': 20,
|
||||
'os_rom': 'android',
|
||||
'q': encodeURIComponent(wd),
|
||||
'start': 0
|
||||
}
|
||||
let content = await this.fetch(url, params, this.getSearchHeader())
|
||||
if (!_.isEmpty(content)) {
|
||||
let content_json = JSON.parse(content)
|
||||
await this.jadeLog.debug(`豆瓣搜索结果:${content}`)
|
||||
return await this.parseDoubanVodShortListFromJson(content_json["items"])
|
||||
}
|
||||
return null
|
||||
|
||||
} catch (e) {
|
||||
await this.jadeLog.error("反向代理出错,失败原因为:" + e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export {Spider, Result}
|
||||
290
cat/tjs/js/star.js
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* @File : star.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/21 10:36
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 星视界 需要翻墙
|
||||
*/
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
import {_} from "../lib/cat.js";
|
||||
|
||||
|
||||
class StarSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.histar.tv"
|
||||
this.apiUrl = "https://aws.ulivetv.net"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "星视界"
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "☄️┃星视界┃墙☄️"
|
||||
}
|
||||
getJSName() {
|
||||
return "star"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
getApiHeader() {
|
||||
return {'User-Agent': Utils.MOBILEUA, "Content-Type": 'application/json'}
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let $ = await this.getHtml()
|
||||
let navElements = $($("[class=\"nav_nav__zgz60\"]")[0]).find("a")
|
||||
for (const navElement of navElements) {
|
||||
let type_id = navElement.attribs.href
|
||||
let type_name = $(navElement).text()
|
||||
if (type_id !== "/" && type_name !== "电视直播") {
|
||||
this.classes.push(this.getTypeDic(type_name, type_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convertTypeData(typeData, key, name) {
|
||||
if (!typeData || !typeData[key] || typeData[key].length <= 2) {
|
||||
return null;
|
||||
}
|
||||
let valueList = typeData[key];
|
||||
if (key === 'time') {
|
||||
valueList = valueList.sort((a, b) => {
|
||||
return b - a;
|
||||
});
|
||||
valueList.pop();
|
||||
}
|
||||
const values = _.map(valueList, (item) => {
|
||||
let name;
|
||||
let value;
|
||||
if (item instanceof Array) {
|
||||
name = item[0];
|
||||
value = item[0];
|
||||
} else {
|
||||
name = item.toString();
|
||||
value = item.toString();
|
||||
}
|
||||
return {
|
||||
n: name, v: value,
|
||||
};
|
||||
});
|
||||
values.unshift({
|
||||
n: '全部', v: '',
|
||||
});
|
||||
return {
|
||||
key: key, name: name, init: '', value: values,
|
||||
};
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
const json = $('#__NEXT_DATA__')[0].children[0].data;
|
||||
const obj = JSON.parse(json).props["pageProps"]["filterCondition"];
|
||||
const label = this.convertTypeData(obj, 'label', '类型');
|
||||
const country = this.convertTypeData(obj, 'country', '地区');
|
||||
const time = this.convertTypeData(obj, 'time', '年份');
|
||||
return [label, country, time];
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const type_dic of this.classes.slice(1, 5)) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "最近更新") {
|
||||
let url = this.siteUrl + `${type_id}/all/all/all`
|
||||
let $ = await this.getHtml(url)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseVodShortFromtJson(obj) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = obj["id"]
|
||||
vodShort.vod_name = obj["name"]
|
||||
vodShort.vod_pic = obj["img"]
|
||||
if (_.isEmpty(vodShort.vod_pic)) {
|
||||
vodShort.vod_pic = obj["picurl"] ?? ""
|
||||
}
|
||||
vodShort.vod_remarks = obj["countStr"]
|
||||
if (_.isEmpty(vodShort.vod_remarks)) {
|
||||
vodShort.vod_remarks = obj["time"]
|
||||
}
|
||||
return vodShort
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const results of obj) {
|
||||
let name = results["name"]
|
||||
if (name !== "电视直播") {
|
||||
let cards = results["cards"]
|
||||
for (const result of cards) {
|
||||
let vodShort = this.parseVodShortFromtJson(result)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodShortListFromJsonByCategory(obj) {
|
||||
let vod_list = []
|
||||
for (const result of obj.list) {
|
||||
let vodShort = this.parseVodShortFromtJson(result)
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
getObjectValues(objList,key){
|
||||
let value_list = []
|
||||
for (const result of objList){
|
||||
value_list.push(result[key])
|
||||
}
|
||||
return value_list
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
const vObj = obj["collectionInfo"];
|
||||
let vodDetail = new VodDetail();
|
||||
vodDetail.vod_name = vObj["name"]
|
||||
vodDetail.type_name = vObj["chname"]
|
||||
vodDetail.vod_pic = vObj["picurl"]
|
||||
vodDetail.vod_area = vObj["country"]
|
||||
vodDetail.vod_remarks = vObj["countStr"]
|
||||
vodDetail.vod_actor = this.getObjectValues(vObj["actor"],"name").join("/")
|
||||
vodDetail.vod_director = this.getObjectValues(vObj["director"],"name").join("/")
|
||||
vodDetail.vod_content = vObj["desc"]
|
||||
const playInfo = vObj["videosGroup"];
|
||||
const playVod = {};
|
||||
_.each(playInfo, (info) => {
|
||||
const sourceName = info.name;
|
||||
let playList = '';
|
||||
const videoInfo = info["videos"];
|
||||
const vodItems = _.map(videoInfo, (epObj) => {
|
||||
const epName = "第" + epObj["eporder"] + "集";
|
||||
const playUrl = epObj["purl"]
|
||||
return epName + '$' + playUrl;
|
||||
});
|
||||
if (_.isEmpty(vodItems)) return;
|
||||
playList = vodItems.join('#');
|
||||
playVod[sourceName] = playList;
|
||||
});
|
||||
vodDetail.vod_play_from = _.keys(playVod).join('$$$');
|
||||
vodDetail.vod_play_url = _.values(playVod).join('$$$');
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let json = await this.fetch(this.apiUrl + "/v3/web/api/home?chName=首页", null, this.getApiHeader())
|
||||
const obj = JSON.parse(json)["data"]["cardsGroup"];
|
||||
this.homeVodList = await this.parseVodShortListFromJson(obj)
|
||||
}
|
||||
|
||||
getClassChName(tid) {
|
||||
for (const class_dic of this.classes) {
|
||||
if (tid === class_dic["type_id"]) {
|
||||
return class_dic["type_name"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
this.limit = 16;
|
||||
const param = {
|
||||
chName: this.getClassChName(tid), page: parseInt(pg), pageSize: this.limit
|
||||
};
|
||||
if (extend["label"] !== undefined) {
|
||||
param["label"] = extend["label"]
|
||||
}
|
||||
if (extend["country"] !== undefined) {
|
||||
param["country"] = extend["country"]
|
||||
}
|
||||
if (extend["time"] !== undefined) {
|
||||
const year = parseInt(extend["time"]);
|
||||
param["startTime"] = year;
|
||||
param["endTime"] = year;
|
||||
}
|
||||
const json = await this.post(this.apiUrl + '/v3/web/api/filter', JSON.stringify(param), {
|
||||
'User-Agent': Utils.MOBILEUA, "Content-Type": 'application/json'
|
||||
}, "")
|
||||
const data = JSON.parse(json).data;
|
||||
this.vodList = await this.parseVodShortListFromJsonByCategory(data)
|
||||
this.count = Math.floor(data["total"] / this.limit);
|
||||
this.total = data.total
|
||||
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
|
||||
const $ = await this.getHtml(this.siteUrl + '/vod/detail/' + id);
|
||||
const json = $('#__NEXT_DATA__')[0].children[0].data;
|
||||
const obj = JSON.parse(json).props["pageProps"];
|
||||
this.vodDetail = await this.parseVodDetailfromJson(obj)
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
const limit = 20;
|
||||
const param = {
|
||||
word: wd, page: 1, pageSize: limit,
|
||||
};
|
||||
const json = await this.post(this.apiUrl + '/v3/web/api/search', JSON.stringify(param), this.getApiHeader(), "");
|
||||
const data = JSON.parse(json).data;
|
||||
this.vodList = await this.parseVodShortListFromJsonByCategory(data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new StarSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
108
cat/tjs/js/testSpider.js
Normal file
@ -0,0 +1,108 @@
|
||||
import {__jsEvalReturn} from './bilibili.js';
|
||||
import * as Utils from "../lib/utils.js";
|
||||
|
||||
|
||||
let spider = __jsEvalReturn();
|
||||
|
||||
async function testPlay(vodDetail) {
|
||||
if (vodDetail.list && vodDetail.list.length > 0) {
|
||||
const pFlag = vodDetail.list[0].vod_play_from.split('$$$');
|
||||
const pUrls = vodDetail.list[0].vod_play_url.split('$$$');
|
||||
if (pFlag.length > 0 && pUrls.length > 0) {
|
||||
for (const i in pFlag) {
|
||||
// console.debug(i)
|
||||
let flag = pFlag[i];
|
||||
let urls = pUrls[i].split('#');
|
||||
// console.debug(flag, urls)
|
||||
for (const j in urls) {
|
||||
var name = urls[j].split('$')[0];
|
||||
var url = urls[j].split('$')[1];
|
||||
console.debug(flag + " | " + name + " | " + url);
|
||||
var playUrl = await spider.play(flag, url, []);
|
||||
console.debug('playURL: ' + playUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function testMusicPlay(vodDetail) {
|
||||
if (vodDetail.list && vodDetail.list.length > 0) {
|
||||
const pFlag = vodDetail.list[0].volumes.split('$$$');
|
||||
const pUrls = vodDetail.list[0].urls.split('$$$');
|
||||
if (pFlag.length > 0 && pUrls.length > 0) {
|
||||
for (const i in pFlag) {
|
||||
// console.debug(i)
|
||||
let flag = pFlag[i];
|
||||
let urls = pUrls[i].split('#');
|
||||
// console.debug(flag, urls)
|
||||
for (const j in urls) {
|
||||
var name = urls[j].split('$')[0];
|
||||
var url = urls[j].split('$')[1];
|
||||
console.debug(flag + " | " + name + " | " + url);
|
||||
var playUrl = await spider.play(flag, url, []);
|
||||
console.debug('playURL: ' + playUrl);
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function test() {
|
||||
let siteKey = 'bilibili';
|
||||
let siteType = 0;
|
||||
await spider.init({
|
||||
skey: siteKey, stype: siteType, ext: {
|
||||
"token": "6827db23e5474d02a07fd7431d3d5a5a",
|
||||
"box": "TV",
|
||||
"code": "1",
|
||||
"from": "justlive1",
|
||||
"danmu": true,
|
||||
"cookie": "buvid3=02675249-8ED3-C418-87F5-59E18316459714816infoc; b_nut=1704421014; _uuid=5D435F74-F574-D9AB-62C1-B9294DE465D913102infoc; buvid_fp=e8c5650c749398e9b5cad3f3ddb5081e; buvid4=007E85D1-52C1-7E6E-07CF-837FFBC9349516677-024010502-J5vTDSZDCw4fNnXRejbSVg%3D%3D; rpdid=|()kYJmulRu0J'u~|RRJl)JR; PVID=1; SESSDATA=3be091d3%2C1720332009%2C699ed%2A11CjAcCdwXG5kY1umhCOpQHOn_WP7L9xFBfWO7KKd4BPweodpR6VyIfeNyPiRmkr5jCqsSVjg0R0dZOVVHRUo3RnhPRTZFc3JPbGdiUjFCdHpiRDhiTkticmdKTjVyS1VhbDdvNjFMSDJlbUJydUlRdjFUNGFBNkJlV2ZTa0N1Q1BEVi1QYTQzTUh3IIEC; bili_jct=b0ee7b5d3f27df893545d811d95506d4; DedeUserID=78014638; DedeUserID__ckMd5=4c8c5d65065e468a; enable_web_push=DISABLE; header_theme_version=CLOSE; home_feed_column=5; CURRENT_BLACKGAP=0; CURRENT_FNVAL=4048; b_lsid=75E916AA_18EA1A8D995; bsource=search_baidu; FEED_LIVE_VERSION=V_HEADER_LIVE_NO_POP; browser_resolution=1507-691; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTIzNjk5MTMsImlhdCI6MTcxMjExMDY1MywicGx0IjotMX0.8zQW_fNTCSBlK_JkHnzu3gDw62wuTK1qgKcbGec3swM; bili_ticket_expires=171236985"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let classes = JSON.parse(await spider.home(true));
|
||||
console.debug(JSON.stringify(classes))
|
||||
|
||||
|
||||
// //测试首页列表
|
||||
let homeVod = JSON.parse(await spider.homeVod())
|
||||
console.debug(JSON.stringify(homeVod));
|
||||
|
||||
|
||||
|
||||
//测试分类列表
|
||||
let catePage = JSON.parse(await spider.category("历史记录", "1", undefined, {}));
|
||||
console.debug(JSON.stringify(catePage));
|
||||
|
||||
// 测试详情
|
||||
let detail1 = JSON.parse(await spider.detail("BV12r421t7nu"))
|
||||
await testPlay(detail1)
|
||||
|
||||
|
||||
|
||||
|
||||
// 测试搜索
|
||||
let search_page = JSON.parse(await spider.search("海贼王"))
|
||||
console.debug(JSON.stringify(search_page));
|
||||
|
||||
|
||||
// 测试详情
|
||||
if (search_page.list && search_page.list.length > 0) {
|
||||
for (const k in search_page.list) {
|
||||
// console.debug(k)
|
||||
if (k >= 1) break;
|
||||
let obj = search_page.list[k]
|
||||
let spVid = search_page.list[k].vod_id
|
||||
console.debug("===", spVid)
|
||||
var detail = JSON.parse(await spider.detail(spVid || search_page.list[k].vod_id));
|
||||
|
||||
await testPlay(detail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {test};
|
||||
184
cat/tjs/js/vodSpider.js
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* @File : vodSpider.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/6 16:53
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
class VodSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "http://cj.ffzyapi.com"
|
||||
this.remove18 = false
|
||||
this.type_id_18 = 34
|
||||
}
|
||||
|
||||
async spiderInit(inReq) {
|
||||
if (inReq !== null) {
|
||||
this.detailProxy = await js2Proxy(inReq, "detail", this.getHeader());
|
||||
} else {
|
||||
this.detailProxy = await js2Proxy(true, this.siteType, this.siteKey, 'detail/', this.getHeader());
|
||||
}
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await this.spiderInit(null)
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj, isSearch = false) {
|
||||
let vod_list = []
|
||||
let vodShort;
|
||||
for (const vod_data of obj["list"]) {
|
||||
if (!isSearch) {
|
||||
vodShort = this.parseVodDetail(vod_data)
|
||||
} else {
|
||||
vodShort = new VodShort();
|
||||
vodShort.vod_pic = this.detailProxy + Utils.base64Encode(vod_data["vod_id"])
|
||||
vodShort.vod_id = vod_data["vod_id"]
|
||||
vodShort.vod_name = vod_data["vod_name"]
|
||||
vodShort.vod_remarks = vod_data["vod_remarks"]
|
||||
}
|
||||
if (this.remove18 && vod_data["type_id"] !== this.type_id_18) {
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
if (!this.remove18 && vod_data["type_id"] === this.type_id_18) {
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
|
||||
parseVodDetail(vod_data) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_id = vod_data["vod_id"]
|
||||
vodDetail.vod_name = vod_data["vod_name"]
|
||||
vodDetail.vod_pic = vod_data["vod_pic"]
|
||||
vodDetail.vod_remarks = vod_data["vod_remarks"]
|
||||
vodDetail.vod_area = vod_data["vod_area"]
|
||||
vodDetail.vod_year = vod_data["vod_year"]
|
||||
vodDetail.vod_actor = vod_data["vod_actor"]
|
||||
vodDetail.vod_director = vod_data["vod_director"]
|
||||
let $ = load(vod_data['vod_content'])
|
||||
vodDetail.vod_content = $.text()
|
||||
vodDetail.vod_play_from = vod_data["vod_play_from"]
|
||||
vodDetail.vod_play_url = vod_data["vod_play_url"]
|
||||
vodDetail.type_name = vod_data["type_name"]
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let vodDetail;
|
||||
let vod_data_list = obj["list"]
|
||||
if (vod_data_list.length > 0) {
|
||||
let vod_data = vod_data_list[0]
|
||||
vodDetail = this.parseVodDetail(vod_data)
|
||||
}
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let content = await this.fetch(this.siteUrl + "/api.php/provide/vod/from", {"ac": "list"}, this.getHeader())
|
||||
let content_json = JSON.parse(content)
|
||||
for (const class_dic of content_json["class"]) {
|
||||
if (class_dic["type_pid"] !== 0) {
|
||||
this.classes.push(this.getTypeDic(class_dic["type_name"], class_dic["type_id"]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
let content = await this.fetch(this.siteUrl + "/api.php/provide/vod/from", {"ac": "list"}, this.getHeader())
|
||||
let content_json = JSON.parse(content)
|
||||
for (const root_class_dic of this.classes) {
|
||||
let type_id = root_class_dic["type_id"].toString()
|
||||
if (type_id !== "最近更新") {
|
||||
let extend_dic = {"key": "1", "name": "分类", "value": [{"n": "全部", "v": type_id}]}
|
||||
for (const class_dic of content_json["class"]) {
|
||||
let type_name = class_dic["type_name"]
|
||||
if (type_name === this.type_name_18) {
|
||||
this.type_id_18 = class_dic["type_id"].toString()
|
||||
}
|
||||
if (this.remove18) {
|
||||
if (class_dic["type_pid"] === root_class_dic["type_id"] && type_name !== this.type_name_18) {
|
||||
extend_dic["value"].push({"n": type_name, "v": class_dic["type_id"].toString()})
|
||||
}
|
||||
} else {
|
||||
if (class_dic["type_pid"] === root_class_dic["type_id"] && type_name === this.type_name_18) {
|
||||
extend_dic["value"].push({"n": type_name, "v": class_dic["type_id"].toString()})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!this.remove18) {
|
||||
this.classes = [this.getTypeDic("最近更新", "最近更新"), this.getTypeDic(this.type_name_18, this.type_id_18)]
|
||||
} else {
|
||||
this.filterObj[type_id] = [extend_dic]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let content = await this.fetch(this.siteUrl + "/index.php/ajax/data", {
|
||||
"mid": "1"
|
||||
}, this.getHeader())
|
||||
this.homeVodList = await this.parseVodShortListFromJson(JSON.parse(content))
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let content = await this.fetch(this.siteUrl + "/api.php/provide/vod", {
|
||||
"ac": "detail", "ids": id
|
||||
}, this.getHeader())
|
||||
this.vodDetail = await this.parseVodDetailfromJson(JSON.parse(content))
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
tid = extend["1"] ?? tid
|
||||
let url = this.siteUrl + `/index.php/ajax/data?mid=1&tid=${tid}&page=${pg}&limit=20`
|
||||
await this.jadeLog.debug(`分类URL:${url}`)
|
||||
let content = await this.fetch(url, null, this.getHeader())
|
||||
await this.jadeLog.debug(`分类内容为:${content}`)
|
||||
this.vodList = await this.parseVodShortListFromJson(JSON.parse(content))
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let content = await this.fetch(this.siteUrl + "/api.php/provide/vod/", {"wd": wd}, this.getHeader())
|
||||
this.vodList = await this.parseVodShortListFromJson(JSON.parse(content), true)
|
||||
}
|
||||
|
||||
async proxy(segments, headers) {
|
||||
await this.jadeLog.debug(`正在设置反向代理 segments = ${segments.join(",")},headers = ${JSON.stringify(headers)}`)
|
||||
let what = segments[0];
|
||||
let url = Utils.base64Decode(segments[1]);
|
||||
await this.jadeLog.debug(`反向代理参数为:${url}`)
|
||||
if (what === 'detail') {
|
||||
let content = await this.fetch(this.siteUrl + "/api.php/provide/vod", {
|
||||
"ac": "detail", "ids": url
|
||||
}, this.getHeader())
|
||||
let vod_detail = await this.parseVodDetailfromJson(JSON.parse(content))
|
||||
let pic_content = await this.fetch(vod_detail.vod_pic, null, this.getHeader(), false, false, 2)
|
||||
if (!_.isEmpty(pic_content)) {
|
||||
return JSON.stringify({
|
||||
code: 200, buffer: 2, content: pic_content, headers: {},
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
code: 500, buffer: 2, content: "", headers: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export {VodSpider}
|
||||
322
cat/tjs/js/weixine.js
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* @Author: samples jadehh@live.com
|
||||
* @Date: 2023-12-14 11:03:04
|
||||
* @LastEditors: samples jadehh@live.com
|
||||
* @LastEditTime: 2023-12-14 11:03:04
|
||||
* @FilePath: js/weixine.js
|
||||
* @Description: 阿里影视(已失效)
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import {detailContent, initAli, playContent} from "../lib/ali.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
|
||||
class WeiXineSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = 'https://www.weixine.link';
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await initAli(this.cfgObj["token"]);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return `💂┃阿里影视┃💂`
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "阿里影视"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "weixine"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let items = $('.module-item');
|
||||
let vod_list = [];
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort()
|
||||
let oneA = $(item).find('.module-item-cover .module-item-pic a').first();
|
||||
vodShort.vod_id = oneA.attr('href');
|
||||
vodShort.vod_name = oneA.attr('title');
|
||||
vodShort.vod_pic = $(item).find('.module-item-cover .module-item-pic img').first().attr('data-src');
|
||||
if (vodShort.vod_pic.indexOf("img.php?url=") > 0) {
|
||||
vodShort.vod_pic = vodShort.vod_pic.split("img.php?url=")[1]
|
||||
}
|
||||
vodShort.vod_remarks = $(item).find('.module-item-text').first().text();
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = $('.page-title')[0].children[0].data
|
||||
vodDetail.vod_pic = $($(".mobile-play")).find(".lazyload")[0].attribs["data-src"]
|
||||
let video_info_aux_list = $($(".video-info-aux")).find(".tag-link")[1].children
|
||||
for (const video_info_aux of video_info_aux_list) {
|
||||
try {
|
||||
vodDetail.type_name = vodDetail.type_name + video_info_aux.children[0].data
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
let video_items = $('.video-info-items')
|
||||
vodDetail.vod_director = $(video_items[0]).find("a")[0].children[0].data
|
||||
let vidoe_info_actor_list = $(video_items[1]).find("a")
|
||||
let actor_list = []
|
||||
for (const video_info_actor of vidoe_info_actor_list) {
|
||||
if (video_info_actor.children.length > 0) {
|
||||
actor_list.push(video_info_actor.children[0].data)
|
||||
}
|
||||
}
|
||||
vodDetail.vod_actor = actor_list.join(" * ")
|
||||
vodDetail.vod_year = $(video_items[2]).find("a")[0].children[0].data
|
||||
vodDetail.vod_remarks = `清晰度:${$(video_items[3]).find("div")[0].children[0].data}, 制作人:Jade`
|
||||
vodDetail.vod_content = $(video_items[4]).find("p")[0].children[0].data
|
||||
|
||||
vodDetail.vod_content = vodDetail.vod_content.replace("[收起部分]", "").replace("[展开全部]", "")
|
||||
const share_url_list = [];
|
||||
let items = $('.module-row-info')
|
||||
for (const item of items) {
|
||||
let aliUrl = $(item).find("p")[0].children[0].data
|
||||
let matches = aliUrl.match(Utils.patternAli);
|
||||
if (!_.isEmpty(matches)) share_url_list.push(matches[1])
|
||||
}
|
||||
if (share_url_list.length > 0) {
|
||||
let aliVodDetail = await detailContent(share_url_list)
|
||||
vodDetail.vod_play_url = aliVodDetail.vod_play_url
|
||||
vodDetail.vod_play_from = aliVodDetail.vod_play_from
|
||||
} else {
|
||||
await this.jadeLog.warning(`获取详情界面失败,失败原因为:没有分享链接`)
|
||||
}
|
||||
return vodDetail
|
||||
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let items = $('.module-search-item');
|
||||
let vod_list = [];
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = $(item).find(".video-serial")[0].attribs.href;
|
||||
vodShort.vod_name = $(item).find(".video-serial")[0].attribs.title;
|
||||
vodShort.vod_pic = $(item).find(".module-item-pic > img")[0].attribs['data-src'];
|
||||
vodShort.vod_remarks = '';
|
||||
vod_list.push(vodShort);
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
get_extend_sort_dic(tid) {
|
||||
/***
|
||||
tid为1,2,3的时候,电影,剧情,动漫
|
||||
urlParams#0表示类别,1表示全部地区,2表示人气评分,3表示全部剧情,4表示全部语言,5表示字母查找,6表示页数,11表示时间
|
||||
#key为1,代表全部剧情
|
||||
#key为2,代表全部地区
|
||||
#key为3,代表全部语言
|
||||
#key为4,代表全部时间
|
||||
#key为5,字幕查找
|
||||
#key为6,时间排序
|
||||
https://www.wogg.xyz/index.php/vodshow/1-全部地区-时间排序-全部剧情-全部语言-字幕查找------全部时间.html
|
||||
|
||||
tid为4,综艺
|
||||
#key为1,代表全部地区
|
||||
#key为2,代表全部时间
|
||||
#key为3,字幕查找
|
||||
#key为4,时间排序
|
||||
https://tvfan.xxooo.cf/index.php/vodshow/4-全部地区-时间排序---字母查找------全部时间.html
|
||||
|
||||
tid为5:音乐
|
||||
#key为1,字幕查找
|
||||
#key为2,时间排序
|
||||
https://tvfan.xxooo.cf/index.php/vodshow/5--时间排序---字幕查找------.html
|
||||
|
||||
tid为6,短剧
|
||||
#key为1,代表全部剧情
|
||||
#key为2,代表全部地区
|
||||
#key为3,代表全部时间
|
||||
#key为4,字幕查找
|
||||
#key为5,时间排序
|
||||
https://tvfan.xxooo.cf/index.php/vodshow/6-全部地区-时间排序-全部剧情--字母查找------全部时间.html
|
||||
*/
|
||||
let extend_dic = {}
|
||||
if (tid < 4) {
|
||||
extend_dic = {
|
||||
"1": 3, "2": 1, "3": 4, "4": 11, "5": 5, "6": 2
|
||||
}
|
||||
} else if (tid === 4) {
|
||||
extend_dic = {
|
||||
"1": 1, "2": 11, "3": 5, "4": 2,
|
||||
}
|
||||
} else if (tid === 6) {
|
||||
extend_dic = {
|
||||
"1": 3, "2": 1, "3": 11, "4": 5, "5": 2,
|
||||
}
|
||||
} else if (tid === 5) {
|
||||
extend_dic = {
|
||||
"1": 5, "2": 2,
|
||||
}
|
||||
}
|
||||
|
||||
return extend_dic
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let con = await this.fetch(this.siteUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(con)) {
|
||||
const $ = load(con);
|
||||
let elements = $('.nav-link')
|
||||
for (const element of elements) {
|
||||
let type_id = parseInt(element.attribs.href.split("/").slice(-1)[0].split(".html")[0])
|
||||
let type_name = element.children.slice(-1)[0].data.replaceAll("\n", "").replaceAll(" ", "").replaceAll("玩偶", "").replaceAll("\t", "")
|
||||
let type_dic = {"type_id": type_id, "type_name": type_name}
|
||||
this.classes.push(type_dic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let elements = $("[class='scroll-content']").slice(1)
|
||||
let extend_list = []
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let extend_dic = {"key": (i + 1).toString(), "name": "", "value": []}
|
||||
if (i < elements.length - 1) {
|
||||
extend_dic["name"] = $($(elements[i]).find("a")[0]).text()
|
||||
extend_dic["value"].push({"n": "全部", "v": "0"})
|
||||
for (const ele of $(elements[i]).find("a").slice(1)) {
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": $(ele).text()})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
} else {
|
||||
extend_dic["name"] = $($(elements[i]).find("a")[0]).text()
|
||||
extend_dic["value"] = [{"n": "全部", "v": "0"}, {
|
||||
"n": $($(elements[i]).find("a")[1]).text(), "v": "hits"
|
||||
}, {"n": $($(elements[i]).find("a")[2]).text(), "v": "score"}]
|
||||
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const type_dic of this.classes) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "/" && type_id !== "最近更新") {
|
||||
let url = this.siteUrl + `/index.php/vodshow/${type_id}--------1---.html`
|
||||
let html = await this.fetch(url, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let con = await this.fetch(this.siteUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(con)) {
|
||||
const $ = load(con);
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let urlParams = [tid.toString(), "", "", "", "", "", "", "", pg.toString(), "", "", ""]
|
||||
let extend_dic = this.get_extend_sort_dic(parseInt(tid))
|
||||
for (const key of Object.keys(extend_dic)) {
|
||||
if (extend[key] === "0") {
|
||||
urlParams[extend_dic[key]] = ""
|
||||
} else {
|
||||
urlParams[extend_dic[key]] = extend[key]
|
||||
}
|
||||
}
|
||||
let reqUrl = this.siteUrl + '/index.php/vodshow/' + urlParams.join("-") + '.html';
|
||||
let html = await this.fetch(reqUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
let total = Utils.getStrByRegex(/\$\("\.mac_total"\)\.text\('(\d+)'\)/, html)
|
||||
this.limit = 72;
|
||||
if (total.length > 0) {
|
||||
this.total = parseInt(total)
|
||||
}
|
||||
if (this.total <= this.limit) {
|
||||
this.count = 1
|
||||
} else {
|
||||
this.count = Math.ceil(this.total / this.limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let detailUrl = this.siteUrl + id;
|
||||
let html = await this.fetch(detailUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async play(flag, id, flags) {
|
||||
return await playContent(flag, id, flags);
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let searchUrl = this.siteUrl + '/index.php/vodsearch/-------------.html?wd=' + wd;
|
||||
let html = await this.fetch(searchUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new WeiXineSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
319
cat/tjs/js/wogg.js
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* @Author: samples jadehh@live.com
|
||||
* @Date: 2023-12-14 11:03:04
|
||||
* @LastEditors: samples jadehh@live.com
|
||||
* @LastEditTime: 2023-12-14 11:03:04
|
||||
* @FilePath: js/wogg.js
|
||||
* @Description: 玩偶哥哥爬虫类
|
||||
*/
|
||||
import {_, load} from '../lib/cat.js';
|
||||
import {VodDetail, VodShort} from "../lib/vod.js"
|
||||
import {detailContent, initAli, playContent} from "../lib/ali.js";
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
|
||||
class WoggSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = 'https://tvfan.xxooo.cf';
|
||||
this.woggTypeObj = {"玩偶电影":"电影","玩偶剧集":"电视剧"}
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
await initAli(this.cfgObj["token"]);
|
||||
this.danmuStaus = true
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "💂┃阿里玩偶┃💂"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "阿里玩偶"
|
||||
}
|
||||
getJSName() {
|
||||
return "wogg"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
async parseVodShortListFromDoc($) {
|
||||
let items = $('.module-item');
|
||||
let vod_list = [];
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort()
|
||||
let oneA = $(item).find('.module-item-cover .module-item-pic a').first();
|
||||
vodShort.vod_id = oneA.attr('href');
|
||||
vodShort.vod_name = oneA.attr('title');
|
||||
vodShort.vod_pic = $(item).find('.module-item-cover .module-item-pic img').first().attr('data-src');
|
||||
if (vodShort.vod_pic.indexOf("img.php?url=") > 0) {
|
||||
vodShort.vod_pic = vodShort.vod_pic.split("img.php?url=")[1]
|
||||
}
|
||||
vodShort.vod_remarks = $(item).find('.module-item-text').first().text();
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = $('.page-title')[0].children[0].data
|
||||
vodDetail.vod_pic = $($(".mobile-play")).find(".lazyload")[0].attribs["data-src"]
|
||||
vodDetail.type_name = this.woggTypeObj[$("[class=\"video-info-aux\"]").find("a")[0].attribs.title]
|
||||
let video_items = $('.video-info-items')
|
||||
vodDetail.vod_director = $(video_items[0]).find("a")[0].children[0].data
|
||||
let vidoe_info_actor_list = $(video_items[1]).find("a")
|
||||
let actor_list = []
|
||||
for (const video_info_actor of vidoe_info_actor_list) {
|
||||
if (video_info_actor.children.length > 0){
|
||||
actor_list.push(video_info_actor.children[0].data)
|
||||
}
|
||||
}
|
||||
vodDetail.vod_actor = actor_list.join(" * ")
|
||||
vodDetail.vod_year = $(video_items[2]).find("a")[0].children[0].data
|
||||
vodDetail.vod_remarks = `${$(video_items[3]).find("div")[0].children[0].data}, 制作人:Jade`
|
||||
vodDetail.vod_content = $(video_items[4]).find("p")[0].children[0].data
|
||||
|
||||
vodDetail.vod_content = vodDetail.vod_content.replace("[收起部分]", "").replace("[展开全部]", "")
|
||||
const share_url_list = [];
|
||||
let items = $('.module-row-info')
|
||||
for (const item of items) {
|
||||
let aliUrl = $(item).find("p")[0].children[0].data
|
||||
let matches = aliUrl.match(Utils.patternAli);
|
||||
if (!_.isEmpty(matches)) share_url_list.push(matches[1])
|
||||
}
|
||||
if (share_url_list.length > 0) {
|
||||
let aliVodDetail = await detailContent(share_url_list,vodDetail.type_name)
|
||||
vodDetail.vod_play_url = aliVodDetail.vod_play_url
|
||||
vodDetail.vod_play_from = aliVodDetail.vod_play_from
|
||||
} else {
|
||||
await this.jadeLog.warning(`获取详情界面失败,失败原因为:没有分享链接`)
|
||||
}
|
||||
return vodDetail
|
||||
|
||||
}
|
||||
|
||||
async parseVodShortListFromDocBySearch($) {
|
||||
let items = $('.module-search-item');
|
||||
let vod_list = [];
|
||||
for (const item of items) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = $(item).find(".video-serial")[0].attribs.href;
|
||||
vodShort.vod_name = $(item).find(".video-serial")[0].attribs.title;
|
||||
vodShort.vod_pic = $(item).find(".module-item-pic > img")[0].attribs['data-src'];
|
||||
vodShort.vod_remarks = '';
|
||||
vod_list.push(vodShort);
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
get_extend_sort_dic(tid) {
|
||||
/***
|
||||
tid为1,2,3的时候,电影,剧情,动漫
|
||||
urlParams#0表示类别,1表示全部地区,2表示人气评分,3表示全部剧情,4表示全部语言,5表示字母查找,6表示页数,11表示时间
|
||||
#key为1,代表全部剧情
|
||||
#key为2,代表全部地区
|
||||
#key为3,代表全部语言
|
||||
#key为4,代表全部时间
|
||||
#key为5,字幕查找
|
||||
#key为6,时间排序
|
||||
https://www.wogg.xyz/index.php/vodshow/1-全部地区-时间排序-全部剧情-全部语言-字幕查找------全部时间.html
|
||||
|
||||
tid为4,综艺
|
||||
#key为1,代表全部地区
|
||||
#key为2,代表全部时间
|
||||
#key为3,字幕查找
|
||||
#key为4,时间排序
|
||||
https://tvfan.xxooo.cf/index.php/vodshow/4-全部地区-时间排序---字母查找------全部时间.html
|
||||
|
||||
tid为5:音乐
|
||||
#key为1,字幕查找
|
||||
#key为2,时间排序
|
||||
https://tvfan.xxooo.cf/index.php/vodshow/5--时间排序---字幕查找------.html
|
||||
|
||||
tid为6,短剧
|
||||
#key为1,代表全部剧情
|
||||
#key为2,代表全部地区
|
||||
#key为3,代表全部时间
|
||||
#key为4,字幕查找
|
||||
#key为5,时间排序
|
||||
https://tvfan.xxooo.cf/index.php/vodshow/6-全部地区-时间排序-全部剧情--字母查找------全部时间.html
|
||||
*/
|
||||
let extend_dic = {}
|
||||
if (tid < 4) {
|
||||
extend_dic = {
|
||||
"1": 3, "2": 1, "3": 4, "4": 11, "5": 5, "6": 2
|
||||
}
|
||||
} else if (tid === 4) {
|
||||
extend_dic = {
|
||||
"1": 1, "2": 11, "3": 5, "4": 2,
|
||||
}
|
||||
} else if (tid === 6) {
|
||||
extend_dic = {
|
||||
"1": 3, "2": 1, "3": 11, "4": 5, "5": 2,
|
||||
}
|
||||
} else if (tid === 5) {
|
||||
extend_dic = {
|
||||
"1": 5, "2": 2,
|
||||
}
|
||||
}
|
||||
|
||||
return extend_dic
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let con = await this.fetch(this.siteUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(con)) {
|
||||
const $ = load(con);
|
||||
let elements = $('.nav-link')
|
||||
for (const element of elements) {
|
||||
let type_id = parseInt(element.attribs.href.split("/").slice(-1)[0].split(".html")[0])
|
||||
let type_name = element.children.slice(-1)[0].data.replace("\n", "").replace(" ", "").replace("玩偶", "")
|
||||
let type_dic = {"type_id": type_id, "type_name": type_name}
|
||||
this.classes.push(type_dic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFilter($) {
|
||||
let elements = $("[class='scroll-content']").slice(1)
|
||||
let extend_list = []
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let extend_dic = {"key": (i + 1).toString(), "name": "", "value": []}
|
||||
if (i < elements.length - 1) {
|
||||
extend_dic["name"] = $($(elements[i]).find("a")[0]).text()
|
||||
extend_dic["value"].push({"n": "全部", "v": "0"})
|
||||
for (const ele of $(elements[i]).find("a").slice(1)) {
|
||||
extend_dic["value"].push({"n": $(ele).text(), "v": $(ele).text()})
|
||||
}
|
||||
extend_list.push(extend_dic)
|
||||
} else {
|
||||
extend_dic["name"] = $($(elements[i]).find("a")[0]).text()
|
||||
extend_dic["value"] = [{"n": "全部", "v": "0"}, {
|
||||
"n": $($(elements[i]).find("a")[1]).text(),
|
||||
"v": "hits"
|
||||
}, {"n": $($(elements[i]).find("a")[2]).text(), "v": "score"}]
|
||||
|
||||
extend_list.push(extend_dic)
|
||||
}
|
||||
|
||||
}
|
||||
return extend_list
|
||||
}
|
||||
|
||||
async setFilterObj() {
|
||||
for (const type_dic of this.classes) {
|
||||
let type_id = type_dic["type_id"]
|
||||
if (type_id !== "/" && type_id !== "最近更新") {
|
||||
let url = this.siteUrl + `/index.php/vodshow/${type_id}--------1---.html`
|
||||
let html = await this.fetch(url, null, this.getHeader())
|
||||
if (html != null) {
|
||||
let $ = load(html)
|
||||
this.filterObj[type_id] = await this.getFilter($)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async setHomeVod() {
|
||||
let con = await this.fetch(this.siteUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(con)) {
|
||||
const $ = load(con);
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let urlParams = [tid.toString(), "", "", "", "", "", "", "", pg.toString(), "", "", ""]
|
||||
let extend_dic = this.get_extend_sort_dic(parseInt(tid))
|
||||
for (const key of Object.keys(extend_dic)) {
|
||||
if (extend[key] === "0") {
|
||||
urlParams[extend_dic[key]] = ""
|
||||
} else {
|
||||
urlParams[extend_dic[key]] = extend[key]
|
||||
}
|
||||
}
|
||||
let reqUrl = this.siteUrl + '/index.php/vodshow/' + urlParams.join("-") + '.html';
|
||||
let html = await this.fetch(reqUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
let total = Utils.getStrByRegex(/\$\("\.mac_total"\)\.text\('(\d+)'\)/, html)
|
||||
this.limit = 72;
|
||||
if (total.length > 0) {
|
||||
this.total = parseInt(total)
|
||||
}
|
||||
if (this.total <= this.limit) {
|
||||
this.count = 1
|
||||
} else {
|
||||
this.count = Math.ceil(this.total / this.limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let detailUrl = this.siteUrl + id;
|
||||
let html = await this.fetch(detailUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let playObjStr = await playContent(flag, id, flags);
|
||||
this.playUrl = JSON.parse(playObjStr)["url"]
|
||||
}
|
||||
|
||||
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let searchUrl = this.siteUrl + '/index.php/vodsearch/-------------.html?wd=' + wd;
|
||||
let html = await this.fetch(searchUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDocBySearch($)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new WoggSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
|
||||
export {spider}
|
||||
304
cat/tjs/js/xb6v.js
Normal file
@ -0,0 +1,304 @@
|
||||
/*
|
||||
* @File : xb6v.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/26 10:13
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {_, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
|
||||
class Xb6vSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://www.xb6v.com";
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "🧲┃磁力新6V┃🧲"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "磁力新6V"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "xb6v"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
async redirect(response) {
|
||||
await this.jadeLog.debug(`重定向回复值为:${response.content}`)
|
||||
let matcher = /<a HREF=(.*?)>/.exec(response.content)
|
||||
if (matcher.length > 1) {
|
||||
let new_url = this.siteUrl + "/e/search/" + matcher[1].replaceAll("\\", "").replaceAll("\"", "")
|
||||
await this.jadeLog.info(`重定向url为:${new_url}`)
|
||||
return await this.fetch(new_url, null, this.getHeader())
|
||||
}
|
||||
}
|
||||
|
||||
getActorOrDirector(pattern, str) {
|
||||
return Utils.getStrByRegex(pattern, str)
|
||||
.replace(/<br>/g, "")
|
||||
.replace(/ ./g, "")
|
||||
.replace(/&/g, "")
|
||||
.replace(/middot;/g, "・")
|
||||
.replace(/ /g, ",")
|
||||
.replace(/ /g, ",")
|
||||
.replace(/ /g, "");
|
||||
}
|
||||
|
||||
getDescription(pattern, str) {
|
||||
return Utils.getStrByRegex(pattern, str)
|
||||
.replace(/<\/?[^>]+>/g, "")
|
||||
.replace(/\n/g, "")
|
||||
.replace(/&/g, "")
|
||||
.replace(/middot;/g, "・")
|
||||
.replace(/ldquo;/g, "【")
|
||||
.replace(/rdquo;/g, "】")
|
||||
.replace(/ /g, "");
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let items = $("#post_container .post_hover");
|
||||
let vod_list = []
|
||||
for (const item of items) {
|
||||
let element = $(item).find("[class=zoom]")[0];
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = element.attribs["href"];
|
||||
vodShort.vod_name = element.attribs["title"].replaceAll(/<\\?[^>]+>/g, "");
|
||||
vodShort.vod_pic = $(element).find("img")[0].attribs["src"];
|
||||
vodShort.vod_remarks = $(item).find("[rel=\"category tag\"]").text().replaceAll("\n", "").replaceAll(" ", "");
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list;
|
||||
}
|
||||
|
||||
async parseVodDetailFromDoc($) {
|
||||
let sourceList = $("#post_content");
|
||||
let play_form_list = []
|
||||
let play_url_list = []
|
||||
if (!this.catOpenStatus) {
|
||||
let i = 0
|
||||
let circuitName = "磁力线路";
|
||||
for (const source of sourceList) {
|
||||
let aList = $(source).find("table a")
|
||||
let vodItems = []
|
||||
for (const a of aList) {
|
||||
let episodeUrl = a.attribs["href"]
|
||||
let episodeName = a.children[0].data
|
||||
if (!episodeUrl.toLowerCase().startsWith("magnet")) continue;
|
||||
vodItems.push(episodeName + "$" + episodeUrl);
|
||||
}
|
||||
if (vodItems.length > 0) {
|
||||
i++;
|
||||
play_form_list.push(circuitName + i)
|
||||
play_url_list.push(vodItems.join("#"))
|
||||
}
|
||||
}
|
||||
}
|
||||
let playSourceList = $($(".mainleft")).find("[class=\"widget box row\"]")
|
||||
for (const source of playSourceList) {
|
||||
let play_format = $(source).find("h3").text()
|
||||
let vodItems = []
|
||||
if (!_.isEmpty(play_format)) {
|
||||
let urlSourceList = $(source).find("a")
|
||||
for (const url_source of urlSourceList) {
|
||||
vodItems.push(url_source.attribs["title"] + "$" + url_source.attribs["href"])
|
||||
}
|
||||
play_form_list.push(play_format)
|
||||
play_url_list.push(vodItems.join("#"))
|
||||
}
|
||||
}
|
||||
let partHTML = $(".context").html();
|
||||
let vodDetail = new VodDetail();
|
||||
vodDetail.vod_name = $(".article_container > h1").text();
|
||||
vodDetail.vod_pic = $("#post_content img").attr("src");
|
||||
vodDetail.type_name = Utils.getStrByRegex(/◎类 别 (.*?)<br>/, partHTML);
|
||||
if (_.isEmpty(vodDetail.type_name)) vodDetail.type_name = $("[rel=\"category tag\"]").text();
|
||||
vodDetail.vod_year = Utils.getStrByRegex(/◎年 代 (.*?)<br>/, partHTML);
|
||||
if (_.isEmpty(vodDetail.vod_year)) vodDetail.vod_year = Utils.getStrByRegex(/首播:(.*?)<br>"/, partHTML);
|
||||
vodDetail.vod_area = Utils.getStrByRegex(/◎产 地 (.*?)<br>/, partHTML);
|
||||
if (_.isEmpty(vodDetail.vod_year)) vodDetail.vod_area = Utils.getStrByRegex(/地区:(.*?)<br>"/, partHTML);
|
||||
vodDetail.vod_remarks = Utils.getStrByRegex(/◎上映日期 (.*?)<br>/, partHTML);
|
||||
vodDetail.vod_actor = this.getActorOrDirector(/◎演 员 (.*?)<\/p>/, partHTML);
|
||||
if (_.isEmpty(vodDetail.vod_actor)) vodDetail.vod_actor = this.getActorOrDirector(/◎主 演 (.*?)<\/p>/, partHTML);
|
||||
if (_.isEmpty(vodDetail.vod_actor)) vodDetail.vod_actor = this.getActorOrDirector(/主演:(.*?)<br>/, partHTML);
|
||||
vodDetail.vod_director = this.getActorOrDirector(/◎导 演 (.*?)<br>/, partHTML);
|
||||
if (_.isEmpty(vodDetail.vod_director)) vodDetail.vod_director = this.getActorOrDirector(/导演:(.*?)<br>/, partHTML);
|
||||
vodDetail.vod_content = this.getDescription(/◎简 介(.*?)<hr>/gi, partHTML);
|
||||
if (_.isEmpty(vodDetail.vod_content)) vodDetail.vod_content = this.getDescription(/简介(.*?)<\/p>/gi, partHTML);
|
||||
if (_.isEmpty(vodDetail.vod_content)) vodDetail.vod_content = this.getDescription(/◎简 介(.*?)<br>/gi, partHTML);
|
||||
vodDetail.vod_play_from = play_form_list.join("$$$")
|
||||
vodDetail.vod_play_url = play_url_list.join("$$$")
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async parseVodPlayFromDoc(flag, $) {
|
||||
let play_url = ""
|
||||
let html = $.html()
|
||||
switch (flag) {
|
||||
case "播放地址(无插件 极速播放)":
|
||||
case "播放地址三":
|
||||
play_url = $($(".video")).find("iframe")[0].attribs["src"] + "/index.m3u8"
|
||||
break
|
||||
case "播放地址(无需安装插件)":
|
||||
let matchers2 = /url: '(.*?)',/gs.exec(html)
|
||||
if (matchers2.length > 1) {
|
||||
play_url = matchers2[1]
|
||||
}
|
||||
break
|
||||
case "播放地址四":
|
||||
let matchers4 = /source: "(.*?)",/gs.exec(html)
|
||||
if (matchers4.length > 1) {
|
||||
play_url = matchers4[1]
|
||||
}
|
||||
break
|
||||
default:
|
||||
await this.jadeLog.warning(`暂不支持当前格式,当前格式为:${flag}`)
|
||||
break
|
||||
}
|
||||
return play_url
|
||||
}
|
||||
|
||||
|
||||
async setClasses() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html);
|
||||
let elements = $('#menus > li > a');
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let element = elements[i]
|
||||
if (i < 2 || i === elements.length - 1) continue;
|
||||
let typeName = element.children[0].data;
|
||||
let typeId = element.attribs["href"];
|
||||
this.classes.push({"type_name": typeName, "type_id": typeId})
|
||||
if (typeName === "电视剧") {
|
||||
let values = [{"n": "不限", "v": ""}]
|
||||
for (const a of $(element.next).find("a")) {
|
||||
values.push({"n": a.children[0].data, "v": a.attribs["href"].replaceAll(typeId, "")})
|
||||
}
|
||||
this.filterObj[typeId] = [{
|
||||
"key": "cateId", "name": "类型", "value": values
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async setHomeVod() {
|
||||
let html = await this.fetch(this.siteUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html);
|
||||
this.homeVodList = await this.parseVodShortListFromDoc($)
|
||||
} else {
|
||||
await this.jadeLog.info("首页类别解析失败", true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let cateId = extend["cateId"] ?? "";
|
||||
let cateUrl = this.siteUrl + tid + cateId;
|
||||
this.page = parseInt(pg)
|
||||
this.count = 0
|
||||
this.limit = 18;
|
||||
this.total = 0;
|
||||
if (this.page !== 1) {
|
||||
cateUrl += "index_" + pg + ".html";
|
||||
}
|
||||
let html = await this.fetch(cateUrl, null, this.getHeader());
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
let href_elements = $(".pagination > a")
|
||||
if (href_elements.length > 0) {
|
||||
let href = href_elements.slice(-1)[0].attribs["href"];
|
||||
let patternPageCount = /index_(.*?).html/
|
||||
let matches = patternPageCount.exec(href)
|
||||
this.count = parseInt(matches[1])
|
||||
let items = $("#post_container .post_hover");
|
||||
this.total = this.page === this.count ? (this.page - 1) * this.limit + items.length : this.count * this.limit;
|
||||
}
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let searchUrl = this.siteUrl + "/e/search/index.php";
|
||||
let params = {
|
||||
"show": "title", "tempid": "1", "tbname": "article", "mid": "1", "dopost": "search", "keyboard": wd,
|
||||
}
|
||||
let html = await this.post(searchUrl, params, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html)
|
||||
this.vodList = await this.parseVodShortListFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async setDetail(id) {
|
||||
let detailUrl = this.siteUrl + id;
|
||||
let html = await this.fetch(detailUrl, null, this.getHeader())
|
||||
if (!_.isEmpty(html)) {
|
||||
let $ = load(html);
|
||||
this.vodDetail = await this.parseVodDetailFromDoc($)
|
||||
}
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
if (id.toLowerCase().startsWith("magnet")) {
|
||||
this.playUrl = id
|
||||
} else {
|
||||
let playUrl = this.siteUrl + id
|
||||
let html = await this.fetch(playUrl, null, this.getHeader())
|
||||
let $ = load(html)
|
||||
this.playUrl = await this.parseVodPlayFromDoc(flag, $)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let spider = new Xb6vSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
268
cat/tjs/js/yiqikan.js
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* @File : yiqikan.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/19 18:45
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc : 一起看
|
||||
*/
|
||||
import * as Utils from "../lib/utils.js";
|
||||
import {_, load} from "../lib/cat.js";
|
||||
import {VodDetail, VodShort} from "../lib/vod.js";
|
||||
import {Spider} from "./spider.js";
|
||||
|
||||
|
||||
class YiQiKanSpider extends Spider {
|
||||
constructor() {
|
||||
super();
|
||||
this.siteUrl = "https://api.gquaxhce.com"
|
||||
this.nextObj = {}
|
||||
}
|
||||
|
||||
async init(cfg) {
|
||||
await super.init(cfg);
|
||||
this.danmuStaus = true;
|
||||
}
|
||||
|
||||
getRequestId() {
|
||||
let strArr = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
|
||||
let sb = "";
|
||||
for (let i = 0; i < 32; i++) {
|
||||
sb = sb + strArr[_.random(0, strArr.length)];
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
getName() {
|
||||
return "🛫┃一起看┃🛫"
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "一起看"
|
||||
}
|
||||
|
||||
getJSName() {
|
||||
return "yiqikan"
|
||||
}
|
||||
|
||||
getType() {
|
||||
return 3
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
let headers = super.getHeader();
|
||||
headers["Connection"] = "keep-alive"
|
||||
headers["Host"] = "api.gquaxhce.com"
|
||||
return headers
|
||||
}
|
||||
|
||||
getParams(ob_params = null) {
|
||||
let requestId = this.getRequestId()
|
||||
let appid = "e6ddefe09e0349739874563459f56c54"
|
||||
let reqDomain = "m.yqktv888.com"
|
||||
let udid = Utils.getUUID();
|
||||
let appKey = "3359de478f8d45638125e446a10ec541"
|
||||
let params = {"appId": appid}
|
||||
if (!_.isEmpty(ob_params)) {
|
||||
for (const ob_key of Object.keys(ob_params)) {
|
||||
if (!_.isEmpty(ob_params[ob_key]) && (ob_key === "epId" || ob_key === "nextCount" || ob_key === "nextVal" || ob_key === "queryValueJson" || ob_key === "keyword")) {
|
||||
params[ob_key] = ob_params[ob_key]
|
||||
}
|
||||
}
|
||||
}
|
||||
params["reqDomain"] = reqDomain
|
||||
params["requestId"] = requestId
|
||||
params["udid"] = udid
|
||||
if (!_.isEmpty(ob_params)) {
|
||||
for (const ob_key of Object.keys(ob_params)) {
|
||||
if (!_.isEmpty(ob_params[ob_key]) && (ob_key === "vodId" || ob_key === "vodResolution")) {
|
||||
params[ob_key] = ob_params[ob_key]
|
||||
}
|
||||
}
|
||||
}
|
||||
params["appKey"] = appKey
|
||||
params["sign"] = md5X(Utils.objectToStr(params))
|
||||
delete params["appKey"]
|
||||
return params
|
||||
}
|
||||
|
||||
async setClasses() {
|
||||
let response = JSON.parse(await this.post(this.siteUrl + "/v1/api/home/header", this.getParams(), this.getHeader(), "raw"))
|
||||
for (const data of response["data"]["channelList"]) {
|
||||
this.classes.push(this.getTypeDic(data["channelName"], data["channelId"]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async parseVodShortListFromJson(obj) {
|
||||
let vod_list = []
|
||||
for (const data of obj) {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.vod_id = data["vodId"]
|
||||
vodShort.vod_name = data["vodName"]
|
||||
vodShort.vod_remarks = data["watchingCountDesc"]
|
||||
vodShort.vod_pic = data["coverImg"]
|
||||
vod_list.push(vodShort)
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async parseVodDetailfromJson(obj) {
|
||||
let vodDetail = new VodDetail()
|
||||
vodDetail.vod_name = obj["vodName"]
|
||||
vodDetail.vod_content = obj["intro"]
|
||||
vodDetail.vod_area = obj["areaName"]
|
||||
vodDetail.vod_year = obj["year"]
|
||||
vodDetail.type_name = obj["channelName"]
|
||||
vodDetail.vod_remarks = "评分:" + obj["score"].toString()
|
||||
vodDetail.vod_pic = obj["coverImg"]
|
||||
vodDetail.vod_actor = Utils.objToList(obj["actorList"], "vodWorkerName")
|
||||
vodDetail.vod_director = Utils.objToList(obj["directorList"], "vodWorkerName")
|
||||
let playlist = {}
|
||||
for (const playDic of obj["playerList"]) {
|
||||
let vodItems = []
|
||||
for (const item of playDic["epList"]) {
|
||||
let playId = item["epId"]
|
||||
let playName = item["epName"]
|
||||
vodItems.push(playName + "$" + playId)
|
||||
}
|
||||
playlist[playDic["playerName"]] = vodItems.join("#")
|
||||
}
|
||||
vodDetail.vod_play_url = _.values(playlist).join('$$$');
|
||||
vodDetail.vod_play_from = _.keys(playlist).join('$$$');
|
||||
return vodDetail
|
||||
}
|
||||
|
||||
async setHomeVod() {
|
||||
let response = await this.post(this.siteUrl + "/v1/api/home/body", this.getParams(), this.getHeader(), "raw")
|
||||
let resJson = JSON.parse(response)
|
||||
if (resJson["result"]) {
|
||||
this.homeVodList = await this.parseVodShortListFromJson(resJson["data"]["hotVodList"])
|
||||
} else {
|
||||
await this.jadeLog.error(`获取首页失败,失败原因为:${resJson["msg"]}`)
|
||||
}
|
||||
}
|
||||
|
||||
async setCategory(tid, pg, filter, extend) {
|
||||
let url = this.siteUrl + "/v1/api/search/queryNow"
|
||||
this.limit = 18
|
||||
let ob_params = {}
|
||||
if (!_.isEmpty(this.nextObj[tid])) {
|
||||
ob_params["nextVal"] = this.nextObj[tid]
|
||||
}
|
||||
ob_params["nextCount"] = 18
|
||||
ob_params["queryValueJson"] = JSON.stringify([{
|
||||
"filerName": "channelId", "filerValue": tid.toString()
|
||||
}]).replaceAll("\\\\", "")
|
||||
let response = await this.post(url, this.getParams(ob_params), this.getHeader(), "raw")
|
||||
let resJson = JSON.parse(response)
|
||||
if (resJson["result"]) {
|
||||
if (resJson["data"]["hasNext"]) {
|
||||
this.nextObj[tid] = resJson["data"]["nextVal"]
|
||||
}
|
||||
this.vodList = await this.parseVodShortListFromJson(resJson["data"]["items"])
|
||||
} else {
|
||||
await this.jadeLog.error(`获取分类失败,失败原因为:${resJson["msg"]}`)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
async setDetail(id) {
|
||||
let url = this.siteUrl + "/v1/api/vodInfo/detail"
|
||||
let ob_params = {"vodId": id}
|
||||
let response = await this.post(url, this.getParams(ob_params), this.getHeader(), "raw")
|
||||
let resJson = JSON.parse(response)
|
||||
if (resJson["result"]) {
|
||||
this.vodDetail = await this.parseVodDetailfromJson(resJson["data"])
|
||||
} else {
|
||||
await this.jadeLog.error(`获取详情失败,失败原因为:${resJson["msg"]}`)
|
||||
}
|
||||
}
|
||||
|
||||
async setPlay(flag, id, flags) {
|
||||
let url = this.siteUrl + "/v1/api/vodInfo/getEpDetail"
|
||||
let ob_params = {"epId": id}
|
||||
let ep_detail_response = await this.post(url, this.getParams(ob_params), this.getHeader(), "raw")
|
||||
let ep_detail_resJson = JSON.parse(ep_detail_response)
|
||||
let vodResolution = "1";
|
||||
if (ep_detail_resJson["result"]) {
|
||||
if (ep_detail_resJson["data"]["resolutionItems"].length > 0) {
|
||||
vodResolution = ep_detail_resJson["data"]["resolutionItems"].slice(-1)[0]["vodResolution"].toString()
|
||||
let playUrl = this.siteUrl + "/v1/api/vodInfo/getPlayUrl"
|
||||
let play_params = {"epId": id, "vodResolution": vodResolution}
|
||||
let play_response = await this.post(playUrl, this.getParams(play_params), this.getHeader(), "raw")
|
||||
let play_resJson = JSON.parse(play_response)
|
||||
if (play_resJson["result"]) {
|
||||
this.playUrl = play_resJson["data"]
|
||||
}else{
|
||||
await this.jadeLog.error(`获取播放链接失败,失败原因为:${ep_detail_resJson["msg"]}`)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await this.jadeLog.error(`获取播放详情失败,失败原因为:${ep_detail_resJson["msg"]}`)
|
||||
}
|
||||
}
|
||||
|
||||
async setSearch(wd, quick) {
|
||||
let url = this.siteUrl + "/v1/api/search/search"
|
||||
let ob_prams = {"nextCount":15,"nextVal":"","keyword":wd}
|
||||
let esponse = await this.post(url, this.getParams(ob_prams), this.getHeader(), "raw")
|
||||
let resJson = JSON.parse(esponse)
|
||||
if (resJson["result"]) {
|
||||
this.vodList = await this.parseVodShortListFromJson(resJson["data"]["items"])
|
||||
} else {
|
||||
await this.jadeLog.error(`获取详情失败,失败原因为:${resJson["msg"]}`)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let spider = new YiQiKanSpider()
|
||||
|
||||
async function init(cfg) {
|
||||
await spider.init(cfg)
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return await spider.home(filter)
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await spider.homeVod()
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
return await spider.category(tid, pg, filter, extend)
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
return await spider.detail(id)
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
return await spider.play(flag, id, flags)
|
||||
}
|
||||
|
||||
async function search(wd, quick) {
|
||||
return await spider.search(wd, quick)
|
||||
}
|
||||
|
||||
async function proxy(segments, headers) {
|
||||
return await spider.proxy(segments, headers)
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
proxy: proxy
|
||||
};
|
||||
}
|
||||
export {spider}
|
||||
190
cat/tjs/lib/TextDecoder.js
Normal file
@ -0,0 +1,190 @@
|
||||
import Stream, { DEFAULT_ENCODING, getEncoding } from './text_decoder_index.js'
|
||||
import { end_of_stream, finished, codePointsToString } from './text_decoder_utils.js'
|
||||
import { decoders } from './table.js'
|
||||
|
||||
// 8.1 Interface TextDecoder
|
||||
|
||||
class TextDecoder {
|
||||
/**
|
||||
* @param {string=} label The label of the encoding; defaults to 'utf-8'.
|
||||
* @param {Object=} options
|
||||
*/
|
||||
constructor(label = DEFAULT_ENCODING, options = {}) {
|
||||
// A TextDecoder object has an associated encoding, decoder,
|
||||
// stream, ignore BOM flag (initially unset), BOM seen flag
|
||||
// (initially unset), error mode (initially replacement), and do
|
||||
// not flush flag (initially unset).
|
||||
|
||||
/** @private */
|
||||
this._encoding = null
|
||||
/** @private @type {?Decoder} */
|
||||
this._decoder = null
|
||||
/** @private @type {boolean} */
|
||||
this._ignoreBOM = false
|
||||
/** @private @type {boolean} */
|
||||
this._BOMseen = false
|
||||
/** @private @type {string} */
|
||||
this._error_mode = 'replacement'
|
||||
/** @private @type {boolean} */
|
||||
this._do_not_flush = false
|
||||
|
||||
|
||||
// 1. Let encoding be the result of getting an encoding from
|
||||
// label.
|
||||
const encoding = getEncoding(label)
|
||||
|
||||
// 2. If encoding is failure or replacement, throw a RangeError.
|
||||
if (encoding === null || encoding.name == 'replacement')
|
||||
throw RangeError('Unknown encoding: ' + label)
|
||||
if (!decoders[encoding.name]) {
|
||||
throw Error('Decoder not present.' +
|
||||
' Did you forget to include encoding-indexes.js first?')
|
||||
}
|
||||
|
||||
// 4. Set dec's encoding to encoding.
|
||||
this._encoding = encoding
|
||||
|
||||
// 5. If options's fatal member is true, set dec's error mode to
|
||||
// fatal.
|
||||
if (options['fatal'])
|
||||
this._error_mode = 'fatal'
|
||||
|
||||
// 6. If options's ignoreBOM member is true, set dec's ignore BOM
|
||||
// flag.
|
||||
if (options['ignoreBOM'])
|
||||
this._ignoreBOM = true
|
||||
}
|
||||
|
||||
get encoding() {
|
||||
return this._encoding.name.toLowerCase()
|
||||
}
|
||||
get fatal() {
|
||||
return this._error_mode === 'fatal'
|
||||
}
|
||||
get ignoreBOM() {
|
||||
return this._ignoreBOM
|
||||
}
|
||||
/**
|
||||
* @param {BufferSource=} input The buffer of bytes to decode.
|
||||
* @param {Object=} options
|
||||
* @return The decoded string.
|
||||
*/
|
||||
decode(input, options = {}) {
|
||||
let bytes
|
||||
if (typeof input === 'object' && input instanceof ArrayBuffer) {
|
||||
bytes = new Uint8Array(input)
|
||||
} else if (typeof input === 'object' && 'buffer' in input &&
|
||||
input.buffer instanceof ArrayBuffer) {
|
||||
bytes = new Uint8Array(input.buffer,
|
||||
input.byteOffset,
|
||||
input.byteLength)
|
||||
} else {
|
||||
bytes = new Uint8Array(0)
|
||||
}
|
||||
|
||||
// 1. If the do not flush flag is unset, set decoder to a new
|
||||
// encoding's decoder, set stream to a new stream, and unset the
|
||||
// BOM seen flag.
|
||||
if (!this._do_not_flush) {
|
||||
this._decoder = decoders[this._encoding.name]({
|
||||
fatal: this._error_mode === 'fatal' })
|
||||
this._BOMseen = false
|
||||
}
|
||||
|
||||
// 2. If options's stream is true, set the do not flush flag, and
|
||||
// unset the do not flush flag otherwise.
|
||||
this._do_not_flush = Boolean(options['stream'])
|
||||
|
||||
// 3. If input is given, push a copy of input to stream.
|
||||
// TODO: Align with spec algorithm - maintain stream on instance.
|
||||
const input_stream = new Stream(bytes)
|
||||
|
||||
// 4. Let output be a new stream.
|
||||
const output = []
|
||||
|
||||
/** @type {?(number|!Array.<number>)} */
|
||||
let result
|
||||
|
||||
// 5. While true:
|
||||
while (true) {
|
||||
// 1. Let token be the result of reading from stream.
|
||||
const token = input_stream.read()
|
||||
|
||||
// 2. If token is end-of-stream and the do not flush flag is
|
||||
// set, return output, serialized.
|
||||
// TODO: Align with spec algorithm.
|
||||
if (token === end_of_stream)
|
||||
break
|
||||
|
||||
// 3. Otherwise, run these subsubsteps:
|
||||
|
||||
// 1. Let result be the result of processing token for decoder,
|
||||
// stream, output, and error mode.
|
||||
result = this._decoder.handler(input_stream, token)
|
||||
|
||||
// 2. If result is finished, return output, serialized.
|
||||
if (result === finished)
|
||||
break
|
||||
|
||||
if (result !== null) {
|
||||
if (Array.isArray(result))
|
||||
output.push.apply(output, /**@type {!Array.<number>}*/(result))
|
||||
else
|
||||
output.push(result)
|
||||
}
|
||||
|
||||
// 3. Otherwise, if result is error, throw a TypeError.
|
||||
// (Thrown in handler)
|
||||
|
||||
// 4. Otherwise, do nothing.
|
||||
}
|
||||
// TODO: Align with spec algorithm.
|
||||
if (!this._do_not_flush) {
|
||||
do {
|
||||
result = this._decoder.handler(input_stream, input_stream.read())
|
||||
if (result === finished)
|
||||
break
|
||||
if (result === null)
|
||||
continue
|
||||
if (Array.isArray(result))
|
||||
output.push.apply(output, /**@type {!Array.<number>}*/(result))
|
||||
else
|
||||
output.push(result)
|
||||
} while (!input_stream.endOfStream())
|
||||
this._decoder = null
|
||||
}
|
||||
|
||||
return this.serializeStream(output)
|
||||
}
|
||||
// A TextDecoder object also has an associated serialize stream
|
||||
// algorithm...
|
||||
/**
|
||||
* @param {!Array.<number>} stream
|
||||
*/
|
||||
serializeStream(stream) {
|
||||
// 1. Let token be the result of reading from stream.
|
||||
// (Done in-place on array, rather than as a stream)
|
||||
|
||||
// 2. If encoding is UTF-8, UTF-16BE, or UTF-16LE, and ignore
|
||||
// BOM flag and BOM seen flag are unset, run these subsubsteps:
|
||||
if (['UTF-8', 'UTF-16LE', 'UTF-16BE'].includes(this._encoding.name) &&
|
||||
!this._ignoreBOM && !this._BOMseen) {
|
||||
if (stream.length > 0 && stream[0] === 0xFEFF) {
|
||||
// 1. If token is U+FEFF, set BOM seen flag.
|
||||
this._BOMseen = true
|
||||
stream.shift()
|
||||
} else if (stream.length > 0) {
|
||||
// 2. Otherwise, if token is not end-of-stream, set BOM seen
|
||||
// flag and append token to stream.
|
||||
this._BOMseen = true
|
||||
} else {
|
||||
// 3. Otherwise, if token is not end-of-stream, append token
|
||||
// to output.
|
||||
// (no-op)
|
||||
}
|
||||
}
|
||||
// 4. Otherwise, return output.
|
||||
return codePointsToString(stream)
|
||||
}
|
||||
}
|
||||
export {TextDecoder}
|
||||
83
cat/tjs/lib/ali.js
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* @Author: samples jadehh@live.com
|
||||
* @Date: 2023-12-14 11:03:04
|
||||
* @LastEditors: samples jadehh@live.com
|
||||
* @LastEditTime: 2023-12-14 11:03:04
|
||||
* @FilePath: lib/ali.js
|
||||
* @Description: 阿里云盘Spider公共
|
||||
*/
|
||||
import {
|
||||
getVod,
|
||||
initSome,
|
||||
clearFile,
|
||||
playerContent,
|
||||
playerContentByFlag,
|
||||
setShareId,
|
||||
setToken,
|
||||
getFileByShare, getTempFileId
|
||||
} from './ali_api.js';
|
||||
import {JadeLogging} from "./log.js";
|
||||
|
||||
const JadeLog = new JadeLogging("阿里云盘")
|
||||
|
||||
|
||||
async function initAli(token) {
|
||||
await initSome();
|
||||
await setToken(token);
|
||||
await getTempFileId();
|
||||
// await clearFile();
|
||||
await JadeLog.info("阿里云盘初始化完成", true)
|
||||
}
|
||||
|
||||
|
||||
function getShareId(share_url) {
|
||||
let patternAli = /https:\/\/www\.alipan\.com\/s\/([^\\/]+)(\/folder\/([^\\/]+))?|https:\/\/www\.aliyundrive\.com\/s\/([^\\/]+)(\/folder\/([^\\/]+))?/
|
||||
let matches = patternAli.exec(share_url)
|
||||
const filteredArr = matches.filter(item => item !== undefined);
|
||||
if (filteredArr.length > 1) {
|
||||
return filteredArr[1]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
async function detailContent(share_url_list, type_name = "电影") {
|
||||
try {
|
||||
let video_items = [], sub_items = []
|
||||
|
||||
for (const share_url of share_url_list) {
|
||||
let share_id = getShareId(share_url)
|
||||
let share_token = await setShareId(share_id)
|
||||
if (share_token !== undefined) {
|
||||
await getFileByShare(share_token, share_url, video_items, sub_items)
|
||||
}
|
||||
}
|
||||
if (video_items.length > 0) {
|
||||
await JadeLog.info(`获取播放链接成功,分享链接为:${share_url_list.join("\t")}`)
|
||||
} else {
|
||||
await JadeLog.error(`获取播放链接失败,检查分享链接为:${share_url_list.join("\t")}`)
|
||||
}
|
||||
return getVod(video_items, sub_items, type_name)
|
||||
} catch (e) {
|
||||
await JadeLog.error('获取阿里视频失败,失败原因为:' + e.message + ' 行数为:' + e.lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
async function playContent(flag, id, flags) {
|
||||
if (flags.length > 0) {
|
||||
await JadeLog.info(`准备播放,播放类型为:${flag},播放文件Id为:${id},播放所有类型为:${flags.join("")}`)
|
||||
} else {
|
||||
await JadeLog.info(`准备播放,播放类型为:${flag},播放文件Id为:${id},播放所有类型为:${flags.join("")}`)
|
||||
}
|
||||
let file_id_list = id.split("+")
|
||||
let share_id = file_id_list[1]
|
||||
let file_id = file_id_list[0]
|
||||
let share_token = file_id_list[2]
|
||||
return flag === "原画" ? await playerContent(file_id, share_id, share_token) : await playerContentByFlag(file_id, flag, share_id, share_token);
|
||||
}
|
||||
|
||||
export {
|
||||
initAli,
|
||||
detailContent,
|
||||
playContent
|
||||
};
|
||||
711
cat/tjs/lib/ali_api.js
Normal file
@ -0,0 +1,711 @@
|
||||
/*
|
||||
* @Author: samples jadehh@live.com
|
||||
* @Date: 2023-12-14 11:03:04
|
||||
* @LastEditors: samples jadehh@live.com
|
||||
* @LastEditTime: 2023-12-14 11:03:04
|
||||
* @FilePath: /lib/ali_api.js
|
||||
* @Description: 阿里云盘Api
|
||||
*/
|
||||
import {_, jinja2} from "./cat.js";
|
||||
import * as Utils from "./utils.js";
|
||||
import {JadeLogging} from "./log.js";
|
||||
import {
|
||||
Code, Drive, getHeader, getOAuthCache, getUserCache, Item, OAuth, post, postJson, Sub, User, CLIENT_ID
|
||||
} from "./ali_object.js";
|
||||
|
||||
let quality = {}, tempIds = [], shareToken = "", shareId = "", oauth = new OAuth(), user = new User(),
|
||||
driveInfo = new Drive(), tmpFolderName = "TV", curTmpFolderFileId = "",
|
||||
JadeLog = new JadeLogging("阿里云盘", "INFO");
|
||||
|
||||
async function initSome() {
|
||||
let user_cache_str = await getUserCache();
|
||||
user = User.objectFrom(user_cache_str);
|
||||
if (!_.isEmpty(user.getRefreshToken())) {
|
||||
await JadeLog.info("读取用户缓存成功", true);
|
||||
} else {
|
||||
await JadeLog.error("读取用户缓存失败", true);
|
||||
}
|
||||
|
||||
let oauth_cache_str = await getOAuthCache();
|
||||
oauth = OAuth.objectFrom(oauth_cache_str);
|
||||
if (!_.isEmpty(oauth.getAccessToken())) {
|
||||
await JadeLog.info("读取授权成功", true)
|
||||
} else {
|
||||
await JadeLog.error("读取授权失败", true)
|
||||
|
||||
}
|
||||
// quality = {
|
||||
// "4K": "UHD", "2k": "QHD", "超清": "FHD", "高清": "HD", "标清": "SD", "流畅": "LD"
|
||||
// };
|
||||
quality = {
|
||||
"4K": "UHD", "2k": "QHD", "超清": "QHD", "高清": "HD", "标清": "SD", "流畅": "LD"
|
||||
};
|
||||
await JadeLog.info("阿里Api初始化完成")
|
||||
}
|
||||
|
||||
async function getTempFileId() {
|
||||
curTmpFolderFileId = await createTmpFolder();
|
||||
}
|
||||
|
||||
async function clearFile() {
|
||||
try {
|
||||
await deleteTmpFolderAndRecreate();
|
||||
} catch (e) {
|
||||
await JadeLog.error("清空缓存文件失败,失败原因为:{}" + e)
|
||||
}
|
||||
await cleanRecord()
|
||||
}
|
||||
|
||||
async function cleanRecord() {
|
||||
await local.set("file", "file_id", JSON.stringify({}))
|
||||
}
|
||||
|
||||
async function setShareId(share_id) {
|
||||
getOAuthCache().length === 0 && (await oauth.clean().save());
|
||||
getUserCache().length === 0 && (await user.clean().save());
|
||||
shareId = share_id;
|
||||
return await refreshShareToken();
|
||||
}
|
||||
|
||||
function getHeaderAuth(shareToken) {
|
||||
const params = {};
|
||||
params["x-share-token"] = shareToken;
|
||||
params["X-Canary"] = "client=Android,app=adrive,version=v4.3.1";
|
||||
|
||||
if (user.isAuthed()) {
|
||||
params.authorization = user.getAuthorization();
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
function getHeaderShare() {
|
||||
const params = getHeader();
|
||||
params["x-share-token"] = shareToken;
|
||||
params["X-Canary"] = "client=Android,app=adrive,version=v4.3.1";
|
||||
return params;
|
||||
}
|
||||
|
||||
function getHeaderOpen() {
|
||||
const params = {};
|
||||
params.authorization = oauth.getAuthorization();
|
||||
return params;
|
||||
}
|
||||
|
||||
function aliExpection(data_str) {
|
||||
if (data_str.indexOf("TooManyRequests") > -1) {
|
||||
Utils.sleep(1)
|
||||
return {code: 429, content: data_str}
|
||||
} else if (data_str.indexOf("AccessTokenInvalid") > -1) {
|
||||
return {code: 400, content: data_str}
|
||||
} else if (data_str.indexOf("AccessTokenExpired") > -1) {
|
||||
return {code: 401, content: data_str}
|
||||
} else if (data_str.indexOf("BadRequest") > -1) {
|
||||
return {code: 402, content: data_str}
|
||||
} else if (data_str.indexOf("NotFound.File") > -1 || data_str.indexOf("ForbiddenFileInTheRecycleBin") > -1) {
|
||||
return {code: 403, content: data_str}
|
||||
} else if (data_str.indexOf("ForbiddenNoPermission.File") > -1) {
|
||||
return {code: 500, content: data_str}
|
||||
} else if (data_str.indexOf("InvalidParameter.ToParentFileId") > -1) {
|
||||
return {code: 501, content: data_str}
|
||||
} else if (data_str.indexOf("NotFound.ParentFileId") > -1) {
|
||||
return {code: 502, content: data_str}
|
||||
} else if (data_str.indexOf("The resource drive has exceeded the limit. File size exceeded drive capacity") > -1) {
|
||||
return {code: 503, content: data_str}
|
||||
}
|
||||
return {code: 200, content: data_str}
|
||||
}
|
||||
|
||||
async function alistManyRequest(data_str) {
|
||||
if (!(data_str.indexOf("Too Many Requests") > -1)) {
|
||||
return false;
|
||||
}
|
||||
await oauth.clean().save();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Alist Token获取
|
||||
async function alist(url_param, params) {
|
||||
let url = "https://api-cf.nn.ci/alist/ali_open/" + url_param;
|
||||
let response = await postJson(url, params, getHeader()), response_content = response.content;
|
||||
if (await alistManyRequest(response_content)) {
|
||||
await JadeLog.error(`Alist授权Token失败,失败原因为:太多请求,失败详情为:${response_content}`)
|
||||
return false;
|
||||
}
|
||||
oauth = await OAuth.objectFrom(response_content).save();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 阿里云盘用户Api
|
||||
async function auth(url, params, shareToken, retry) {
|
||||
url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url;
|
||||
let response = await postJson(url, params, getHeaderAuth(shareToken));
|
||||
await JadeLog.debug(`正在请求需要阿里登录的url:${url},参数为:${JSON.stringify(params)}`)
|
||||
response = aliExpection(response.content)
|
||||
if (retry && (response.code === 400)) {
|
||||
await JadeLog.error("登录阿里云盘失败,失败原因为:登录Token无效,准备重新授权,失败详情:" + response.content)
|
||||
await refreshAccessToken("")
|
||||
return await auth(url, params, shareToken, false);
|
||||
}
|
||||
await JadeLog.debug(`完成请求需要阿里登录的url:${url},参数为:${JSON.stringify(params)},请求结果为${response.content}`)
|
||||
return response.content;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 需要授权的Api
|
||||
async function oauthFunc(url, params, retry) {
|
||||
url = url.startsWith("https") ? url : "https://open.aliyundrive.com/adrive/v1.0/" + url;
|
||||
await JadeLog.debug(`正在请求需要阿里授权的url:${url},参数为:${JSON.stringify(params)}`)
|
||||
let open_header = getHeaderOpen();
|
||||
let response = await postJson(url, params, open_header);
|
||||
response = aliExpection(response.content)
|
||||
if (retry && (response.code === 400 || response.code === 401 || response.code === 429 || response.code === 402 || response.code === 403)) {
|
||||
if (response.code === 400) {
|
||||
await JadeLog.error("阿里授权失败,失败原因为:授权Token无效,准备重新授权,失败详情:" + response.content)
|
||||
await activateRefreshOpenToken()
|
||||
} else if (response.code === 401) {
|
||||
await JadeLog.error("阿里授权失败,失败原因为:授权Token失效,准备重新授权,失败详情:" + response.content)
|
||||
await activateRefreshOpenToken()
|
||||
} else if (response.code === 402) {
|
||||
await JadeLog.error("阿里授权失败,失败原因为:授权Token失效,准备重新授权,失败详情:" + response.content)
|
||||
return await oauthFunc(url, params, true)
|
||||
} else if (response.code === 403) {
|
||||
await JadeLog.error("阿里授权失败,失败原因为:没有找到缓存文件,失败详情:" + response.content)
|
||||
await cleanRecord()
|
||||
return "retry"
|
||||
} else if (response.code === 429) {
|
||||
await JadeLog.error(`正在请求需要阿里授权的url:${url},请求过于频繁,稍后重试,10分钟后再重试`)
|
||||
Utils.sleep(10 * 60)
|
||||
return await oauthFunc(url, params, true)
|
||||
}
|
||||
return await oauthFunc(url, params, false)
|
||||
}
|
||||
await JadeLog.debug(`完成请求需要阿里授权的url:${url},参数为:${JSON.stringify(params)},请求结果为:${JSON.stringify(response)}`)
|
||||
return response.content;
|
||||
|
||||
}
|
||||
|
||||
async function shareFunc(url, params) {
|
||||
url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url;
|
||||
let headers = getHeaderShare(), response = await postJson(url, params, headers);
|
||||
return response.content;
|
||||
}
|
||||
|
||||
//主动刷新授权Token
|
||||
|
||||
async function activateRefreshOpenToken() {
|
||||
await oauth.clean().save()
|
||||
await refreshOpenToken()
|
||||
}
|
||||
|
||||
|
||||
async function refreshShareToken() {
|
||||
try {
|
||||
let params = {};
|
||||
params.share_id = shareId;
|
||||
params.share_pwd = "";
|
||||
let response_content = await post("v2/share_link/get_share_token", params),
|
||||
response_json = JSON.parse(response_content);
|
||||
if (response_json["code"] === "ShareLink.Cancelled") {
|
||||
await JadeLog.error("分享链接被取消了")
|
||||
}
|
||||
shareToken = response_json.share_token;
|
||||
return shareToken
|
||||
} catch (e) {
|
||||
await JadeLog.error("刷新Share Token失败" + e)
|
||||
}
|
||||
}
|
||||
|
||||
//支持切换Token
|
||||
async function refreshAccessToken(token) {
|
||||
try {
|
||||
if (_.isEmpty(user.getAccessToken()) || user.getRefreshToken() !== token) {
|
||||
let refresh_token_params = {};
|
||||
refresh_token_params.refresh_token = user.getRefreshToken();
|
||||
refresh_token_params.grant_type = "refresh_token";
|
||||
await JadeLog.info(`准备登录阿里云盘,登录Token为:${user.getRefreshToken()}`)
|
||||
let response_conetent = await post("https://auth.aliyundrive.com/v2/account/token", refresh_token_params);
|
||||
if (response_conetent.indexOf("InvalidParameter.RefreshToken") > 1 || _.isEmpty(response_conetent)) {
|
||||
if (_.isEmpty(response_conetent)) {
|
||||
await JadeLog.error(`登录阿里云盘失败,登录Token为:${user.getRefreshToken()},失败原因为:检查Token是否正确`)
|
||||
} else {
|
||||
await JadeLog.error(`登录阿里云盘失败,登录Token为:${user.getRefreshToken()},失败原因为:检查Token是否正确,返回结果为:${response_conetent}`)
|
||||
}
|
||||
} else {
|
||||
await JadeLog.info(`登录阿里云盘成功,登录Token为:${user.getRefreshToken()}`)
|
||||
user = await User.objectFrom(response_conetent).save();
|
||||
}
|
||||
} else {
|
||||
await JadeLog.info(`阿里云盘已登录,无需重复登录,登录Token为:${user.getRefreshToken()}`)
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
await JadeLog.error(`登录阿里云盘失败,登录Token为:${user.getRefreshToken()},失败原因为:${e}`)
|
||||
await user.clean().save();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async function oauthRequest() {
|
||||
try {
|
||||
let params = {};
|
||||
params.authorize = 1;
|
||||
params.scope = "user:base,file:all:read,file:all:write";
|
||||
let url = "https://open.aliyundrive.com/oauth/users/authorize?client_id=" + CLIENT_ID + "&redirect_uri=https://alist.nn.ci/tool/aliyundrive/callback&scope=user:base,file:all:read,file:all:write&state="
|
||||
await JadeLog.debug(`正在请求获取阿里授权码的url:${url},参数为:${params}`)
|
||||
let response_str = await auth(url, params, shareToken, true);
|
||||
await JadeLog.debug(`完成请求获取阿里授权码的url:${url},参数为:${params},返回值为:${response_str}`)
|
||||
if (_.isEmpty(response_str) || response_str.indexOf("AccessTokenInvalid") > -1) {
|
||||
if (_.isEmpty(response_str)) {
|
||||
await JadeLog.error(`请求获取阿里授权码失败,失败原因为:还未登录`)
|
||||
} else {
|
||||
await JadeLog.error(`请求获取阿里授权码失败,失败原因为:还未登录,失败详情为:${response_str}`)
|
||||
}
|
||||
} else {
|
||||
await JadeLog.info(`请求获取阿里授权码成功,返回值为:${response_str}`)
|
||||
return await oauthRedirect(Code.objectFrom(response_str).getCode());
|
||||
}
|
||||
} catch (e) {
|
||||
await JadeLog.error(`请求获取阿里授权失败,失败原因为:${e}`)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function oauthRedirect(code) {
|
||||
try {
|
||||
let params = {};
|
||||
params.code = code;
|
||||
params.grant_type = "authorization_code";
|
||||
return await alist("code", params);
|
||||
} catch (e) {
|
||||
// // console.debug(_0x114c46);
|
||||
await oauth.clean().save();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshOpenToken() {
|
||||
try {
|
||||
// 刷新 Refresh Token
|
||||
if (_.isEmpty(oauth.getRefreshToken())) {
|
||||
return await oauthRequest();
|
||||
}
|
||||
// 刷新Access Token
|
||||
if (_.isEmpty(oauth.getAccessToken())) {
|
||||
let params = {};
|
||||
params.grant_type = "refresh_token";
|
||||
params.refresh_token = oauth.getRefreshToken();
|
||||
return await alist("token", params);
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
await JadeLog.error("刷新授权Token失败,失败原因为:" + e);
|
||||
await oauth.clean().save();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getFileByShare(share_token, share_url, video_item_list, sub_item_list) {
|
||||
await JadeLog.info(`正在获取播放链接,分享链接为:${share_url}`)
|
||||
let params = {};
|
||||
params.share_id = shareId;
|
||||
let file_id = share_url.split("folder/").slice(-1)[0]
|
||||
if (file_id.length !== 40) {
|
||||
file_id = ""
|
||||
}
|
||||
let response_str = await post("adrive/v3/share_link/get_share_by_anonymous", params),
|
||||
response_json = JSON.parse(response_str), item_file_id = getParentFileId(file_id, response_json),
|
||||
item = new Item(item_file_id);
|
||||
await listFiles(item, video_item_list, sub_item_list, share_token);
|
||||
}
|
||||
|
||||
async function getVod(video_item_list, sub_item_list, type_name) {
|
||||
let play_foramt_list = ["原画", "超清", "高清", "标清"], episode_list = [], episode_str_list = [];
|
||||
for (const video_item of video_item_list) {
|
||||
episode_list.push(video_item.getDisplayName(type_name) + "$" + video_item.getFileId() + "+" + video_item.shareId + "+" + video_item.shareToken + findSubs(video_item.getName(), sub_item_list));
|
||||
}
|
||||
for (let index = 0; index < play_foramt_list.length; index++) {
|
||||
episode_str_list.push(episode_list.join("#"));
|
||||
}
|
||||
return {
|
||||
vod_play_url: episode_str_list.join("$$$"), vod_play_from: play_foramt_list.map(item => item).join("$$$"),
|
||||
};
|
||||
}
|
||||
|
||||
async function listFiles(item, video_item_list, sub_item_list, share_token) {
|
||||
return await listFilesMarker(item, video_item_list, sub_item_list, "", share_token);
|
||||
}
|
||||
|
||||
async function listFilesMarker(item, video_item_list, sub_item_list, netxt_markers, share_token) {
|
||||
let new_item = {}, file_list = [];
|
||||
new_item.limit = 200;
|
||||
new_item.share_id = shareId;
|
||||
new_item.share_token = share_token
|
||||
new_item.parent_file_id = item.getFileId();
|
||||
new_item.order_by = "name";
|
||||
new_item.order_direction = "ASC";
|
||||
if (netxt_markers.length > 0) {
|
||||
new_item.marker = netxt_markers;
|
||||
}
|
||||
let items = Item.objectFrom(await shareFunc("adrive/v2/file/list_by_share", new_item), shareToken);
|
||||
for (const r_item of items.getItems()) {
|
||||
if (r_item.getType() === "folder") {
|
||||
file_list.push(r_item);
|
||||
} else {
|
||||
if ((r_item.getCategory() === "video" || r_item.getCategory() === "audio")) {
|
||||
//判断数组中是否有file_id
|
||||
//
|
||||
let is_video_file_exists = false
|
||||
for (const video_item of video_item_list) {
|
||||
if (r_item.getFileId() === video_item.getFileId()) {
|
||||
is_video_file_exists = true
|
||||
await JadeLog.debug('视频分享文件重复,无需添加')
|
||||
}
|
||||
}
|
||||
if (!is_video_file_exists) {
|
||||
if (r_item.getCategory() === "video" && r_item.size / 1000000 > 10) {
|
||||
video_item_list.push(r_item.parentFunc(item.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (Utils.isSub(r_item.getExt())) {
|
||||
let is_sub_file_exists = false
|
||||
for (const sub_item of sub_item_list) {
|
||||
if (r_item.getFileId() === sub_item.getFileId()) {
|
||||
is_sub_file_exists = true
|
||||
await JadeLog.debug('字幕分享文件重复,无需添加')
|
||||
}
|
||||
}
|
||||
if (!is_sub_file_exists) {
|
||||
sub_item_list.push(r_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
items.getNextMarker().length > 0 && (await listFilesMarker(item, video_item_list, sub_item_list, items.getNextMarker()));
|
||||
for (const file of file_list) {
|
||||
await listFiles(file, video_item_list, sub_item_list);
|
||||
}
|
||||
}
|
||||
|
||||
function getParentFileId(file_id, items) {
|
||||
let file_infos = items.file_infos;
|
||||
|
||||
if (!_.isEmpty(file_id)) {
|
||||
return file_id;
|
||||
}
|
||||
|
||||
if (file_infos.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let item = file_infos[0];
|
||||
|
||||
if (item.type === "folder") {
|
||||
return item.file_id;
|
||||
}
|
||||
if (item.type === "file" && item.category === "video") {
|
||||
return "root";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//字幕匹配
|
||||
function pair(name, item_list, sub_item_list) {
|
||||
for (const item of item_list) {
|
||||
const sub_name = Utils.removeExt(item.getName()).toLowerCase();
|
||||
if (name.indexOf(sub_name) > -1 || sub_name.indexOf(name) > -1) {
|
||||
sub_item_list.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//找出所有字幕
|
||||
function findSubs(name, item_list) {
|
||||
let sub_item_list = [];
|
||||
pair(Utils.removeExt(name).toLowerCase(), item_list, sub_item_list);
|
||||
if (sub_item_list.length === 0) {
|
||||
for (const item of item_list) {
|
||||
sub_item_list.push(item);
|
||||
}
|
||||
}
|
||||
let sub_str = "";
|
||||
for (const item of sub_item_list) {
|
||||
sub_str += "+" + Utils.removeExt(item.getName()) + "@@@" + item.getExt() + "@@@" + item.getFileId();
|
||||
}
|
||||
return sub_str;
|
||||
}
|
||||
|
||||
async function getSubs(sub_list, share_id) {
|
||||
let sub_url_list = [];
|
||||
for (const sub_str of sub_list) {
|
||||
if (!(sub_str.indexOf("@@@") > -1)) {
|
||||
continue;
|
||||
}
|
||||
let sub_split_list = sub_str.split("@@@"), sub_name = sub_split_list[0], sub_ext = sub_split_list[1],
|
||||
sub_file_id = sub_split_list[2], sub_url = await getDownloadUrl(sub_file_id, share_id);
|
||||
|
||||
sub_url_list.push(Sub.create().setName(sub_name).setExt(sub_ext).setUrl(sub_url));
|
||||
}
|
||||
return sub_url_list;
|
||||
}
|
||||
|
||||
async function getDriveInfo() {
|
||||
if (!_.isEmpty(driveInfo) && !_.isEmpty(driveInfo.default_drive_id)) {
|
||||
return driveInfo;
|
||||
}
|
||||
|
||||
let _0x3740f3 = await oauthFunc("user/getDriveInfo", {}, true), _0x56fde5 = JSON.parse(_0x3740f3);
|
||||
|
||||
driveInfo = {
|
||||
default_drive_id: _0x56fde5.default_drive_id,
|
||||
resource_drive_id: _0x56fde5.resource_drive_id,
|
||||
backup_drive_id: _0x56fde5.backup_drive_id
|
||||
};
|
||||
return driveInfo;
|
||||
}
|
||||
|
||||
async function getDriveId() {
|
||||
if (_.isEmpty(user.getDriveId())) {
|
||||
let drive = await getDriveInfo();
|
||||
return drive.resource_drive_id;
|
||||
}
|
||||
return user.getDriveId();
|
||||
}
|
||||
|
||||
async function getDownloadUrl(file_id, share_id, share_token) {
|
||||
let drive_id = await getDriveId();
|
||||
tempIds.unshift(await copy(file_id, share_id, share_token));
|
||||
let params = {};
|
||||
params.file_id = tempIds[0];
|
||||
params.drive_id = drive_id;
|
||||
if (tempIds[0] !== null) {
|
||||
let response_str = await oauthFunc("openFile/getDownloadUrl", params, true);
|
||||
if (response_str === "retry") {
|
||||
await JadeLog.info("尝试重新获取下载链接");
|
||||
return await getDownloadUrl(file_id, share_id)
|
||||
} else {
|
||||
await JadeLog.info("获取下载链接成功:返回结果为:" + response_str + "请求参数为:" + JSON.stringify(params));
|
||||
return JSON.parse(response_str).url;
|
||||
}
|
||||
} else {
|
||||
await JadeLog.error("获取下载链接失败:失败原因:请检查转存文件失败原因")
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function getVideoPreviewPlayInfo(file_id, share_id, shareToken) {
|
||||
let drive_id = await getDriveId();
|
||||
tempIds.unshift(await copy(file_id, share_id, shareToken));
|
||||
let params = {};
|
||||
params.file_id = tempIds[0];
|
||||
params.drive_id = drive_id;
|
||||
params.category = "live_transcoding";
|
||||
params.url_expire_sec = "14400";
|
||||
let response_str = await oauthFunc("openFile/getVideoPreviewPlayInfo", params, true);
|
||||
return JSON.parse(response_str).video_preview_play_info;
|
||||
}
|
||||
|
||||
async function playerContent(file_id, share_id, share_token) {
|
||||
try {
|
||||
await JadeLog.info("正在获取原画的播放地址和字幕下载链接", true)
|
||||
let download_url = await getDownloadUrl(file_id, share_id, share_token);
|
||||
// let sub_list = await getSubs(file_id_list,share_id);
|
||||
await JadeLog.info("获取原画的播放地址和字幕下载链接成功", true)
|
||||
await JadeLog.info(`下载地址为:${download_url}`)
|
||||
return JSON.stringify({
|
||||
parse: 0, url: download_url, header: getHeader(), format: "application/octet-stream", subs: []
|
||||
});
|
||||
} catch (e) {
|
||||
await JadeLog.error("获取原画的播放地址和字幕下载链接失败:失败原因为:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
// 转码头的Url和字幕
|
||||
async function playerContentByFlag(file_id, flag, share_id, shareToken) {
|
||||
try {
|
||||
await JadeLog.info("正在获取转码后的播放地址和字幕下载链接", true)
|
||||
let video_preview_play_info = await getVideoPreviewPlayInfo(file_id, share_id, shareToken),
|
||||
video_preview_url = getPreviewUrl(video_preview_play_info, flag);
|
||||
// let sub_list = await getSubs(file_id_list,share_id),
|
||||
// sub_p_list = getSubsByPlayInfo(video_preview_play_info);
|
||||
|
||||
// for (const sub_p of sub_p_list) {
|
||||
// sub_list.push(sub_p);
|
||||
// }
|
||||
|
||||
await JadeLog.info("获取转码后的播放地址和字幕下载链接成功", true)
|
||||
await JadeLog.info(`下载地址为:${video_preview_url}`)
|
||||
return JSON.stringify({
|
||||
parse: 0, url: video_preview_url, header: getHeader(), format: "application/x-mpegURL", subs: []
|
||||
});
|
||||
} catch (e) {
|
||||
await JadeLog.error(`获取转码后的播放地址和字幕下载链接失败,失败原因为:${e}`)
|
||||
}
|
||||
}
|
||||
|
||||
function getPreviewUrl(video_preview_play_info, flag) {
|
||||
if (!video_preview_play_info.hasOwnProperty("live_transcoding_task_list")) {
|
||||
return "";
|
||||
}
|
||||
let live_transcoding_task_list = video_preview_play_info.live_transcoding_task_list;
|
||||
for (let index = 0; index < live_transcoding_task_list.length; ++index) {
|
||||
let live_transcoding_task = live_transcoding_task_list[index];
|
||||
|
||||
if (live_transcoding_task.template_id === quality[flag]) {
|
||||
return live_transcoding_task.url;
|
||||
}
|
||||
}
|
||||
return live_transcoding_task_list[0].url;
|
||||
}
|
||||
|
||||
function getSubsByPlayInfo(video_preview_play_info) {
|
||||
if (!video_preview_play_info.hasOwnProperty("live_transcoding_subtitle_task_list")) {
|
||||
return [];
|
||||
}
|
||||
let live_transcoding_subtitle_task_list = video_preview_play_info.live_transcoding_subtitle_task_list,
|
||||
sub_p_list = [];
|
||||
for (let index = 0; index < live_transcoding_subtitle_task_list.length; ++index) {
|
||||
let live_transcoding_subtitle_task = live_transcoding_subtitle_task_list[index],
|
||||
language = live_transcoding_subtitle_task.language, url = live_transcoding_subtitle_task.url;
|
||||
sub_p_list.push(Sub.create().setUrl(url).setName(language).setLang(language).setExt("vtt"));
|
||||
}
|
||||
return sub_p_list;
|
||||
}
|
||||
|
||||
async function copy(file_id, shareId, shareToken) {
|
||||
let copy_file_id
|
||||
let cache_dic = {}
|
||||
try {
|
||||
cache_dic = JSON.parse(await local.get("file", "file_id"))
|
||||
} catch (e) {
|
||||
}
|
||||
copy_file_id = cache_dic[file_id]
|
||||
if (typeof (copy_file_id) == "string") {
|
||||
await JadeLog.info(`file id为:${file_id},已经缓存过,copy file id为:${copy_file_id}`)
|
||||
} else {
|
||||
let params_str = "{\"requests\":[{\"body\":{\"file_id\":\"{{data.fileId}}\",\"share_id\":\"{{data.shareId}}\",\"auto_rename\":true,\"to_parent_file_id\":\"{{data.tmpFolderFileId}}\",\"to_drive_id\":\"{{data.driveId}}\"},\"headers\":{\"Content-Type\":\"application/json\"},\"id\":\"0\",\"method\":\"POST\",\"url\":\"/file/copy\"}],\"resource\":\"file\"}",
|
||||
drive_id = await getDriveId(), params = {
|
||||
fileId: file_id, shareId: shareId, driveId: drive_id, tmpFolderFileId: curTmpFolderFileId
|
||||
};
|
||||
params_str = jinja2(params_str, {
|
||||
data: params
|
||||
});
|
||||
await JadeLog.debug(`正在转存文件,文件id为:${file_id}`, true)
|
||||
let response_str = await auth("adrive/v2/batch", JSON.parse(params_str), shareToken, true);
|
||||
let response = aliExpection(response_str)
|
||||
if (response.code === 500 || response.code === 501 || response.code === 502 || response.code === 503 || response.code === 403) {
|
||||
if (response.code === 500) {
|
||||
await JadeLog.error("转存文件失败,失败详情:" + response.content)
|
||||
return copy(file_id);
|
||||
} else if (response.code === 501) {
|
||||
await JadeLog.error("转存文件失败,失败详情:" + response.content)
|
||||
return copy(file_id)
|
||||
} else if (response.code === 502) {
|
||||
await JadeLog.error("转存文件失败,失败原因为:转存文件夹不存在,失败详情:" + response.content)
|
||||
return null;
|
||||
} else if (response.code === 503) {
|
||||
await JadeLog.error("转存文件失败,失败原因为:转存文件夹大小被限制" + response.content)
|
||||
await clearFile()
|
||||
return copy(file_id)
|
||||
} else if (response.code === 403) {
|
||||
await JadeLog.error("转存文件失败,失败原因为:没有找到File Id,失败详情:" + response.content)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
await JadeLog.debug(`转存文件成功,文件id为:${file_id},请求结果为:${response_str}`)
|
||||
copy_file_id = JSON.parse(response_str).responses[0].body.file_id;
|
||||
let file_dic = {}
|
||||
try {
|
||||
JSON.parse(await local.get("file", "file_id"))
|
||||
} catch (e) {
|
||||
}
|
||||
file_dic[file_id] = copy_file_id
|
||||
await local.set("file", "file_id", JSON.stringify(file_dic))
|
||||
}
|
||||
return copy_file_id;
|
||||
}
|
||||
|
||||
async function deleteTmpFolderAndRecreate() {
|
||||
// 删除缓存文件夹
|
||||
let file_id = await tmpFolderExistsFunc();
|
||||
file_id && (await trashFile(file_id), await recyclebinClear());
|
||||
await getTempFileId();
|
||||
}
|
||||
|
||||
//放入回车站
|
||||
async function trashFile(file_id) {
|
||||
let params_str = "{\"requests\":[{\"body\":{\"file_id\":\"{{data.fileId}}\",\"drive_id\":\"{{data.driveId}}\"},\"headers\":{\"Content-Type\":\"application/json\"},\"id\":\"0\",\"method\":\"POST\",\"url\":\"/recyclebin/trash\"}],\"resource\":\"file\"}",
|
||||
drive_id = await getDriveId(), params = {
|
||||
fileId: file_id, driveId: drive_id
|
||||
};
|
||||
params_str = jinja2(params_str, {
|
||||
data: params
|
||||
});
|
||||
await JadeLog.debug(`正在准备删除文件,文件id为:${file_id}`, true)
|
||||
let response = await auth("v2/batch", JSON.parse(params_str), shareToken, true);
|
||||
await JadeLog.debug(`删除文件成功,文件id为:${file_id},请求结果为:${response}`)
|
||||
return true;
|
||||
}
|
||||
|
||||
//清空回车站
|
||||
async function recyclebinClear() {
|
||||
let drive_id = await getDriveId(), params = {
|
||||
drive_id: drive_id
|
||||
};
|
||||
await auth("v2/recyclebin/clear", params, shareToken, true);
|
||||
await JadeLog.info("清空回车站成功", true)
|
||||
return true;
|
||||
}
|
||||
|
||||
async function createTmpFolder() {
|
||||
//创建文件夹
|
||||
let file_id = await tmpFolderExistsFunc();
|
||||
if (file_id) {
|
||||
await JadeLog.info("文件夹存在,无需重新创建")
|
||||
return file_id;
|
||||
}
|
||||
await JadeLog.debug("文件夹不存在,重新创建文件夹")
|
||||
let drive_id = await getDriveId(), params = {
|
||||
check_name_mode: "refuse", drive_id: drive_id, name: tmpFolderName, parent_file_id: "root", type: "folder"
|
||||
}, response_str = await oauthFunc("openFile/create", params, true);
|
||||
let response_json = JSON.parse(response_str);
|
||||
if (_.isEmpty(response_json.drive_id)) {
|
||||
await JadeLog.error(`创建文件夹失败,失败原因为:${response_str}`)
|
||||
return null;
|
||||
}
|
||||
await JadeLog.info(`创建文件夹成功`, true)
|
||||
return response_json.file_id;
|
||||
}
|
||||
|
||||
async function tmpFolderExistsFunc() {
|
||||
let drive_id = await getDriveId(), params = {
|
||||
drive_id: drive_id, parent_file_id: "root", limit: 100, order_by: "updated_at", order_direction: "DESC"
|
||||
}, response_str = await oauthFunc("openFile/list", params, true);
|
||||
let response_json = JSON.parse(response_str);
|
||||
if (_.isEmpty(response_json.items)) {
|
||||
return false;
|
||||
}
|
||||
for (const item of response_json.items) {
|
||||
if (item.name === tmpFolderName) {
|
||||
return item.file_id;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
async function setToken(token) {
|
||||
// Token设置
|
||||
user.setRefreshToken(token);
|
||||
await refreshAccessToken(token);
|
||||
await refreshOpenToken();
|
||||
}
|
||||
|
||||
export {
|
||||
initSome, setToken, clearFile, setShareId, getFileByShare, getVod, playerContent, playerContentByFlag, getTempFileId
|
||||
};
|
||||
510
cat/tjs/lib/ali_object.js
Normal file
@ -0,0 +1,510 @@
|
||||
/*
|
||||
* @Author: samples jadehh@live.com
|
||||
* @Date: 2023-12-14 11:03:04
|
||||
* @LastEditors: samples jadehh@live.com
|
||||
* @LastEditTime: 2023-12-14 11:03:04
|
||||
* @FilePath: /lib/ali_object.js
|
||||
* @Description: 阿里云盘基础类
|
||||
*/
|
||||
import {_} from "./cat.js";
|
||||
|
||||
const UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"
|
||||
const CLIENT_ID = "76917ccccd4441c39457a04f6084fb2f";
|
||||
import * as Utils from "./utils.js";
|
||||
// 引用会导致出错
|
||||
// import qs from "qs";
|
||||
// import axios from "axios";
|
||||
// import https from "https";
|
||||
|
||||
function getHeader() {
|
||||
const params = {};
|
||||
params["User-Agent"] = UA;
|
||||
params.Referer = "https://www.aliyundrive.com/";
|
||||
return params;
|
||||
}
|
||||
|
||||
class User {
|
||||
constructor() {
|
||||
this.driveId = "";
|
||||
this.userId = "";
|
||||
this.tokenType = "";
|
||||
this.accessToken = "";
|
||||
this.refreshToken = "";
|
||||
}
|
||||
|
||||
static objectFrom(json_str) {
|
||||
if (_.isEmpty(json_str)) {
|
||||
return new User();
|
||||
}
|
||||
|
||||
let resonse = JSON.parse(json_str), user = new User();
|
||||
user.driveId = resonse.default_drive_id;
|
||||
user.userId = resonse.user_id;
|
||||
user.tokenType = resonse.token_type;
|
||||
user.accessToken = resonse.access_token;
|
||||
user.refreshToken = resonse.refresh_token; // 刷新Token记录原有的Token
|
||||
return user;
|
||||
}
|
||||
|
||||
getDriveId() {
|
||||
return _.isEmpty(this.driveId) ? "" : this.driveId;
|
||||
}
|
||||
|
||||
getUserId() {
|
||||
return _.isEmpty(this.userId) ? "" : this.userId;
|
||||
}
|
||||
|
||||
getTokenType() {
|
||||
return _.isEmpty(this.tokenType) ? "" : this.tokenType;
|
||||
}
|
||||
|
||||
getAccessToken() {
|
||||
return _.isEmpty(this.accessToken) ? "" : this.accessToken;
|
||||
}
|
||||
|
||||
getRefreshToken() {
|
||||
return _.isEmpty(this.refreshToken) ? "" : this.refreshToken;
|
||||
}
|
||||
|
||||
setRefreshToken(refresh_token) {
|
||||
this.refreshToken = refresh_token
|
||||
}
|
||||
|
||||
getAuthorization() {
|
||||
return this.getTokenType() + " " + this.getAccessToken();
|
||||
}
|
||||
|
||||
isAuthed() {
|
||||
return this.getTokenType().length > 0 && this.getAccessToken().length > 0;
|
||||
}
|
||||
|
||||
clean() {
|
||||
this.refreshToken = "";
|
||||
this.accessToken = "";
|
||||
return this;
|
||||
}
|
||||
|
||||
async save() {
|
||||
await local.set("ali", "aliyundrive_user", this.toString());
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return JSON.stringify(this.toDict());
|
||||
}
|
||||
|
||||
toDict() {
|
||||
return {
|
||||
default_drive_id: this.getDriveId(),
|
||||
user_id: this.getUserId(),
|
||||
token_type: this.getTokenType(),
|
||||
access_token: this.getAccessToken(),
|
||||
refresh_token: this.getRefreshToken()
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OAuth {
|
||||
constructor() {
|
||||
this.tokenType = "";
|
||||
this.accessToken = "";
|
||||
this.refreshToken = "";
|
||||
}
|
||||
|
||||
static objectFrom(json_str) {
|
||||
if (_.isEmpty(json_str)) {
|
||||
return new OAuth();
|
||||
}
|
||||
let oauth_json = JSON.parse(json_str), oAuth = new OAuth();
|
||||
oAuth.tokenType = oauth_json.token_type;
|
||||
oAuth.accessToken = oauth_json.access_token;
|
||||
oAuth.refreshToken = oauth_json.refresh_token;
|
||||
return oAuth;
|
||||
}
|
||||
|
||||
getTokenType() {
|
||||
return _.isEmpty(this.tokenType) ? "" : this.tokenType;
|
||||
}
|
||||
|
||||
getAccessToken() {
|
||||
return _.isEmpty(this.accessToken) ? "" : this.accessToken;
|
||||
}
|
||||
|
||||
getRefreshToken() {
|
||||
return _.isEmpty(this.refreshToken) ? "" : this.refreshToken;
|
||||
}
|
||||
|
||||
getAuthorization() {
|
||||
return this.getTokenType() + " " + this.getAccessToken();
|
||||
}
|
||||
|
||||
clean() {
|
||||
this.refreshToken = "";
|
||||
this.accessToken = "";
|
||||
return this;
|
||||
}
|
||||
|
||||
async save() {
|
||||
await local.set("ali", "aliyundrive_oauth", this.toString());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return JSON.stringify(this.toDict());
|
||||
}
|
||||
|
||||
toDict() {
|
||||
return {
|
||||
token_type: this.getTokenType(), access_token: this.getAccessToken(), refresh_token: this.getRefreshToken()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Drive {
|
||||
constructor() {
|
||||
this.defaultDriveId = "";
|
||||
this.resourceDriveId = "";
|
||||
this.backupDriveId = "";
|
||||
}
|
||||
|
||||
static objectFrom(json_str) {
|
||||
if (_.isEmpty(json_str)) {
|
||||
return new Drive();
|
||||
}
|
||||
let obj = JSON.parse(json_str), drive = new Drive();
|
||||
drive.defaultDriveId = obj.default_drive_id;
|
||||
drive.resourceDriveId = obj.resource_drive_id;
|
||||
drive.backupDriveId = obj.backup_drive_id;
|
||||
return drive;
|
||||
}
|
||||
|
||||
getDefaultDriveId() {
|
||||
return _.isEmpty(this.defaultDriveId) ? "" : this.defaultDriveId;
|
||||
}
|
||||
|
||||
getResourceDriveId() {
|
||||
return _.isEmpty(this.resourceDriveId) ? "" : this.resourceDriveId;
|
||||
}
|
||||
|
||||
getBackupDriveId() {
|
||||
return _.isEmpty(this.backupDriveId) ? "" : this.backupDriveId;
|
||||
}
|
||||
|
||||
clean() {
|
||||
this.defaultDriveId = "";
|
||||
this.backupDriveId = "";
|
||||
this.resourceDriveId = "";
|
||||
return this;
|
||||
}
|
||||
|
||||
async save() {
|
||||
await local.set("ali", "aliyundrive_drive", this.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
toString() {
|
||||
const params = {
|
||||
default_drive_id: this.getDefaultDriveId(),
|
||||
resource_drive_id: this.getResourceDriveId(),
|
||||
backup_drive_id: this.getBackupDriveId()
|
||||
};
|
||||
return JSON.stringify(params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Code {
|
||||
constructor() {
|
||||
this.redirectUri = "";
|
||||
}
|
||||
|
||||
static objectFrom(json_str) {
|
||||
if (_.isEmpty(json_str)) {
|
||||
return new Code();
|
||||
}
|
||||
let code_json = JSON.parse(json_str), code = new Code();
|
||||
code.redirectUri = code_json.redirectUri;
|
||||
return code;
|
||||
}
|
||||
|
||||
getRedirectUri() {
|
||||
return _.isEmpty(this.redirectUri) ? "" : this.redirectUri;
|
||||
}
|
||||
|
||||
getCode() {
|
||||
return this.getRedirectUri().split("code=")[1];
|
||||
}
|
||||
}
|
||||
|
||||
class Item {
|
||||
constructor(file_id) {
|
||||
this.items = [];
|
||||
this.nextMarker = "";
|
||||
this.fileId = file_id;
|
||||
this.shareId = "";
|
||||
this.name = "";
|
||||
this.type = "";
|
||||
this.fileExtension = "";
|
||||
this.category = "";
|
||||
this.size = "";
|
||||
this.parent = "";
|
||||
this.shareToken = ""
|
||||
}
|
||||
|
||||
static objectFrom(json_str, shareToken) {
|
||||
if (_.isEmpty(json_str)) {
|
||||
return new Item();
|
||||
}
|
||||
|
||||
let item_json = JSON.parse(json_str), item = new Item();
|
||||
|
||||
item.nextMarker = typeof item_json.next_marker == "undefined" ? "" : item_json.next_marker;
|
||||
item.fileId = typeof item_json.file_id == "undefined" ? "" : item_json.file_id;
|
||||
item.shareId = typeof item_json.share_id == "undefined" ? "" : item_json.share_id;
|
||||
item.shareToken = shareToken
|
||||
item.name = typeof item_json.name == "undefined" ? "" : item_json.name;
|
||||
item.type = typeof item_json.type == "undefined" ? "" : item_json.type;
|
||||
item.fileExtension = typeof item_json.file_extension == "undefined" ? "" : item_json.file_extension;
|
||||
item.category = typeof item_json.category == "undefined" ? "" : item_json.category;
|
||||
item.size = typeof item_json.size == "undefined" ? "" : item_json.size;
|
||||
item.parent = typeof item_json.parent_file_id == "undefined" ? "" : item_json.parent_file_id;
|
||||
typeof item.items != "undefined" && Array.isArray(item_json.items) && !_.isEmpty(item_json.items) && item_json.items.forEach(function (x) {
|
||||
let new_item = Item.objectFrom(JSON.stringify((x)), shareToken)
|
||||
item.items.push(new_item);
|
||||
});
|
||||
return item;
|
||||
}
|
||||
|
||||
getItems() {
|
||||
return _.isEmpty(this.items) ? [] : this.items;
|
||||
}
|
||||
|
||||
getNextMarker() {
|
||||
return _.isEmpty(this.nextMarker) ? "" : this.nextMarker;
|
||||
}
|
||||
|
||||
getFileId() {
|
||||
return _.isEmpty(this.fileId) ? "" : this.fileId;
|
||||
}
|
||||
|
||||
getShareId() {
|
||||
return _.isEmpty(this.shareId) ? "" : this.shareId;
|
||||
}
|
||||
|
||||
getFileExtension() {
|
||||
return _.isEmpty(this.fileExtension) ? "" : this.fileExtension;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return _.isEmpty(this.name) ? "" : this.name;
|
||||
}
|
||||
|
||||
getType() {
|
||||
return _.isEmpty(this.type) ? "" : this.type;
|
||||
}
|
||||
|
||||
getExt() {
|
||||
return _.isEmpty(this.fileExtension) ? "" : this.fileExtension;
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return _.isEmpty(this.category) ? "" : this.category;
|
||||
}
|
||||
|
||||
getSize() {
|
||||
return this.size === 0 ? "" : "[" + Utils.getSize(this.size) + "]";
|
||||
}
|
||||
|
||||
getParent() {
|
||||
return _.isEmpty(this.parent) ? "" : "[" + this.parent + "]";
|
||||
}
|
||||
|
||||
parentFunc(item) {
|
||||
this.parent = item;
|
||||
return this;
|
||||
}
|
||||
|
||||
// getDisplayName() {
|
||||
// return this.getParent() + " " + this.getName() + " " + this.getSize();
|
||||
// }
|
||||
|
||||
getDisplayName(type_name) {
|
||||
let name = this.getName();
|
||||
name = name.replaceAll("玩偶哥 q 频道:【神秘的哥哥们】", "")
|
||||
if (type_name === "电视剧") {
|
||||
let replaceNameList = ["4k", "4K"]
|
||||
name = name.replaceAll("." + this.getFileExtension(), "")
|
||||
name = name.replaceAll(" ", "").replaceAll(" ", "")
|
||||
for (const replaceName of replaceNameList) {
|
||||
name = name.replaceAll(replaceName, "")
|
||||
}
|
||||
name = Utils.getStrByRegexDefault(/\.S01E(.*?)\./, name)
|
||||
const numbers = name.match(/\d+/g);
|
||||
if (numbers.length > 0) {
|
||||
name = numbers[0]
|
||||
}
|
||||
}
|
||||
return name + " " + this.getParent() + " " + this.getSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Sub {
|
||||
constructor() {
|
||||
this.url = "";
|
||||
this.name = "";
|
||||
this.lang = "";
|
||||
this.format = "";
|
||||
}
|
||||
|
||||
static create() {
|
||||
return new Sub();
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
setUrl(url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
setLang(lang) {
|
||||
this.lang = lang;
|
||||
return this;
|
||||
}
|
||||
|
||||
setFormat(format) {
|
||||
this.format = format;
|
||||
return this;
|
||||
}
|
||||
|
||||
setExt(ext) {
|
||||
switch (ext) {
|
||||
case "vtt":
|
||||
return this.setFormat("text/vtt");
|
||||
case "ass":
|
||||
case "ssa":
|
||||
return this.setFormat("text/x-ssa");
|
||||
default:
|
||||
return this.setFormat("application/x-subrip");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function getUserCache() {
|
||||
return await local.get("ali", "aliyundrive_user");
|
||||
}
|
||||
|
||||
async function getOAuthCache() {
|
||||
return await local.get("ali", "aliyundrive_oauth");
|
||||
}
|
||||
|
||||
function getShareId(share_url) {
|
||||
let patternAli = /https:\/\/www\.alipan\.com\/s\/([^\\/]+)(\/folder\/([^\\/]+))?|https:\/\/www\.aliyundrive\.com\/s\/([^\\/]+)(\/folder\/([^\\/]+))?/
|
||||
let matches = patternAli.exec(share_url)
|
||||
const filteredArr = matches.filter(item => item !== undefined);
|
||||
if (filteredArr.length > 1) {
|
||||
return matches[1]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
async function ali_request(url, opt) {
|
||||
|
||||
let resp;
|
||||
let data;
|
||||
try {
|
||||
data = opt ? opt.data || null : null;
|
||||
const postType = opt ? opt.postType || null : null;
|
||||
const returnBuffer = opt ? opt.buffer || 0 : 0;
|
||||
const timeout = opt ? opt.timeout || 5000 : 5000;
|
||||
|
||||
const headers = opt ? opt.headers || {} : {};
|
||||
if (postType === 'form') {
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
|
||||
if (data != null) {
|
||||
data = qs.stringify(data, {encode: false});
|
||||
}
|
||||
}
|
||||
let respType = returnBuffer === 1 || returnBuffer === 2 ? 'arraybuffer' : undefined;
|
||||
resp = await axios(url, {
|
||||
responseType: respType,
|
||||
method: opt ? opt.method || 'get' : 'get',
|
||||
headers: headers,
|
||||
data: data,
|
||||
timeout: timeout,
|
||||
httpsAgent: https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
}),
|
||||
});
|
||||
data = resp.data;
|
||||
|
||||
const resHeader = {};
|
||||
for (const hks of resp.headers) {
|
||||
const v = hks[1];
|
||||
resHeader[hks[0]] = Array.isArray(v) ? (v.length === 1 ? v[0] : v) : v;
|
||||
}
|
||||
|
||||
if (!returnBuffer) {
|
||||
if (typeof data === 'object') {
|
||||
data = JSON.stringify(data);
|
||||
}
|
||||
} else if (returnBuffer === 1) {
|
||||
return {code: resp.status, headers: resHeader, content: data};
|
||||
} else if (returnBuffer === 2) {
|
||||
return {code: resp.status, headers: resHeader, content: data.toString('base64')};
|
||||
}
|
||||
return {code: resp.status, headers: resHeader, content: data};
|
||||
} catch (error) {
|
||||
// await Utils.log(`请求失败,URL为:${url},失败原因为:${error}`)
|
||||
resp = error.response
|
||||
try {
|
||||
return {code: resp.status, headers: resp.headers, content: JSON.stringify(resp.data)};
|
||||
} catch (err) {
|
||||
return {headers: {}, content: ''};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function post(url, params) {
|
||||
url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url;
|
||||
let response = await postJson(url, params, getHeader());
|
||||
return response.content;
|
||||
}
|
||||
|
||||
async function postJson(url, params, headers) {
|
||||
params["Content-Type"] = "application/json";
|
||||
return await req(url, {
|
||||
headers: headers, method: "post", data: params
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
UA,
|
||||
CLIENT_ID,
|
||||
OAuth,
|
||||
Code,
|
||||
Sub,
|
||||
User,
|
||||
Item,
|
||||
Drive,
|
||||
getHeader,
|
||||
getShareId,
|
||||
getOAuthCache,
|
||||
getUserCache,
|
||||
post,
|
||||
postJson
|
||||
};
|
||||
154
cat/tjs/lib/big5.js
Normal file
@ -0,0 +1,154 @@
|
||||
import { inRange, decoderError, encoderError, isASCIICodePoint,
|
||||
end_of_stream, finished, isASCIIByte, floor } from './text_decoder_utils.js'
|
||||
import index, { indexBig5PointerFor, indexCodePointFor } from './text_decoder_indexes.js'
|
||||
|
||||
//
|
||||
// 12. Legacy multi-byte Chinese (traditional) encodings
|
||||
//
|
||||
|
||||
// 12.1 Big5
|
||||
|
||||
// 12.1.1 Big5 decoder
|
||||
/**
|
||||
* @implements {Decoder}
|
||||
*/
|
||||
export class Big5Decoder {
|
||||
constructor(options) {
|
||||
const { fatal } = options
|
||||
this.fatal = fatal
|
||||
// Big5's decoder has an associated Big5 lead (initially 0x00).
|
||||
this.Big5_lead = 0x00
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// 1. If byte is end-of-stream and Big5 lead is not 0x00, set
|
||||
// Big5 lead to 0x00 and return error.
|
||||
if (bite === end_of_stream && this.Big5_lead !== 0x00) {
|
||||
this.Big5_lead = 0x00
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 2. If byte is end-of-stream and Big5 lead is 0x00, return
|
||||
// finished.
|
||||
if (bite === end_of_stream && this.Big5_lead === 0x00)
|
||||
return finished
|
||||
|
||||
// 3. If Big5 lead is not 0x00, let lead be Big5 lead, let
|
||||
// pointer be null, set Big5 lead to 0x00, and then run these
|
||||
// substeps:
|
||||
if (this.Big5_lead !== 0x00) {
|
||||
const lead = this.Big5_lead
|
||||
let pointer = null
|
||||
this.Big5_lead = 0x00
|
||||
|
||||
// 1. Let offset be 0x40 if byte is less than 0x7F and 0x62
|
||||
// otherwise.
|
||||
const offset = bite < 0x7F ? 0x40 : 0x62
|
||||
|
||||
// 2. If byte is in the range 0x40 to 0x7E, inclusive, or 0xA1
|
||||
// to 0xFE, inclusive, set pointer to (lead − 0x81) × 157 +
|
||||
// (byte − offset).
|
||||
if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE))
|
||||
pointer = (lead - 0x81) * 157 + (bite - offset)
|
||||
|
||||
// 3. If there is a row in the table below whose first column
|
||||
// is pointer, return the two code points listed in its second
|
||||
// column
|
||||
// Pointer | Code points
|
||||
// --------+--------------
|
||||
// 1133 | U+00CA U+0304
|
||||
// 1135 | U+00CA U+030C
|
||||
// 1164 | U+00EA U+0304
|
||||
// 1166 | U+00EA U+030C
|
||||
switch (pointer) {
|
||||
case 1133: return [0x00CA, 0x0304]
|
||||
case 1135: return [0x00CA, 0x030C]
|
||||
case 1164: return [0x00EA, 0x0304]
|
||||
case 1166: return [0x00EA, 0x030C]
|
||||
}
|
||||
|
||||
// 4. Let code point be null if pointer is null and the index
|
||||
// code point for pointer in index Big5 otherwise.
|
||||
const code_point = (pointer === null) ? null :
|
||||
indexCodePointFor(pointer, index('big5'))
|
||||
|
||||
// 5. If code point is null and byte is an ASCII byte, prepend
|
||||
// byte to stream.
|
||||
if (code_point === null && isASCIIByte(bite))
|
||||
stream.prepend(bite)
|
||||
|
||||
// 6. If code point is null, return error.
|
||||
if (code_point === null)
|
||||
return decoderError(this.fatal)
|
||||
|
||||
// 7. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
|
||||
// 4. If byte is an ASCII byte, return a code point whose value
|
||||
// is byte.
|
||||
if (isASCIIByte(bite))
|
||||
return bite
|
||||
|
||||
// 5. If byte is in the range 0x81 to 0xFE, inclusive, set Big5
|
||||
// lead to byte and return continue.
|
||||
if (inRange(bite, 0x81, 0xFE)) {
|
||||
this.Big5_lead = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 6. Return error.
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 12.1.2 Big5 encoder
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class Big5Encoder {
|
||||
constructor() {
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
*/
|
||||
this.handler = function(stream, code_point) {
|
||||
// 1. If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is an ASCII code point, return a byte whose
|
||||
// value is code point.
|
||||
if (isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 3. Let pointer be the index Big5 pointer for code point.
|
||||
const pointer = indexBig5PointerFor(code_point)
|
||||
|
||||
// 4. If pointer is null, return error with code point.
|
||||
if (pointer === null)
|
||||
return encoderError(code_point)
|
||||
|
||||
// 5. Let lead be floor(pointer / 157) + 0x81.
|
||||
const lead = floor(pointer / 157) + 0x81
|
||||
|
||||
// 6. If lead is less than 0xA1, return error with code point.
|
||||
if (lead < 0xA1)
|
||||
return encoderError(code_point)
|
||||
|
||||
// 7. Let trail be pointer % 157.
|
||||
const trail = pointer % 157
|
||||
|
||||
// 8. Let offset be 0x40 if trail is less than 0x3F and 0x62
|
||||
// otherwise.
|
||||
const offset = trail < 0x3F ? 0x40 : 0x62
|
||||
|
||||
// Return two bytes whose values are lead and trail + offset.
|
||||
return [lead, trail + offset]
|
||||
}
|
||||
}
|
||||
}
|
||||
31
cat/tjs/lib/bilibili_ASS_Danmaku_Downloader.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* @File : bilibili_ASS_Danmaku_Downloader.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/14 13:19
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
function parseXML(json) {
|
||||
let list = [];
|
||||
/**
|
||||
* <d p="{time},{type},{size},{color},{timestamp},{pool},{uid_crc32},{row_id}">
|
||||
*
|
||||
* {Text}
|
||||
* time为弹幕在视频里的时间 -->
|
||||
* type为弹幕类型 -->
|
||||
* size为字体大小 -->
|
||||
* color为十进制的RGB颜色(16进制转10进制) -->
|
||||
* timestamp为弹幕发送时间戳(unix时间戳) -->
|
||||
* pool为弹幕池 -->
|
||||
* uid_crc32为发送者uid的crc32 -->
|
||||
*/
|
||||
Array.from(json.danmuku).forEach(x => {
|
||||
let start = Number(x[0]);
|
||||
let content = x[4];
|
||||
list.push(`<d p="${start},1,25,16777215,1659282294,0,8b53b65c,1108899274487246080"><![CDATA[${content}]]></d>`)
|
||||
});
|
||||
return String.raw`<?xml version="1.0" encoding="UTF-8"?><i><chatserver>chat.bilibili.com</chatserver><chatid>52175602</chatid><mission>0</mission><maxlimit>1000</maxlimit><state>0</state><real_name>0</real_name><source>k-v</source>` + list.join('') + "</i>"
|
||||
}
|
||||
export {parseXML}
|
||||
|
||||
76
cat/tjs/lib/book.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* @File : book.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/30 17:01
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
export class BookShort {
|
||||
|
||||
constructor() {
|
||||
this.book_id = "" //id
|
||||
this.book_name = "" //名称
|
||||
this.book_pic = "" //图片
|
||||
this.book_remarks = "" //备注
|
||||
}
|
||||
|
||||
to_dict() {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
|
||||
load_dic(json_str) {
|
||||
let obj = JSON.parse(json_str)
|
||||
for (let propName in obj) {
|
||||
this[propName] = obj[propName];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class BookDetail extends BookShort {
|
||||
/**
|
||||
* let book = {
|
||||
* book_name: $('[property$=book_name]')[0].attribs.content,
|
||||
* book_year: $('[property$=update_time]')[0].attribs.content,
|
||||
* book_director: $('[property$=author]')[0].attribs.content,
|
||||
* book_content: $('[property$=description]')[0].attribs.content,
|
||||
* };
|
||||
* $ = await this.getHtml(this.siteUrl + id + `list.html`);
|
||||
* let urls = [];
|
||||
* const links = $('dl>dd>a[href*="/html/"]');
|
||||
* for (const l of links) {
|
||||
* const name = $(l).text().trim();
|
||||
* const link = l.attribs.href;
|
||||
* urls.push(name + '$' + link);
|
||||
* }
|
||||
* book.volumes = '全卷';
|
||||
* book.urls = urls.join('#');
|
||||
* return book
|
||||
* */
|
||||
constructor() {
|
||||
super();
|
||||
this.book_year = ""
|
||||
this.book_director = ""
|
||||
this.book_content = ""
|
||||
this.volumes = ""
|
||||
this.urls = ""
|
||||
}
|
||||
|
||||
to_short() {
|
||||
let bookShort = new BookShort()
|
||||
bookShort.load_dic(this.to_dict())
|
||||
return bookShort.to_dict()
|
||||
}
|
||||
|
||||
load_dic(json_str) {
|
||||
let obj = JSON.parse(json_str)
|
||||
for (let propName in obj) {
|
||||
this[propName] = obj[propName];
|
||||
console.log(propName);//打印👉属性名-->name age gender address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
15158
cat/tjs/lib/cat.js
Normal file
181
cat/tjs/lib/danmuSpider.js
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* @File : danmuSpider.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/13 13:39
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
|
||||
import {_, load, Uri} from "./cat.js";
|
||||
import * as Utils from "./utils.js";
|
||||
import {JadeLogging} from "./log.js";
|
||||
import {VodDetail, VodShort} from "./vod.js";
|
||||
import {parseXML} from "./bilibili_ASS_Danmaku_Downloader.js";
|
||||
|
||||
class DanmuSpider {
|
||||
constructor() {
|
||||
this.siteUrl = "https://search.youku.com"
|
||||
this.reconnectTimes = 0
|
||||
this.maxReconnectTimes = 5
|
||||
this.jadeLog = new JadeLogging(this.getAppName(), "DEBUG")
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "弹幕"
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
return {"User-Agent": Utils.CHROME, "Referer": this.siteUrl + "/"};
|
||||
}
|
||||
|
||||
async reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer) {
|
||||
await this.jadeLog.error("请求失败,请检查url:" + reqUrl + ",两秒后重试")
|
||||
Utils.sleep(2)
|
||||
if (this.reconnectTimes < this.maxReconnectTimes) {
|
||||
this.reconnectTimes = this.reconnectTimes + 1
|
||||
return await this.fetch(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||||
} else {
|
||||
await this.jadeLog.error("请求失败,重连失败")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async getResponse(reqUrl, params, headers, redirect_url, return_cookie, buffer, response) {
|
||||
{
|
||||
if (response.headers["location"] !== undefined) {
|
||||
if (redirect_url) {
|
||||
await this.jadeLog.debug(`返回重定向连接:${response.headers["location"]}`)
|
||||
return response.headers["location"]
|
||||
} else {
|
||||
return this.fetch(response.headers["location"], params, headers, redirect_url, return_cookie, buffer)
|
||||
}
|
||||
} else if (response.content.length > 0) {
|
||||
this.reconnectTimes = 0
|
||||
if (return_cookie) {
|
||||
return {"cookie": response.headers["set-cookie"], "content": response.content}
|
||||
} else {
|
||||
return response.content
|
||||
}
|
||||
} else if (buffer === 1) {
|
||||
this.reconnectTimes = 0
|
||||
return response.content
|
||||
} else {
|
||||
await this.jadeLog.error(`请求失败,请求url为:${reqUrl},回复内容为:${JSON.stringify(response)}`)
|
||||
return await this.reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async fetch(reqUrl, params, headers, redirect_url = false, return_cookie = false, buffer = 0) {
|
||||
let data = Utils.objectToStr(params)
|
||||
let url = reqUrl
|
||||
if (!_.isEmpty(data)) {
|
||||
url = reqUrl + "?" + data
|
||||
}
|
||||
let uri = new Uri(url);
|
||||
let response;
|
||||
response = await req(uri.toString(), {method: "get", headers: headers, buffer: buffer, data: null})
|
||||
if (response.code === 200 || response.code === 302 || response.code === 301 || return_cookie) {
|
||||
return await this.getResponse(reqUrl, params, headers, redirect_url, return_cookie, buffer, response)
|
||||
} else {
|
||||
await this.jadeLog.error(`请求失败,失败原因为:状态码出错,请求url为:${uri},回复内容为:${JSON.stringify(response)}`)
|
||||
return await this.reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
async getHtml(url = this.siteUrl, headers = this.getHeader()) {
|
||||
let html = await this.fetch(url, null, headers)
|
||||
if (!_.isEmpty(html)) {
|
||||
return load(html)
|
||||
} else {
|
||||
await this.jadeLog.error(`html获取失败`, true)
|
||||
}
|
||||
}
|
||||
|
||||
async parseVodShortListFromJson(obj, vodDetail) {
|
||||
for (const componentObj of obj["pageComponentList"]) {
|
||||
if (componentObj["commonData"] !== undefined) {
|
||||
let searchVodDetail = new VodDetail()
|
||||
let commonData = componentObj["commonData"]
|
||||
searchVodDetail.type_name = commonData["feature"]
|
||||
if (commonData["notice"] !== undefined) {
|
||||
searchVodDetail.vod_actor = commonData["notice"].replaceAll("演员:", "").replaceAll(" ", "")
|
||||
}
|
||||
if (commonData["director"] !== undefined) {
|
||||
searchVodDetail.vod_director = commonData["director"].replaceAll("导演:", "").replaceAll(" ", "")
|
||||
}
|
||||
if (vodDetail.type_name === "电影") {
|
||||
searchVodDetail.vod_id = commonData["leftButtonDTO"]["action"]["value"]
|
||||
} else {
|
||||
searchVodDetail.vod_id = commonData["showId"]
|
||||
}
|
||||
searchVodDetail.vod_name = commonData["titleDTO"]["displayName"]
|
||||
if ( searchVodDetail.vod_name === vodDetail.vod_name || searchVodDetail.type_name.indexOf(vodDetail.vod_year) > -1 || searchVodDetail.type_name.indexOf(vodDetail.type_name) > -1 || searchVodDetail.vod_director === vodDetail.vod_director) {
|
||||
await this.jadeLog.debug(`匹配视频网站成功,名称为:${searchVodDetail.vod_name},类型为:${searchVodDetail.type_name},导演为:${searchVodDetail.vod_director}`, true)
|
||||
return searchVodDetail
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.jadeLog.warning("没有匹配到弹幕网站")
|
||||
return null
|
||||
}
|
||||
|
||||
async parseVodUrlFromJsonByEpisodeId(obj, episodeId) {
|
||||
for (const serises of obj["serisesList"]) {
|
||||
if (Utils.isNumeric(episodeId["episodeId"])) {
|
||||
if (parseInt(episodeId["episodeId"]).toString() === serises["displayName"]) {
|
||||
return serises["action"]["value"]
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.jadeLog.error("没有找到匹配的集数")
|
||||
return ""
|
||||
}
|
||||
|
||||
async downloadDanmu(url) {
|
||||
let json = JSON.parse(await this.fetch(url, null, this.getHeader()))
|
||||
let xml = parseXML(json)
|
||||
let params = {"do": "set", "key": "danmu", "value": xml}
|
||||
await req("http://127.0.0.1:9978/cache", {method: "post", data: params, postType: "form-data"});
|
||||
return "http://127.0.0.1:9978/cache?do=get&key=danmu"
|
||||
}
|
||||
|
||||
async search(vodDetail, episodeId) {
|
||||
let params = {"pg": "1", "keyword": vodDetail.vod_name}
|
||||
let searchObj = JSON.parse(await this.fetch(this.siteUrl + "/api/search", params, this.getHeader()))
|
||||
let searchDetail = await this.parseVodShortListFromJson(searchObj, vodDetail)
|
||||
if (!_.isEmpty(searchDetail)){
|
||||
return await this.getVideoUrl(searchDetail.vod_id, episodeId)
|
||||
}else{
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
async getVideoUrl(showId, episodeId) {
|
||||
let url = "";
|
||||
if (!_.isEmpty(showId)) {
|
||||
if (showId.startsWith("http")) {
|
||||
url = showId
|
||||
} else {
|
||||
let params = {"appScene": "show_episode", "showIds": showId}
|
||||
let matchObj = JSON.parse(await this.fetch(this.siteUrl + "/api/search", params, this.getHeader()))
|
||||
url = await this.parseVodUrlFromJsonByEpisodeId(matchObj, episodeId)
|
||||
}
|
||||
if (!_.isEmpty(url)) {
|
||||
await this.jadeLog.debug(`弹幕视频播放连接为:${url}`)
|
||||
return await this.downloadDanmu("https://dmku.thefilehosting.com/?ac=dm&url=" + url)
|
||||
}
|
||||
}
|
||||
return url
|
||||
|
||||
}
|
||||
|
||||
|
||||
async getDammu(voddetail, episodeId) {
|
||||
return await this.search(voddetail, episodeId)
|
||||
}
|
||||
}
|
||||
|
||||
export {DanmuSpider}
|
||||
37
cat/tjs/lib/encoding-indexes.js
Normal file
460
cat/tjs/lib/encodings.js
Normal file
@ -0,0 +1,460 @@
|
||||
/**
|
||||
* Encodings table: https://encoding.spec.whatwg.org/encodings.json
|
||||
*/
|
||||
const encodings = [
|
||||
{
|
||||
encodings: [
|
||||
{
|
||||
labels: [
|
||||
"unicode-1-1-utf-8",
|
||||
"utf-8",
|
||||
"utf8",
|
||||
],
|
||||
name: "UTF-8",
|
||||
},
|
||||
],
|
||||
heading: "The Encoding",
|
||||
},
|
||||
{
|
||||
encodings: [
|
||||
{
|
||||
labels: [
|
||||
"866",
|
||||
"cp866",
|
||||
"csibm866",
|
||||
"ibm866",
|
||||
],
|
||||
name: "IBM866",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csisolatin2",
|
||||
"iso-8859-2",
|
||||
"iso-ir-101",
|
||||
"iso8859-2",
|
||||
"iso88592",
|
||||
"iso_8859-2",
|
||||
"iso_8859-2:1987",
|
||||
"l2",
|
||||
"latin2",
|
||||
],
|
||||
name: "ISO-8859-2",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csisolatin3",
|
||||
"iso-8859-3",
|
||||
"iso-ir-109",
|
||||
"iso8859-3",
|
||||
"iso88593",
|
||||
"iso_8859-3",
|
||||
"iso_8859-3:1988",
|
||||
"l3",
|
||||
"latin3",
|
||||
],
|
||||
name: "ISO-8859-3",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csisolatin4",
|
||||
"iso-8859-4",
|
||||
"iso-ir-110",
|
||||
"iso8859-4",
|
||||
"iso88594",
|
||||
"iso_8859-4",
|
||||
"iso_8859-4:1988",
|
||||
"l4",
|
||||
"latin4",
|
||||
],
|
||||
name: "ISO-8859-4",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csisolatincyrillic",
|
||||
"cyrillic",
|
||||
"iso-8859-5",
|
||||
"iso-ir-144",
|
||||
"iso8859-5",
|
||||
"iso88595",
|
||||
"iso_8859-5",
|
||||
"iso_8859-5:1988",
|
||||
],
|
||||
name: "ISO-8859-5",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"arabic",
|
||||
"asmo-708",
|
||||
"csiso88596e",
|
||||
"csiso88596i",
|
||||
"csisolatinarabic",
|
||||
"ecma-114",
|
||||
"iso-8859-6",
|
||||
"iso-8859-6-e",
|
||||
"iso-8859-6-i",
|
||||
"iso-ir-127",
|
||||
"iso8859-6",
|
||||
"iso88596",
|
||||
"iso_8859-6",
|
||||
"iso_8859-6:1987",
|
||||
],
|
||||
name: "ISO-8859-6",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csisolatingreek",
|
||||
"ecma-118",
|
||||
"elot_928",
|
||||
"greek",
|
||||
"greek8",
|
||||
"iso-8859-7",
|
||||
"iso-ir-126",
|
||||
"iso8859-7",
|
||||
"iso88597",
|
||||
"iso_8859-7",
|
||||
"iso_8859-7:1987",
|
||||
"sun_eu_greek",
|
||||
],
|
||||
name: "ISO-8859-7",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csiso88598e",
|
||||
"csisolatinhebrew",
|
||||
"hebrew",
|
||||
"iso-8859-8",
|
||||
"iso-8859-8-e",
|
||||
"iso-ir-138",
|
||||
"iso8859-8",
|
||||
"iso88598",
|
||||
"iso_8859-8",
|
||||
"iso_8859-8:1988",
|
||||
"visual",
|
||||
],
|
||||
name: "ISO-8859-8",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csiso88598i",
|
||||
"iso-8859-8-i",
|
||||
"logical",
|
||||
],
|
||||
name: "ISO-8859-8-I",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csisolatin6",
|
||||
"iso-8859-10",
|
||||
"iso-ir-157",
|
||||
"iso8859-10",
|
||||
"iso885910",
|
||||
"l6",
|
||||
"latin6",
|
||||
],
|
||||
name: "ISO-8859-10",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"iso-8859-13",
|
||||
"iso8859-13",
|
||||
"iso885913",
|
||||
],
|
||||
name: "ISO-8859-13",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"iso-8859-14",
|
||||
"iso8859-14",
|
||||
"iso885914",
|
||||
],
|
||||
name: "ISO-8859-14",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csisolatin9",
|
||||
"iso-8859-15",
|
||||
"iso8859-15",
|
||||
"iso885915",
|
||||
"iso_8859-15",
|
||||
"l9",
|
||||
],
|
||||
name: "ISO-8859-15",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"iso-8859-16",
|
||||
],
|
||||
name: "ISO-8859-16",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cskoi8r",
|
||||
"koi",
|
||||
"koi8",
|
||||
"koi8-r",
|
||||
"koi8_r",
|
||||
],
|
||||
name: "KOI8-R",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"koi8-ru",
|
||||
"koi8-u",
|
||||
],
|
||||
name: "KOI8-U",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csmacintosh",
|
||||
"mac",
|
||||
"macintosh",
|
||||
"x-mac-roman",
|
||||
],
|
||||
name: "macintosh",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"dos-874",
|
||||
"iso-8859-11",
|
||||
"iso8859-11",
|
||||
"iso885911",
|
||||
"tis-620",
|
||||
"windows-874",
|
||||
],
|
||||
name: "windows-874",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cp1250",
|
||||
"windows-1250",
|
||||
"x-cp1250",
|
||||
],
|
||||
name: "windows-1250",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cp1251",
|
||||
"windows-1251",
|
||||
"x-cp1251",
|
||||
],
|
||||
name: "windows-1251",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"ansi_x3.4-1968",
|
||||
"ascii",
|
||||
"cp1252",
|
||||
"cp819",
|
||||
"csisolatin1",
|
||||
"ibm819",
|
||||
"iso-8859-1",
|
||||
"iso-ir-100",
|
||||
"iso8859-1",
|
||||
"iso88591",
|
||||
"iso_8859-1",
|
||||
"iso_8859-1:1987",
|
||||
"l1",
|
||||
"latin1",
|
||||
"us-ascii",
|
||||
"windows-1252",
|
||||
"x-cp1252",
|
||||
],
|
||||
name: "windows-1252",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cp1253",
|
||||
"windows-1253",
|
||||
"x-cp1253",
|
||||
],
|
||||
name: "windows-1253",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cp1254",
|
||||
"csisolatin5",
|
||||
"iso-8859-9",
|
||||
"iso-ir-148",
|
||||
"iso8859-9",
|
||||
"iso88599",
|
||||
"iso_8859-9",
|
||||
"iso_8859-9:1989",
|
||||
"l5",
|
||||
"latin5",
|
||||
"windows-1254",
|
||||
"x-cp1254",
|
||||
],
|
||||
name: "windows-1254",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cp1255",
|
||||
"windows-1255",
|
||||
"x-cp1255",
|
||||
],
|
||||
name: "windows-1255",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cp1256",
|
||||
"windows-1256",
|
||||
"x-cp1256",
|
||||
],
|
||||
name: "windows-1256",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cp1257",
|
||||
"windows-1257",
|
||||
"x-cp1257",
|
||||
],
|
||||
name: "windows-1257",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"cp1258",
|
||||
"windows-1258",
|
||||
"x-cp1258",
|
||||
],
|
||||
name: "windows-1258",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"x-mac-cyrillic",
|
||||
"x-mac-ukrainian",
|
||||
],
|
||||
name: "x-mac-cyrillic",
|
||||
},
|
||||
],
|
||||
heading: "Legacy single-byte encodings",
|
||||
},
|
||||
{
|
||||
encodings: [
|
||||
{
|
||||
labels: [
|
||||
"chinese",
|
||||
"csgb2312",
|
||||
"csiso58gb231280",
|
||||
"gb2312",
|
||||
"gb_2312",
|
||||
"gb_2312-80",
|
||||
"gbk",
|
||||
"iso-ir-58",
|
||||
"x-gbk",
|
||||
],
|
||||
name: "GBK",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"gb18030",
|
||||
],
|
||||
name: "gb18030",
|
||||
},
|
||||
],
|
||||
heading: "Legacy multi-byte Chinese (simplified) encodings",
|
||||
},
|
||||
{
|
||||
encodings: [
|
||||
{
|
||||
labels: [
|
||||
"big5",
|
||||
"big5-hkscs",
|
||||
"cn-big5",
|
||||
"csbig5",
|
||||
"x-x-big5",
|
||||
],
|
||||
name: "Big5",
|
||||
},
|
||||
],
|
||||
heading: "Legacy multi-byte Chinese (traditional) encodings",
|
||||
},
|
||||
{
|
||||
encodings: [
|
||||
{
|
||||
labels: [
|
||||
"cseucpkdfmtjapanese",
|
||||
"euc-jp",
|
||||
"x-euc-jp",
|
||||
],
|
||||
name: "EUC-JP",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csiso2022jp",
|
||||
"iso-2022-jp",
|
||||
],
|
||||
name: "ISO-2022-JP",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"csshiftjis",
|
||||
"ms932",
|
||||
"ms_kanji",
|
||||
"shift-jis",
|
||||
"shift_jis",
|
||||
"sjis",
|
||||
"windows-31j",
|
||||
"x-sjis",
|
||||
],
|
||||
name: "Shift_JIS",
|
||||
},
|
||||
],
|
||||
heading: "Legacy multi-byte Japanese encodings",
|
||||
},
|
||||
{
|
||||
encodings: [
|
||||
{
|
||||
labels: [
|
||||
"cseuckr",
|
||||
"csksc56011987",
|
||||
"euc-kr",
|
||||
"iso-ir-149",
|
||||
"korean",
|
||||
"ks_c_5601-1987",
|
||||
"ks_c_5601-1989",
|
||||
"ksc5601",
|
||||
"ksc_5601",
|
||||
"windows-949",
|
||||
],
|
||||
name: "EUC-KR",
|
||||
},
|
||||
],
|
||||
heading: "Legacy multi-byte Korean encodings",
|
||||
},
|
||||
{
|
||||
encodings: [
|
||||
{
|
||||
labels: [
|
||||
"csiso2022kr",
|
||||
"hz-gb-2312",
|
||||
"iso-2022-cn",
|
||||
"iso-2022-cn-ext",
|
||||
"iso-2022-kr",
|
||||
],
|
||||
name: "replacement",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"utf-16be",
|
||||
],
|
||||
name: "UTF-16BE",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"utf-16",
|
||||
"utf-16le",
|
||||
],
|
||||
name: "UTF-16LE",
|
||||
},
|
||||
{
|
||||
labels: [
|
||||
"x-user-defined",
|
||||
],
|
||||
name: "x-user-defined",
|
||||
},
|
||||
],
|
||||
heading: "Legacy miscellaneous encodings",
|
||||
},
|
||||
]
|
||||
|
||||
export default encodings
|
||||
166
cat/tjs/lib/euc-jp.js
Normal file
@ -0,0 +1,166 @@
|
||||
import { inRange, decoderError, encoderError, isASCIICodePoint,
|
||||
end_of_stream, finished, isASCIIByte, floor } from './text_decoder_utils.js'
|
||||
import index, { indexCodePointFor, indexPointerFor } from './text_decoder_indexes.js'
|
||||
|
||||
//
|
||||
// 13. Legacy multi-byte Japanese encodings
|
||||
//
|
||||
|
||||
// 13.1 euc-jp
|
||||
|
||||
// 13.1.1 euc-jp decoder
|
||||
/**
|
||||
* @implements {Decoder}
|
||||
*/
|
||||
export class EUCJPDecoder {
|
||||
constructor(options) {
|
||||
const { fatal } = options
|
||||
this.fatal = fatal
|
||||
|
||||
// euc-jp's decoder has an associated euc-jp jis0212 flag
|
||||
// (initially unset) and euc-jp lead (initially 0x00).
|
||||
this.eucjp_jis0212_flag = false
|
||||
this.eucjp_lead = 0x00
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// 1. If byte is end-of-stream and euc-jp lead is not 0x00, set
|
||||
// euc-jp lead to 0x00, and return error.
|
||||
if (bite === end_of_stream && this.eucjp_lead !== 0x00) {
|
||||
this.eucjp_lead = 0x00
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 2. If byte is end-of-stream and euc-jp lead is 0x00, return
|
||||
// finished.
|
||||
if (bite === end_of_stream && this.eucjp_lead === 0x00)
|
||||
return finished
|
||||
|
||||
// 3. If euc-jp lead is 0x8E and byte is in the range 0xA1 to
|
||||
// 0xDF, inclusive, set euc-jp lead to 0x00 and return a code
|
||||
// point whose value is 0xFF61 − 0xA1 + byte.
|
||||
if (this.eucjp_lead === 0x8E && inRange(bite, 0xA1, 0xDF)) {
|
||||
this.eucjp_lead = 0x00
|
||||
return 0xFF61 - 0xA1 + bite
|
||||
}
|
||||
|
||||
// 4. If euc-jp lead is 0x8F and byte is in the range 0xA1 to
|
||||
// 0xFE, inclusive, set the euc-jp jis0212 flag, set euc-jp lead
|
||||
// to byte, and return continue.
|
||||
if (this.eucjp_lead === 0x8F && inRange(bite, 0xA1, 0xFE)) {
|
||||
this.eucjp_jis0212_flag = true
|
||||
this.eucjp_lead = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 5. If euc-jp lead is not 0x00, let lead be euc-jp lead, set
|
||||
// euc-jp lead to 0x00, and run these substeps:
|
||||
if (this.eucjp_lead !== 0x00) {
|
||||
const lead = this.eucjp_lead
|
||||
this.eucjp_lead = 0x00
|
||||
|
||||
// 1. Let code point be null.
|
||||
let code_point = null
|
||||
|
||||
// 2. If lead and byte are both in the range 0xA1 to 0xFE,
|
||||
// inclusive, set code point to the index code point for (lead
|
||||
// − 0xA1) × 94 + byte − 0xA1 in index jis0208 if the euc-jp
|
||||
// jis0212 flag is unset and in index jis0212 otherwise.
|
||||
if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) {
|
||||
code_point = indexCodePointFor(
|
||||
(lead - 0xA1) * 94 + (bite - 0xA1),
|
||||
index(!this.eucjp_jis0212_flag ? 'jis0208' : 'jis0212'))
|
||||
}
|
||||
|
||||
// 3. Unset the euc-jp jis0212 flag.
|
||||
this.eucjp_jis0212_flag = false
|
||||
|
||||
// 4. If byte is not in the range 0xA1 to 0xFE, inclusive,
|
||||
// prepend byte to stream.
|
||||
if (!inRange(bite, 0xA1, 0xFE))
|
||||
stream.prepend(bite)
|
||||
|
||||
// 5. If code point is null, return error.
|
||||
if (code_point === null)
|
||||
return decoderError(this.fatal)
|
||||
|
||||
// 6. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
|
||||
// 6. If byte is an ASCII byte, return a code point whose value
|
||||
// is byte.
|
||||
if (isASCIIByte(bite))
|
||||
return bite
|
||||
|
||||
// 7. If byte is 0x8E, 0x8F, or in the range 0xA1 to 0xFE,
|
||||
// inclusive, set euc-jp lead to byte and return continue.
|
||||
if (bite === 0x8E || bite === 0x8F || inRange(bite, 0xA1, 0xFE)) {
|
||||
this.eucjp_lead = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 8. Return error.
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
}
|
||||
|
||||
// 13.1.2 euc-jp encoder
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class EUCJPEncoder {
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
*/
|
||||
handler(stream, code_point) {
|
||||
// 1. If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is an ASCII code point, return a byte whose
|
||||
// value is code point.
|
||||
if (isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 3. If code point is U+00A5, return byte 0x5C.
|
||||
if (code_point === 0x00A5)
|
||||
return 0x5C
|
||||
|
||||
// 4. If code point is U+203E, return byte 0x7E.
|
||||
if (code_point === 0x203E)
|
||||
return 0x7E
|
||||
|
||||
// 5. If code point is in the range U+FF61 to U+FF9F, inclusive,
|
||||
// return two bytes whose values are 0x8E and code point −
|
||||
// 0xFF61 + 0xA1.
|
||||
if (inRange(code_point, 0xFF61, 0xFF9F))
|
||||
return [0x8E, code_point - 0xFF61 + 0xA1]
|
||||
|
||||
// 6. If code point is U+2212, set it to U+FF0D.
|
||||
if (code_point === 0x2212)
|
||||
code_point = 0xFF0D
|
||||
|
||||
// 7. Let pointer be the index pointer for code point in index
|
||||
// jis0208.
|
||||
const pointer = indexPointerFor(code_point, index('jis0208'))
|
||||
|
||||
// 8. If pointer is null, return error with code point.
|
||||
if (pointer === null)
|
||||
return encoderError(code_point)
|
||||
|
||||
// 9. Let lead be floor(pointer / 94) + 0xA1.
|
||||
const lead = floor(pointer / 94) + 0xA1
|
||||
|
||||
// 10. Let trail be pointer % 94 + 0xA1.
|
||||
const trail = pointer % 94 + 0xA1
|
||||
|
||||
// 11. Return two bytes whose values are lead and trail.
|
||||
return [lead, trail]
|
||||
}
|
||||
}
|
||||
124
cat/tjs/lib/euc-kr.js
Normal file
@ -0,0 +1,124 @@
|
||||
import { inRange, decoderError, encoderError, isASCIICodePoint,
|
||||
end_of_stream, finished, isASCIIByte, floor } from './text_decoder_utils.js'
|
||||
import index, { indexCodePointFor, indexPointerFor } from './text_decoder_indexes.js'
|
||||
|
||||
//
|
||||
// 14. Legacy multi-byte Korean encodings
|
||||
//
|
||||
|
||||
// 14.1 euc-kr
|
||||
|
||||
// 14.1.1 euc-kr decoder
|
||||
/**
|
||||
* @implements {Decoder}
|
||||
*/
|
||||
export class EUCKRDecoder {
|
||||
constructor(options) {
|
||||
const { fatal } = options
|
||||
this.fatal = fatal
|
||||
// euc-kr's decoder has an associated euc-kr lead (initially 0x00).
|
||||
this.euckr_lead = 0x00
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// 1. If byte is end-of-stream and euc-kr lead is not 0x00, set
|
||||
// euc-kr lead to 0x00 and return error.
|
||||
if (bite === end_of_stream && this.euckr_lead !== 0) {
|
||||
this.euckr_lead = 0x00
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 2. If byte is end-of-stream and euc-kr lead is 0x00, return
|
||||
// finished.
|
||||
if (bite === end_of_stream && this.euckr_lead === 0)
|
||||
return finished
|
||||
|
||||
// 3. If euc-kr lead is not 0x00, let lead be euc-kr lead, let
|
||||
// pointer be null, set euc-kr lead to 0x00, and then run these
|
||||
// substeps:
|
||||
if (this.euckr_lead !== 0x00) {
|
||||
const lead = this.euckr_lead
|
||||
let pointer = null
|
||||
this.euckr_lead = 0x00
|
||||
|
||||
// 1. If byte is in the range 0x41 to 0xFE, inclusive, set
|
||||
// pointer to (lead − 0x81) × 190 + (byte − 0x41).
|
||||
if (inRange(bite, 0x41, 0xFE))
|
||||
pointer = (lead - 0x81) * 190 + (bite - 0x41)
|
||||
|
||||
// 2. Let code point be null, if pointer is null, and the
|
||||
// index code point for pointer in index euc-kr otherwise.
|
||||
const code_point = (pointer === null)
|
||||
? null : indexCodePointFor(pointer, index('euc-kr'))
|
||||
|
||||
// 3. If code point is null and byte is an ASCII byte, prepend
|
||||
// byte to stream.
|
||||
if (pointer === null && isASCIIByte(bite))
|
||||
stream.prepend(bite)
|
||||
|
||||
// 4. If code point is null, return error.
|
||||
if (code_point === null)
|
||||
return decoderError(this.fatal)
|
||||
|
||||
// 5. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
|
||||
// 4. If byte is an ASCII byte, return a code point whose value
|
||||
// is byte.
|
||||
if (isASCIIByte(bite))
|
||||
return bite
|
||||
|
||||
// 5. If byte is in the range 0x81 to 0xFE, inclusive, set
|
||||
// euc-kr lead to byte and return continue.
|
||||
if (inRange(bite, 0x81, 0xFE)) {
|
||||
this.euckr_lead = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 6. Return error.
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
}
|
||||
|
||||
// 14.1.2 euc-kr encoder
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class EUCKREncoder {
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
* @return {(number|!Array.<number>)} Byte(s) to emit.
|
||||
*/
|
||||
handler(stream, code_point) {
|
||||
// 1. If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is an ASCII code point, return a byte whose
|
||||
// value is code point.
|
||||
if (isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 3. Let pointer be the index pointer for code point in index
|
||||
// euc-kr.
|
||||
const pointer = indexPointerFor(code_point, index('euc-kr'))
|
||||
|
||||
// 4. If pointer is null, return error with code point.
|
||||
if (pointer === null)
|
||||
return encoderError(code_point)
|
||||
|
||||
// 5. Let lead be floor(pointer / 190) + 0x81.
|
||||
const lead = floor(pointer / 190) + 0x81
|
||||
|
||||
// 6. Let trail be pointer % 190 + 0x41.
|
||||
const trail = (pointer % 190) + 0x41
|
||||
|
||||
// 7. Return two bytes whose values are lead and trail.
|
||||
return [lead, trail]
|
||||
}
|
||||
}
|
||||
480
cat/tjs/lib/ffm3u8_open.js
Normal file
@ -0,0 +1,480 @@
|
||||
/*
|
||||
* @File : ffm3u8_open.js.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/5 16:06
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import { _ } from './cat.js';
|
||||
import * as HLS from './hls.js';
|
||||
|
||||
let key = 'ffm3u8';
|
||||
let url = '';
|
||||
let categories = [];
|
||||
let siteKey = '';
|
||||
let siteType = 0;
|
||||
|
||||
async function request(reqUrl, agentSp) {
|
||||
let res = await req(reqUrl, {
|
||||
method: 'get',
|
||||
});
|
||||
return JSON.parse(res.content);
|
||||
}
|
||||
|
||||
async function init(cfg) {
|
||||
siteKey = cfg.skey;
|
||||
siteType = cfg.stype;
|
||||
url = cfg.ext.url;
|
||||
categories = cfg.ext.categories;
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
const data = await request(url);
|
||||
let classes = [];
|
||||
for (const cls of data.class) {
|
||||
const n = cls.type_name.toString().trim();
|
||||
if (categories && categories.length > 0) {
|
||||
if (categories.indexOf(n) < 0) continue;
|
||||
}
|
||||
classes.push({
|
||||
type_id: cls.type_id.toString(),
|
||||
type_name: n,
|
||||
});
|
||||
}
|
||||
if (categories && categories.length > 0) {
|
||||
classes = _.sortBy(classes, (p) => {
|
||||
return categories.indexOf(p.type_name);
|
||||
});
|
||||
}
|
||||
return {
|
||||
class: classes,
|
||||
};
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return '{}';
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
let page = pg || 1;
|
||||
if (page == 0) page = 1;
|
||||
const data = await request(url + `?ac=detail&t=${tid}&pg=${page}`);
|
||||
let videos = [];
|
||||
for (const vod of data.list) {
|
||||
videos.push({
|
||||
vod_id: vod.vod_id.toString(),
|
||||
vod_name: vod.vod_name.toString(),
|
||||
vod_pic: vod.vod_pic,
|
||||
vod_remarks: vod.vod_remarks,
|
||||
});
|
||||
}
|
||||
return {
|
||||
page: parseInt(data.page),
|
||||
pagecount: data.pagecount,
|
||||
total: data.total,
|
||||
list: videos,
|
||||
};
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
const data = (await request(url + `?ac=detail&ids=${id}`)).list[0];
|
||||
let vod = {
|
||||
vod_id: data.vod_id,
|
||||
vod_name: data.vod_name,
|
||||
vod_pic: data.vod_pic,
|
||||
type_name: data.type_name,
|
||||
vod_year: data.vod_year,
|
||||
vod_area: data.vod_area,
|
||||
vod_remarks: data.vod_remarks,
|
||||
vod_actor: data.vod_actor,
|
||||
vod_director: data.vod_director,
|
||||
vod_content: data.vod_content.trim(),
|
||||
vod_play_from: data.vod_play_from,
|
||||
vod_play_url: data.vod_play_url,
|
||||
};
|
||||
return {
|
||||
list: [vod],
|
||||
};
|
||||
}
|
||||
|
||||
async function proxy(segments, headers, reqHeaders) {
|
||||
let what = segments[0];
|
||||
let segs = decodeURIComponent(segments[1]);
|
||||
if (what == 'hls') {
|
||||
function hlsHeader(data, hls) {
|
||||
let hlsHeaders = {};
|
||||
if (data.headers['content-length']) {
|
||||
Object.assign(hlsHeaders, data.headers, { 'content-length': hls.length.toString() });
|
||||
} else {
|
||||
Object.assign(hlsHeaders, data.headers);
|
||||
}
|
||||
delete hlsHeaders['transfer-encoding'];
|
||||
if (hlsHeaders['content-encoding'] == 'gzip') {
|
||||
delete hlsHeaders['content-encoding'];
|
||||
}
|
||||
return hlsHeaders;
|
||||
}
|
||||
const hlsData = await hlsCache(segs, headers);
|
||||
if (hlsData.variants) {
|
||||
// variants -> variants -> .... ignore
|
||||
const hls = HLS.stringify(hlsData.plist);
|
||||
return {
|
||||
code: hlsData.code,
|
||||
content: hls,
|
||||
headers: hlsHeader(hlsData, hls),
|
||||
};
|
||||
} else {
|
||||
const hls = HLS.stringify(hlsData.plist, (segment) => {
|
||||
return js2Proxy(false, siteType, siteKey, 'ts/' + encodeURIComponent(hlsData.key + '/' + segment.mediaSequenceNumber.toString()), headers);
|
||||
});
|
||||
return {
|
||||
code: hlsData.code,
|
||||
content: hls,
|
||||
headers: hlsHeader(hlsData, hls),
|
||||
};
|
||||
}
|
||||
} else if (what == 'ts') {
|
||||
const info = segs.split('/');
|
||||
const hlsKey = info[0];
|
||||
const segIdx = parseInt(info[1]);
|
||||
return await tsCache(hlsKey, segIdx, headers);
|
||||
}
|
||||
return '{}';
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
try {
|
||||
const pUrls = await hls2Urls(id, {});
|
||||
for (let index = 1; index < pUrls.length; index += 2) {
|
||||
pUrls[index] = js2Proxy(false, siteType, siteKey, 'hls/' + encodeURIComponent(pUrls[index]), {});
|
||||
}
|
||||
pUrls.push('original');
|
||||
pUrls.push(id);
|
||||
return {
|
||||
parse: 0,
|
||||
url: pUrls,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
parse: 0,
|
||||
url: id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg) {
|
||||
let page = pg || 1;
|
||||
if (page == 0) page = 1;
|
||||
const data = await request(url + `?ac=detail&wd=${wd}`);
|
||||
let videos = [];
|
||||
for (const vod of data.list) {
|
||||
videos.push({
|
||||
vod_id: vod.vod_id.toString(),
|
||||
vod_name: vod.vod_name.toString(),
|
||||
vod_pic: vod.vod_pic,
|
||||
vod_remarks: vod.vod_remarks,
|
||||
});
|
||||
}
|
||||
return {
|
||||
page: parseInt(data.page),
|
||||
pagecount: data.pagecount,
|
||||
total: data.total,
|
||||
list: videos,
|
||||
};
|
||||
}
|
||||
|
||||
const cacheRoot = 'hls_cache';
|
||||
const hlsKeys = [];
|
||||
const hlsPlistCaches = {};
|
||||
const interrupts = {};
|
||||
const downloadTask = {};
|
||||
let currentDownloadHlsKey = '';
|
||||
|
||||
function hlsCacheInsert(key, data) {
|
||||
hlsKeys.push(key);
|
||||
hlsPlistCaches[key] = data;
|
||||
if (hlsKeys.length > 5) {
|
||||
const rmKey = hlsKeys.shift();
|
||||
hlsCacheRemove(rmKey);
|
||||
}
|
||||
}
|
||||
|
||||
function hlsCacheRemove(key) {
|
||||
delete hlsPlistCaches[key];
|
||||
delete hlsKeys[key];
|
||||
new JSFile(cacheRoot + '/' + key).delete();
|
||||
}
|
||||
|
||||
function plistUriResolve(baseUrl, plist) {
|
||||
if (plist.variants) {
|
||||
for (const v of plist.variants) {
|
||||
if (!v.uri.startsWith('http')) {
|
||||
v.uri = relative2Absolute(baseUrl, v.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (plist.segments) {
|
||||
for (const s of plist.segments) {
|
||||
if (!s.uri.startsWith('http')) {
|
||||
s.uri = relative2Absolute(baseUrl, s.uri);
|
||||
}
|
||||
if (s.key && s.key.uri && !s.key.uri.startsWith('http')) {
|
||||
s.key.uri = relative2Absolute(baseUrl, s.key.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
return plist;
|
||||
}
|
||||
|
||||
async function hls2Urls(url, headers) {
|
||||
let urls = [];
|
||||
let resp = {};
|
||||
let tmpUrl = url;
|
||||
while (true) {
|
||||
resp = await req(tmpUrl, {
|
||||
headers: headers,
|
||||
redirect: 0,
|
||||
});
|
||||
if (resp.headers['location']) {
|
||||
tmpUrl = resp.headers['location'];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (resp.code == 200) {
|
||||
var hls = resp.content;
|
||||
const plist = plistUriResolve(tmpUrl, HLS.parse(hls));
|
||||
if (plist.variants) {
|
||||
for (const vari of _.sortBy(plist.variants, (v) => -1 * v.bandwidth)) {
|
||||
urls.push(`proxy_${vari.resolution.width}x${vari.resolution.height}`);
|
||||
urls.push(vari.uri);
|
||||
}
|
||||
} else {
|
||||
urls.push('proxy');
|
||||
urls.push(url);
|
||||
const hlsKey = md5X(url);
|
||||
hlsCacheInsert(hlsKey, {
|
||||
code: resp.code,
|
||||
plist: plist,
|
||||
key: hlsKey,
|
||||
headers: resp.headers,
|
||||
});
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
async function hlsCache(url, headers) {
|
||||
const hlsKey = md5X(url);
|
||||
if (hlsPlistCaches[hlsKey]) {
|
||||
return hlsPlistCaches[hlsKey];
|
||||
}
|
||||
let resp = {};
|
||||
let tmpUrl = url;
|
||||
while (true) {
|
||||
resp = await req(tmpUrl, {
|
||||
headers: headers,
|
||||
redirect: 0,
|
||||
});
|
||||
if (resp.headers['location']) {
|
||||
tmpUrl = resp.headers['location'];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (resp.code == 200) {
|
||||
var hls = resp.content;
|
||||
const plist = plistUriResolve(tmpUrl, HLS.parse(hls));
|
||||
hlsCacheInsert(hlsKey, {
|
||||
code: resp.code,
|
||||
plist: plist,
|
||||
key: hlsKey,
|
||||
headers: resp.headers,
|
||||
});
|
||||
return hlsPlistCaches[hlsKey];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async function tsCache(hlsKey, segmentIndex, headers) {
|
||||
if (!hlsPlistCaches[hlsKey]) {
|
||||
return {};
|
||||
}
|
||||
const plist = hlsPlistCaches[hlsKey].plist;
|
||||
const segments = plist.segments;
|
||||
|
||||
let startFirst = !downloadTask[hlsKey];
|
||||
if (startFirst) {
|
||||
downloadTask[hlsKey] = {};
|
||||
for (const seg of segments) {
|
||||
const tk = md5X(seg.uri + seg.mediaSequenceNumber.toString());
|
||||
downloadTask[hlsKey][tk] = {
|
||||
file: cacheRoot + '/' + hlsKey + '/' + tk,
|
||||
uri: seg.uri,
|
||||
key: tk,
|
||||
index: seg.mediaSequenceNumber,
|
||||
order: seg.mediaSequenceNumber,
|
||||
state: -1,
|
||||
read: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// sort task
|
||||
for (const tk in downloadTask[hlsKey]) {
|
||||
const task = downloadTask[hlsKey][tk];
|
||||
if (task.index >= segmentIndex) {
|
||||
task.order = task.index - segmentIndex;
|
||||
} else {
|
||||
task.order = segments.length - segmentIndex + task.index;
|
||||
}
|
||||
}
|
||||
|
||||
if (startFirst) {
|
||||
fixedCachePool(hlsKey, 5, headers);
|
||||
}
|
||||
|
||||
const segment = segments[segmentIndex];
|
||||
const tsKey = md5X(segment.uri + segment.mediaSequenceNumber.toString());
|
||||
const task = downloadTask[hlsKey][tsKey];
|
||||
if (task.state == 1 || task.state == -1) {
|
||||
const file = new JSFile(task.file);
|
||||
if (await file.exist()) {
|
||||
task.state = 1;
|
||||
// download finish
|
||||
return {
|
||||
buffer: 3,
|
||||
code: 200,
|
||||
headers: {
|
||||
connection: 'close',
|
||||
'content-type': 'video/mp2t',
|
||||
},
|
||||
content: file,
|
||||
};
|
||||
} else {
|
||||
// file miss?? retry
|
||||
task.state = -1;
|
||||
}
|
||||
}
|
||||
if (task.state == -1) {
|
||||
// start download
|
||||
startTsTask(hlsKey, task, headers);
|
||||
}
|
||||
// wait read dwonload
|
||||
if (task.state == 0) {
|
||||
var stream = new JSProxyStream();
|
||||
stream.head(200, {
|
||||
connection: 'close',
|
||||
'content-type': 'video/mp2t',
|
||||
});
|
||||
let downloaded = 0;
|
||||
task.read = true;
|
||||
new Promise(async function (resolve, reject) {
|
||||
const f = new JSFile(task.file + '.dl');
|
||||
await f.open('r');
|
||||
(async function waitReadFile() {
|
||||
const s = await f.size();
|
||||
if (s > downloaded) {
|
||||
var downloadBuf = await f.read(s - downloaded, downloaded);
|
||||
await stream.write(downloadBuf);
|
||||
downloaded = s;
|
||||
}
|
||||
if (task.state == 1 || task.state < 0) {
|
||||
// finish error or done
|
||||
stream.done();
|
||||
await f.close();
|
||||
await f.delete();
|
||||
task.read = false;
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
setTimeout(waitReadFile, 5);
|
||||
})();
|
||||
});
|
||||
return {
|
||||
buffer: 3,
|
||||
content: stream,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function startTsTask(hlsKey, task, headers) {
|
||||
if (task.state >= 0) return;
|
||||
if (!interrupts[hlsKey]) {
|
||||
return;
|
||||
}
|
||||
task.state = 0;
|
||||
if (await new JSFile(task.file).exist()) {
|
||||
task.state = 1;
|
||||
return;
|
||||
}
|
||||
const file = new JSFile(task.file + '.dl');
|
||||
await file.open('w');
|
||||
const resp = await req(task.uri, {
|
||||
buffer: 3,
|
||||
headers: headers,
|
||||
stream: file,
|
||||
timeout: [5000, 10000],
|
||||
});
|
||||
if (resp.error || resp.code >= 300) {
|
||||
await file.close();
|
||||
if (!task.read) {
|
||||
await file.delete();
|
||||
}
|
||||
task.state = -1;
|
||||
return;
|
||||
}
|
||||
await file.close();
|
||||
if (task.read) {
|
||||
await file.copy(task.file);
|
||||
} else {
|
||||
await file.move(task.file);
|
||||
}
|
||||
task.state = 1;
|
||||
}
|
||||
|
||||
async function fixedCachePool(hlsKey, limit, headers) {
|
||||
// keep last cache task only
|
||||
if (currentDownloadHlsKey && currentDownloadHlsKey != hlsKey) {
|
||||
delete interrupts[currentDownloadHlsKey];
|
||||
}
|
||||
currentDownloadHlsKey = hlsKey;
|
||||
interrupts[hlsKey] = true;
|
||||
for (let index = 0; index < limit; index++) {
|
||||
if (!interrupts[hlsKey]) break;
|
||||
new Promise(function (resolve, reject) {
|
||||
(async function doTask() {
|
||||
if (!interrupts[hlsKey]) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const tasks = _.pickBy(downloadTask[hlsKey], function (o) {
|
||||
return o.state == -1;
|
||||
});
|
||||
const task = _.minBy(Object.values(tasks), function (o) {
|
||||
return o.order;
|
||||
});
|
||||
if (!task) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
await startTsTask(hlsKey, task, headers);
|
||||
setTimeout(doTask, 5);
|
||||
})();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function relative2Absolute(base, relative) {
|
||||
var stack = base.split('/'),
|
||||
parts = relative.split('/');
|
||||
stack.pop();
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
if (parts[i] == '.') continue;
|
||||
if (parts[i] == '..') stack.pop();
|
||||
else stack.push(parts[i]);
|
||||
}
|
||||
return stack.join('/');
|
||||
}
|
||||
export {hls2Urls,hlsCache,tsCache}
|
||||
251
cat/tjs/lib/gb18030.js
Normal file
@ -0,0 +1,251 @@
|
||||
import { inRange, decoderError, encoderError, isASCIICodePoint,
|
||||
end_of_stream, finished, isASCIIByte, floor } from './text_decoder_utils.js'
|
||||
import index, {
|
||||
indexGB18030RangesCodePointFor, indexGB18030RangesPointerFor,
|
||||
indexCodePointFor, indexPointerFor } from './text_decoder_indexes.js'
|
||||
|
||||
// 11.2 gb18030
|
||||
|
||||
// 11.2.1 gb18030 decoder
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {Decoder}
|
||||
* @param {{fatal: boolean}} options
|
||||
*/
|
||||
export class GB18030Decoder {
|
||||
constructor(options) {
|
||||
const { fatal } = options
|
||||
this.fatal = fatal
|
||||
// gb18030's decoder has an associated gb18030 first, gb18030
|
||||
// second, and gb18030 third (all initially 0x00).
|
||||
this.gb18030_first = 0x00
|
||||
this.gb18030_second = 0x00,
|
||||
this.gb18030_third = 0x00
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
* @return The next code point(s) decoded, or null if not enough data exists in the input stream to decode a complete code point.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// 1. If byte is end-of-stream and gb18030 first, gb18030
|
||||
// second, and gb18030 third are 0x00, return finished.
|
||||
if (bite === end_of_stream && this.gb18030_first === 0x00 &&
|
||||
this.gb18030_second === 0x00 && this.gb18030_third === 0x00) {
|
||||
return finished
|
||||
}
|
||||
// 2. If byte is end-of-stream, and gb18030 first, gb18030
|
||||
// second, or gb18030 third is not 0x00, set gb18030 first,
|
||||
// gb18030 second, and gb18030 third to 0x00, and return error.
|
||||
if (bite === end_of_stream &&
|
||||
(this.gb18030_first !== 0x00 || this.gb18030_second !== 0x00 ||
|
||||
this.gb18030_third !== 0x00)) {
|
||||
this.gb18030_first = 0x00
|
||||
this.gb18030_second = 0x00
|
||||
this.gb18030_third = 0x00
|
||||
decoderError(this.fatal)
|
||||
}
|
||||
var code_point
|
||||
// 3. If gb18030 third is not 0x00, run these substeps:
|
||||
if (this.gb18030_third !== 0x00) {
|
||||
// 1. Let code point be null.
|
||||
code_point = null
|
||||
// 2. If byte is in the range 0x30 to 0x39, inclusive, set
|
||||
// code point to the index gb18030 ranges code point for
|
||||
// (((gb18030 first − 0x81) × 10 + gb18030 second − 0x30) ×
|
||||
// 126 + gb18030 third − 0x81) × 10 + byte − 0x30.
|
||||
if (inRange(bite, 0x30, 0x39)) {
|
||||
code_point = indexGB18030RangesCodePointFor(
|
||||
(((this.gb18030_first - 0x81) * 10 + this.gb18030_second - 0x30) * 126 +
|
||||
this.gb18030_third - 0x81) * 10 + bite - 0x30)
|
||||
}
|
||||
|
||||
// 3. Let buffer be a byte sequence consisting of gb18030
|
||||
// second, gb18030 third, and byte, in order.
|
||||
var buffer = [this.gb18030_second, this.gb18030_third, bite]
|
||||
|
||||
// 4. Set gb18030 first, gb18030 second, and gb18030 third to
|
||||
// 0x00.
|
||||
this.gb18030_first = 0x00
|
||||
this.gb18030_second = 0x00
|
||||
this.gb18030_third = 0x00
|
||||
|
||||
// 5. If code point is null, prepend buffer to stream and
|
||||
// return error.
|
||||
if (code_point === null) {
|
||||
stream.prepend(buffer)
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 6. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
|
||||
// 4. If gb18030 second is not 0x00, run these substeps:
|
||||
if (this.gb18030_second !== 0x00) {
|
||||
// 1. If byte is in the range 0x81 to 0xFE, inclusive, set
|
||||
// gb18030 third to byte and return continue.
|
||||
if (inRange(bite, 0x81, 0xFE)) {
|
||||
this.gb18030_third = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 2. Prepend gb18030 second followed by byte to stream, set
|
||||
// gb18030 first and gb18030 second to 0x00, and return error.
|
||||
stream.prepend([this.gb18030_second, bite])
|
||||
this.gb18030_first = 0x00
|
||||
this.gb18030_second = 0x00
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 5. If gb18030 first is not 0x00, run these substeps:
|
||||
if (this.gb18030_first !== 0x00) {
|
||||
// 1. If byte is in the range 0x30 to 0x39, inclusive, set
|
||||
// gb18030 second to byte and return continue.
|
||||
if (inRange(bite, 0x30, 0x39)) {
|
||||
this.gb18030_second = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 2. Let lead be gb18030 first, let pointer be null, and set
|
||||
// gb18030 first to 0x00.
|
||||
var lead = this.gb18030_first
|
||||
var pointer = null
|
||||
this.gb18030_first = 0x00
|
||||
|
||||
// 3. Let offset be 0x40 if byte is less than 0x7F and 0x41
|
||||
// otherwise.
|
||||
var offset = bite < 0x7F ? 0x40 : 0x41
|
||||
|
||||
// 4. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80
|
||||
// to 0xFE, inclusive, set pointer to (lead − 0x81) × 190 +
|
||||
// (byte − offset).
|
||||
if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE))
|
||||
pointer = (lead - 0x81) * 190 + (bite - offset)
|
||||
|
||||
// 5. Let code point be null if pointer is null and the index
|
||||
// code point for pointer in index gb18030 otherwise.
|
||||
code_point = pointer === null ? null :
|
||||
indexCodePointFor(pointer, index('gb18030'))
|
||||
|
||||
// 6. If code point is null and byte is an ASCII byte, prepend
|
||||
// byte to stream.
|
||||
if (code_point === null && isASCIIByte(bite))
|
||||
stream.prepend(bite)
|
||||
|
||||
// 7. If code point is null, return error.
|
||||
if (code_point === null)
|
||||
return decoderError(this.fatal)
|
||||
|
||||
// 8. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
|
||||
// 6. If byte is an ASCII byte, return a code point whose value
|
||||
// is byte.
|
||||
if (isASCIIByte(bite))
|
||||
return bite
|
||||
|
||||
// 7. If byte is 0x80, return code point U+20AC.
|
||||
if (bite === 0x80)
|
||||
return 0x20AC
|
||||
|
||||
// 8. If byte is in the range 0x81 to 0xFE, inclusive, set
|
||||
// gb18030 first to byte and return continue.
|
||||
if (inRange(bite, 0x81, 0xFE)) {
|
||||
this.gb18030_first = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 9. Return error.
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
}
|
||||
|
||||
// 11.2.2 gb18030 encoder
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class GB18030Encoder {
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
* @return Byte(s) to emit.
|
||||
*/
|
||||
handler(stream, code_point) {
|
||||
// 1. If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is an ASCII code point, return a byte whose
|
||||
// value is code point.
|
||||
if (isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 3. If code point is U+E5E5, return error with code point.
|
||||
if (code_point === 0xE5E5)
|
||||
return encoderError(code_point)
|
||||
|
||||
// 4. If the gbk flag is set and code point is U+20AC, return
|
||||
// byte 0x80.
|
||||
if (this.gbk_flag && code_point === 0x20AC)
|
||||
return 0x80
|
||||
|
||||
// 5. Let pointer be the index pointer for code point in index
|
||||
// gb18030.
|
||||
var pointer = indexPointerFor(code_point, index('gb18030'))
|
||||
|
||||
// 6. If pointer is not null, run these substeps:
|
||||
if (pointer !== null) {
|
||||
// 1. Let lead be floor(pointer / 190) + 0x81.
|
||||
var lead = floor(pointer / 190) + 0x81
|
||||
|
||||
// 2. Let trail be pointer % 190.
|
||||
var trail = pointer % 190
|
||||
|
||||
// 3. Let offset be 0x40 if trail is less than 0x3F and 0x41 otherwise.
|
||||
var offset = trail < 0x3F ? 0x40 : 0x41
|
||||
|
||||
// 4. Return two bytes whose values are lead and trail + offset.
|
||||
return [lead, trail + offset]
|
||||
}
|
||||
|
||||
// 7. If gbk flag is set, return error with code point.
|
||||
if (this.gbk_flag)
|
||||
return encoderError(code_point)
|
||||
|
||||
// 8. Set pointer to the index gb18030 ranges pointer for code
|
||||
// point.
|
||||
pointer = indexGB18030RangesPointerFor(code_point)
|
||||
|
||||
// 9. Let byte1 be floor(pointer / 10 / 126 / 10).
|
||||
var byte1 = floor(pointer / 10 / 126 / 10)
|
||||
|
||||
// 10. Set pointer to pointer − byte1 × 10 × 126 × 10.
|
||||
pointer = pointer - byte1 * 10 * 126 * 10
|
||||
|
||||
// 11. Let byte2 be floor(pointer / 10 / 126).
|
||||
var byte2 = floor(pointer / 10 / 126)
|
||||
|
||||
// 12. Set pointer to pointer − byte2 × 10 × 126.
|
||||
pointer = pointer - byte2 * 10 * 126
|
||||
|
||||
// 13. Let byte3 be floor(pointer / 10).
|
||||
var byte3 = floor(pointer / 10)
|
||||
|
||||
// 14. Let byte4 be pointer − byte3 × 10.
|
||||
var byte4 = pointer - byte3 * 10
|
||||
|
||||
// 15. Return four bytes whose values are byte1 + 0x81, byte2 +
|
||||
// 0x30, byte3 + 0x81, byte4 + 0x30.
|
||||
return [byte1 + 0x81,
|
||||
byte2 + 0x30,
|
||||
byte3 + 0x81,
|
||||
byte4 + 0x30]
|
||||
}
|
||||
|
||||
constructor(options = {}, gbk_flag = false) {
|
||||
// gb18030's decoder has an associated gbk flag (initially unset).
|
||||
this.gbk_flag = gbk_flag
|
||||
}
|
||||
}
|
||||
940
cat/tjs/lib/hls.js
Normal file
@ -0,0 +1,940 @@
|
||||
/*
|
||||
* @File : hls.js.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/5 16:07
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
let t = {};
|
||||
|
||||
function e(e) {
|
||||
if (t.strictMode) throw e;
|
||||
t.silent || console.error(e.message)
|
||||
}
|
||||
|
||||
function s(t, ...s) {
|
||||
for (const [i, n] of s.entries()) n || e(new Error(`${t} : Failed at [${i}]`))
|
||||
}
|
||||
|
||||
function i(...t) {
|
||||
for (const [s, [i, n]] of t.entries()) i && (n || e(new Error(`Conditional Assert : Failed at [${s}]`)))
|
||||
}
|
||||
|
||||
function n(...t) {
|
||||
for (const [s, i] of t.entries()) void 0 === i && e(new Error(`Param Check : Failed at [${s}]`))
|
||||
}
|
||||
|
||||
function a(...t) {
|
||||
for (const [s, [i, n]] of t.entries()) i && void 0 === n && e(new Error(`Conditional Param Check : Failed at [${s}]`))
|
||||
}
|
||||
|
||||
function r(t) {
|
||||
e(new Error(`Invalid Playlist : ${t}`))
|
||||
}
|
||||
|
||||
function o(t, e = 10) {
|
||||
if ("number" == typeof t) return t;
|
||||
const s = 10 === e ? Number.parseFloat(t) : Number.parseInt(t, e);
|
||||
return Number.isNaN(s) ? 0 : s
|
||||
}
|
||||
|
||||
function E(t) {
|
||||
(t.startsWith("0x") || t.startsWith("0X")) && (t = t.slice(2));
|
||||
const e = new Uint8Array(t.length / 2);
|
||||
for (let s = 0; s < t.length; s += 2) e[s / 2] = o(t.slice(s, s + 2), 16);
|
||||
return e
|
||||
}
|
||||
|
||||
function T(t, s = 0, i = t.length) {
|
||||
i <= s && e(new Error(`end must be larger than start : start=${s}, end=${i}`));
|
||||
const n = [];
|
||||
for (let e = s; e < i; e++) n.push(`0${(255 & t[e]).toString(16).toUpperCase()}`.slice(-2));
|
||||
return `0x${n.join("")}`
|
||||
}
|
||||
|
||||
function u(t, e, s = 0) {
|
||||
let i = -1;
|
||||
for (let n = 0, a = 0; n < t.length; n++) if (t[n] === e) {
|
||||
if (a++ === s) return [t.slice(0, n), t.slice(n + 1)];
|
||||
i = n
|
||||
}
|
||||
return -1 !== i ? [t.slice(0, i), t.slice(i + 1)] : [t]
|
||||
}
|
||||
|
||||
function c(t) {
|
||||
const e = [];
|
||||
let s = !1;
|
||||
for (const i of t) "-" !== i && "_" !== i ? s ? (e.push(i.toUpperCase()), s = !1) : e.push(i.toLowerCase()) : s = !0;
|
||||
return e.join("")
|
||||
}
|
||||
|
||||
function l(t) {
|
||||
return `${t.getUTCFullYear()}-${("0" + (t.getUTCMonth() + 1)).slice(-2)}-${("0" + t.getUTCDate()).slice(-2)}T${("0" + t.getUTCHours()).slice(-2)}:${("0" + t.getUTCMinutes()).slice(-2)}:${("0" + t.getUTCSeconds()).slice(-2)}.${("00" + t.getUTCMilliseconds()).slice(-3)}Z`
|
||||
}
|
||||
|
||||
function h(e = {}) {
|
||||
t = Object.assign(t, e)
|
||||
}
|
||||
|
||||
function X() {
|
||||
return Object.assign({}, t)
|
||||
}
|
||||
|
||||
function p(t, e) {
|
||||
e = Math.trunc(e) || 0;
|
||||
const s = t.length >>> 0;
|
||||
if (e < 0 && (e = s + e), !(e < 0 || e >= s)) return t[e]
|
||||
}
|
||||
|
||||
class I {
|
||||
constructor({
|
||||
type: t,
|
||||
uri: e,
|
||||
groupId: s,
|
||||
language: a,
|
||||
assocLanguage: r,
|
||||
name: o,
|
||||
isDefault: E,
|
||||
autoselect: T,
|
||||
forced: u,
|
||||
instreamId: c,
|
||||
characteristics: l,
|
||||
channels: h
|
||||
}) {
|
||||
n(t, s, o), i(["SUBTITLES" === t, e], ["CLOSED-CAPTIONS" === t, c], ["CLOSED-CAPTIONS" === t, !e], [u, "SUBTITLES" === t]), this.type = t, this.uri = e, this.groupId = s, this.language = a, this.assocLanguage = r, this.name = o, this.isDefault = E, this.autoselect = T, this.forced = u, this.instreamId = c, this.characteristics = l, this.channels = h
|
||||
}
|
||||
}
|
||||
|
||||
class N {
|
||||
constructor({
|
||||
uri: t,
|
||||
isIFrameOnly: e = !1,
|
||||
bandwidth: s,
|
||||
averageBandwidth: i,
|
||||
score: a,
|
||||
codecs: r,
|
||||
resolution: o,
|
||||
frameRate: E,
|
||||
hdcpLevel: T,
|
||||
allowedCpc: u,
|
||||
videoRange: c,
|
||||
stableVariantId: l,
|
||||
programId: h,
|
||||
audio: X = [],
|
||||
video: p = [],
|
||||
subtitles: I = [],
|
||||
closedCaptions: N = [],
|
||||
currentRenditions: d = {audio: 0, video: 0, subtitles: 0, closedCaptions: 0}
|
||||
}) {
|
||||
n(t, s), this.uri = t, this.isIFrameOnly = e, this.bandwidth = s, this.averageBandwidth = i, this.score = a, this.codecs = r, this.resolution = o, this.frameRate = E, this.hdcpLevel = T, this.allowedCpc = u, this.videoRange = c, this.stableVariantId = l, this.programId = h, this.audio = X, this.video = p, this.subtitles = I, this.closedCaptions = N, this.currentRenditions = d
|
||||
}
|
||||
}
|
||||
|
||||
class d {
|
||||
constructor({id: t, value: e, uri: i, language: a}) {
|
||||
n(t, e || i), s("SessionData cannot have both value and uri, shoud be either.", !(e && i)), this.id = t, this.value = e, this.uri = i, this.language = a
|
||||
}
|
||||
}
|
||||
|
||||
class A {
|
||||
constructor({method: t, uri: e, iv: s, format: r, formatVersion: o}) {
|
||||
n(t), a(["NONE" !== t, e]), i(["NONE" === t, !(e || s || r || o)]), this.method = t, this.uri = e, this.iv = s, this.format = r, this.formatVersion = o
|
||||
}
|
||||
}
|
||||
|
||||
class f {
|
||||
constructor({hint: t = !1, uri: e, mimeType: s, byterange: i}) {
|
||||
n(e), this.hint = t, this.uri = e, this.mimeType = s, this.byterange = i
|
||||
}
|
||||
}
|
||||
|
||||
class S {
|
||||
constructor({
|
||||
id: t,
|
||||
classId: e,
|
||||
start: s,
|
||||
end: r,
|
||||
duration: o,
|
||||
plannedDuration: E,
|
||||
endOnNext: T,
|
||||
attributes: u = {}
|
||||
}) {
|
||||
n(t), a([!0 === T, e]), i([r, s], [r, s <= r], [o, o >= 0], [E, E >= 0]), this.id = t, this.classId = e, this.start = s, this.end = r, this.duration = o, this.plannedDuration = E, this.endOnNext = T, this.attributes = u
|
||||
}
|
||||
}
|
||||
|
||||
class R {
|
||||
constructor({type: t, duration: e, tagName: s, value: i}) {
|
||||
n(t), a(["OUT" === t, e]), a(["RAW" === t, s]), this.type = t, this.duration = e, this.tagName = s, this.value = i
|
||||
}
|
||||
}
|
||||
|
||||
class m {
|
||||
constructor(t) {
|
||||
n(t), this.type = t
|
||||
}
|
||||
}
|
||||
|
||||
class g extends m {
|
||||
constructor({isMasterPlaylist: t, uri: e, version: s, independentSegments: i = !1, start: a, source: r}) {
|
||||
super("playlist"), n(t), this.isMasterPlaylist = t, this.uri = e, this.version = s, this.independentSegments = i, this.start = a, this.source = r
|
||||
}
|
||||
}
|
||||
|
||||
class O extends g {
|
||||
constructor(t = {}) {
|
||||
super(Object.assign(Object.assign({}, t), {isMasterPlaylist: !0}));
|
||||
const {variants: e = [], currentVariant: s, sessionDataList: i = [], sessionKeyList: n = []} = t;
|
||||
this.variants = e, this.currentVariant = s, this.sessionDataList = i, this.sessionKeyList = n
|
||||
}
|
||||
}
|
||||
|
||||
class D extends g {
|
||||
constructor(t = {}) {
|
||||
super(Object.assign(Object.assign({}, t), {isMasterPlaylist: !1}));
|
||||
const {
|
||||
targetDuration: e,
|
||||
mediaSequenceBase: s = 0,
|
||||
discontinuitySequenceBase: i = 0,
|
||||
endlist: n = !1,
|
||||
playlistType: a,
|
||||
isIFrame: r,
|
||||
segments: o = [],
|
||||
prefetchSegments: E = [],
|
||||
lowLatencyCompatibility: T,
|
||||
partTargetDuration: u,
|
||||
renditionReports: c = [],
|
||||
skip: l = 0,
|
||||
hash: h
|
||||
} = t;
|
||||
this.targetDuration = e, this.mediaSequenceBase = s, this.discontinuitySequenceBase = i, this.endlist = n, this.playlistType = a, this.isIFrame = r, this.segments = o, this.prefetchSegments = E, this.lowLatencyCompatibility = T, this.partTargetDuration = u, this.renditionReports = c, this.skip = l, this.hash = h
|
||||
}
|
||||
}
|
||||
|
||||
class P extends m {
|
||||
constructor({
|
||||
uri: t,
|
||||
mimeType: e,
|
||||
data: s,
|
||||
duration: i,
|
||||
title: n,
|
||||
byterange: a,
|
||||
discontinuity: r,
|
||||
mediaSequenceNumber: o = 0,
|
||||
discontinuitySequence: E = 0,
|
||||
key: T,
|
||||
map: u,
|
||||
programDateTime: c,
|
||||
dateRange: l,
|
||||
markers: h = [],
|
||||
parts: X = []
|
||||
}) {
|
||||
super("segment"), this.uri = t, this.mimeType = e, this.data = s, this.duration = i, this.title = n, this.byterange = a, this.discontinuity = r, this.mediaSequenceNumber = o, this.discontinuitySequence = E, this.key = T, this.map = u, this.programDateTime = c, this.dateRange = l, this.markers = h, this.parts = X
|
||||
}
|
||||
}
|
||||
|
||||
class y extends m {
|
||||
constructor({hint: t = !1, uri: e, duration: s, independent: i, byterange: a, gap: r}) {
|
||||
super("part"), n(e), this.hint = t, this.uri = e, this.duration = s, this.independent = i, this.duration = s, this.byterange = a, this.gap = r
|
||||
}
|
||||
}
|
||||
|
||||
class C extends m {
|
||||
constructor({uri: t, discontinuity: e, mediaSequenceNumber: s = 0, discontinuitySequence: i = 0, key: a}) {
|
||||
super("prefetch"), n(t), this.uri = t, this.discontinuity = e, this.mediaSequenceNumber = s, this.discontinuitySequence = i, this.key = a
|
||||
}
|
||||
}
|
||||
|
||||
class U {
|
||||
constructor({uri: t, lastMSN: e, lastPart: s}) {
|
||||
n(t), this.uri = t, this.lastMSN = e, this.lastPart = s
|
||||
}
|
||||
}
|
||||
|
||||
var M = Object.freeze({
|
||||
__proto__: null,
|
||||
Rendition: I,
|
||||
Variant: N,
|
||||
SessionData: d,
|
||||
Key: A,
|
||||
MediaInitializationSection: f,
|
||||
DateRange: S,
|
||||
SpliceInfo: R,
|
||||
Playlist: g,
|
||||
MasterPlaylist: O,
|
||||
MediaPlaylist: D,
|
||||
Segment: P,
|
||||
PartialSegment: y,
|
||||
PrefetchSegment: C,
|
||||
RenditionReport: U
|
||||
});
|
||||
|
||||
function b(t) {
|
||||
return function (t, e = " ") {
|
||||
return t ? (t = t.trim(), " " === e || (t.startsWith(e) && (t = t.slice(1)), t.endsWith(e) && (t = t.slice(0, -1))), t) : t
|
||||
}(t, '"')
|
||||
}
|
||||
|
||||
function L(t) {
|
||||
const e = u(t, ",");
|
||||
return {duration: o(e[0]), title: decodeURIComponent(escape(e[1]))}
|
||||
}
|
||||
|
||||
function v(t) {
|
||||
const e = u(t, "@");
|
||||
return {length: o(e[0]), offset: e[1] ? o(e[1]) : -1}
|
||||
}
|
||||
|
||||
function $(t) {
|
||||
const e = u(t, "x");
|
||||
return {width: o(e[0]), height: o(e[1])}
|
||||
}
|
||||
|
||||
function Y(t) {
|
||||
const e = "ALLOWED-CPC: Each entry must consit of KEYFORMAT and Content Protection Configuration", s = t.split(",");
|
||||
0 === s.length && r(e);
|
||||
const i = [];
|
||||
for (const t of s) {
|
||||
const [s, n] = u(t, ":");
|
||||
s && n ? i.push({format: s, cpcList: n.split("/")}) : r(e)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
function F(t) {
|
||||
const e = E(t);
|
||||
return 16 !== e.length && r("IV must be a 128-bit unsigned integer"), e
|
||||
}
|
||||
|
||||
function G(t, e) {
|
||||
e.IV && t.compatibleVersion < 2 && (t.compatibleVersion = 2), (e.KEYFORMAT || e.KEYFORMATVERSIONS) && t.compatibleVersion < 5 && (t.compatibleVersion = 5)
|
||||
}
|
||||
|
||||
function V(t) {
|
||||
const e = {};
|
||||
for (const i of function (t) {
|
||||
const e = [];
|
||||
let s = !0, i = 0;
|
||||
const n = [];
|
||||
for (let a = 0; a < t.length; a++) {
|
||||
const r = t[a];
|
||||
s && "," === r ? (e.push(t.slice(i, a).trim()), i = a + 1) : '"' !== r && "'" !== r || (s ? (n.push(r), s = !1) : r === p(n, -1) ? (n.pop(), s = !0) : n.push(r))
|
||||
}
|
||||
return e.push(t.slice(i).trim()), e
|
||||
}(t)) {
|
||||
const [t, n] = u(i, "="), a = b(n);
|
||||
switch (t) {
|
||||
case"URI":
|
||||
e[t] = a;
|
||||
break;
|
||||
case"START-DATE":
|
||||
case"END-DATE":
|
||||
e[t] = new Date(a);
|
||||
break;
|
||||
case"IV":
|
||||
e[t] = F(a);
|
||||
break;
|
||||
case"BYTERANGE":
|
||||
e[t] = v(a);
|
||||
break;
|
||||
case"RESOLUTION":
|
||||
e[t] = $(a);
|
||||
break;
|
||||
case"ALLOWED-CPC":
|
||||
e[t] = Y(a);
|
||||
break;
|
||||
case"END-ON-NEXT":
|
||||
case"DEFAULT":
|
||||
case"AUTOSELECT":
|
||||
case"FORCED":
|
||||
case"PRECISE":
|
||||
case"CAN-BLOCK-RELOAD":
|
||||
case"INDEPENDENT":
|
||||
case"GAP":
|
||||
e[t] = "YES" === a;
|
||||
break;
|
||||
case"DURATION":
|
||||
case"PLANNED-DURATION":
|
||||
case"BANDWIDTH":
|
||||
case"AVERAGE-BANDWIDTH":
|
||||
case"FRAME-RATE":
|
||||
case"TIME-OFFSET":
|
||||
case"CAN-SKIP-UNTIL":
|
||||
case"HOLD-BACK":
|
||||
case"PART-HOLD-BACK":
|
||||
case"PART-TARGET":
|
||||
case"BYTERANGE-START":
|
||||
case"BYTERANGE-LENGTH":
|
||||
case"LAST-MSN":
|
||||
case"LAST-PART":
|
||||
case"SKIPPED-SEGMENTS":
|
||||
case"SCORE":
|
||||
case"PROGRAM-ID":
|
||||
e[t] = o(a);
|
||||
break;
|
||||
default:
|
||||
t.startsWith("SCTE35-") ? e[t] = E(a) : t.startsWith("X-") ? e[t] = (s = n).startsWith('"') ? b(s) : s.startsWith("0x") || s.startsWith("0X") ? E(s) : o(s) : ("VIDEO-RANGE" === t && "SDR" !== a && "HLG" !== a && "PQ" !== a && r(`VIDEO-RANGE: unknown value "${a}"`), e[t] = a)
|
||||
}
|
||||
}
|
||||
var s;
|
||||
return e
|
||||
}
|
||||
|
||||
function w() {
|
||||
r("The file contains both media and master playlist tags.")
|
||||
}
|
||||
|
||||
function B(t, e, s) {
|
||||
const i = function ({attributes: t}) {
|
||||
return new I({
|
||||
type: t.TYPE,
|
||||
uri: t.URI,
|
||||
groupId: t["GROUP-ID"],
|
||||
language: t.LANGUAGE,
|
||||
assocLanguage: t["ASSOC-LANGUAGE"],
|
||||
name: t.NAME,
|
||||
isDefault: t.DEFAULT,
|
||||
autoselect: t.AUTOSELECT,
|
||||
forced: t.FORCED,
|
||||
instreamId: t["INSTREAM-ID"],
|
||||
characteristics: t.CHARACTERISTICS,
|
||||
channels: t.CHANNELS
|
||||
})
|
||||
}(e), n = t[c(s)], a = function (t, e) {
|
||||
let s = !1;
|
||||
for (const i of t) {
|
||||
if (i.name === e.name) return "All EXT-X-MEDIA tags in the same Group MUST have different NAME attributes.";
|
||||
i.isDefault && (s = !0)
|
||||
}
|
||||
return s && e.isDefault ? "EXT-X-MEDIA A Group MUST NOT have more than one member with a DEFAULT attribute of YES." : ""
|
||||
}(n, i);
|
||||
a && r(a), n.push(i), i.isDefault && (t.currentRenditions[c(s)] = n.length - 1)
|
||||
}
|
||||
|
||||
function H(t, e, s, i, n) {
|
||||
const a = new N({
|
||||
uri: s,
|
||||
bandwidth: e.BANDWIDTH,
|
||||
averageBandwidth: e["AVERAGE-BANDWIDTH"],
|
||||
score: e.SCORE,
|
||||
codecs: e.CODECS,
|
||||
resolution: e.RESOLUTION,
|
||||
frameRate: e["FRAME-RATE"],
|
||||
hdcpLevel: e["HDCP-LEVEL"],
|
||||
allowedCpc: e["ALLOWED-CPC"],
|
||||
videoRange: e["VIDEO-RANGE"],
|
||||
stableVariantId: e["STABLE-VARIANT-ID"],
|
||||
programId: e["PROGRAM-ID"]
|
||||
});
|
||||
for (const s of t) if ("EXT-X-MEDIA" === s.name) {
|
||||
const t = s.attributes, i = t.TYPE;
|
||||
if (i && t["GROUP-ID"] || r("EXT-X-MEDIA TYPE attribute is REQUIRED."), e[i] === t["GROUP-ID"] && (B(a, s, i), "CLOSED-CAPTIONS" === i)) for (const {instreamId: t} of a.closedCaptions) if (t && t.startsWith("SERVICE") && n.compatibleVersion < 7) {
|
||||
n.compatibleVersion = 7;
|
||||
break
|
||||
}
|
||||
}
|
||||
return function (t, e, s) {
|
||||
for (const i of ["AUDIO", "VIDEO", "SUBTITLES", "CLOSED-CAPTIONS"]) "CLOSED-CAPTIONS" === i && "NONE" === t[i] ? (s.isClosedCaptionsNone = !0, e.closedCaptions = []) : t[i] && !e[c(i)].some((e => e.groupId === t[i])) && r(`${i} attribute MUST match the value of the GROUP-ID attribute of an EXT-X-MEDIA tag whose TYPE attribute is ${i}.`)
|
||||
}(e, a, n), a.isIFrameOnly = i, a
|
||||
}
|
||||
|
||||
function K(t, e) {
|
||||
if (t.method !== e.method) return !1;
|
||||
if (t.uri !== e.uri) return !1;
|
||||
if (t.iv) {
|
||||
if (!e.iv) return !1;
|
||||
if (t.iv.length !== e.iv.length) return !1;
|
||||
for (let s = 0; s < t.iv.length; s++) if (t.iv[s] !== e.iv[s]) return !1
|
||||
} else if (e.iv) return !1;
|
||||
return t.format === e.format && t.formatVersion === e.formatVersion
|
||||
}
|
||||
|
||||
function k(t, e, s, i, n, a, o) {
|
||||
const E = new P({uri: e, mediaSequenceNumber: n, discontinuitySequence: a});
|
||||
let T = !1, u = !1;
|
||||
for (let e = s; e <= i; e++) {
|
||||
const {name: s, value: i, attributes: n} = t[e];
|
||||
if ("EXTINF" === s) !Number.isInteger(i.duration) && o.compatibleVersion < 3 && (o.compatibleVersion = 3), Math.round(i.duration) > o.targetDuration && r("EXTINF duration, when rounded to the nearest integer, MUST be less than or equal to the target duration"), E.duration = i.duration, E.title = i.title; else if ("EXT-X-BYTERANGE" === s) o.compatibleVersion < 4 && (o.compatibleVersion = 4), E.byterange = i; else if ("EXT-X-DISCONTINUITY" === s) E.parts.length > 0 && r("EXT-X-DISCONTINUITY must appear before the first EXT-X-PART tag of the Parent Segment."), E.discontinuity = !0; else if ("EXT-X-KEY" === s) E.parts.length > 0 && r("EXT-X-KEY must appear before the first EXT-X-PART tag of the Parent Segment."), G(o, n), E.key = new A({
|
||||
method: n.METHOD,
|
||||
uri: n.URI,
|
||||
iv: n.IV,
|
||||
format: n.KEYFORMAT,
|
||||
formatVersion: n.KEYFORMATVERSIONS
|
||||
}); else if ("EXT-X-MAP" === s) E.parts.length > 0 && r("EXT-X-MAP must appear before the first EXT-X-PART tag of the Parent Segment."), o.compatibleVersion < 5 && (o.compatibleVersion = 5), o.hasMap = !0, E.map = new f({
|
||||
uri: n.URI,
|
||||
byterange: n.BYTERANGE
|
||||
}); else if ("EXT-X-PROGRAM-DATE-TIME" === s) E.programDateTime = i; else if ("EXT-X-DATERANGE" === s) {
|
||||
const t = {};
|
||||
for (const e of Object.keys(n)) (e.startsWith("SCTE35-") || e.startsWith("X-")) && (t[e] = n[e]);
|
||||
E.dateRange = new S({
|
||||
id: n.ID,
|
||||
classId: n.CLASS,
|
||||
start: n["START-DATE"],
|
||||
end: n["END-DATE"],
|
||||
duration: n.DURATION,
|
||||
plannedDuration: n["PLANNED-DURATION"],
|
||||
endOnNext: n["END-ON-NEXT"],
|
||||
attributes: t
|
||||
})
|
||||
} else if ("EXT-X-CUE-OUT" === s) E.markers.push(new R({
|
||||
type: "OUT",
|
||||
duration: n && n.DURATION || i
|
||||
})); else if ("EXT-X-CUE-IN" === s) E.markers.push(new R({type: "IN"})); else if ("EXT-X-CUE-OUT-CONT" === s || "EXT-X-CUE" === s || "EXT-OATCLS-SCTE35" === s || "EXT-X-ASSET" === s || "EXT-X-SCTE35" === s) E.markers.push(new R({
|
||||
type: "RAW",
|
||||
tagName: s,
|
||||
value: i
|
||||
})); else if ("EXT-X-PRELOAD-HINT" !== s || n.TYPE) if ("EXT-X-PRELOAD-HINT" === s && "PART" === n.TYPE && u) r("Servers should not add more than one EXT-X-PRELOAD-HINT tag with the same TYPE attribute to a Playlist."); else if ("EXT-X-PART" !== s && "EXT-X-PRELOAD-HINT" !== s || n.URI) {
|
||||
if ("EXT-X-PRELOAD-HINT" === s && "MAP" === n.TYPE) T && r("Servers should not add more than one EXT-X-PRELOAD-HINT tag with the same TYPE attribute to a Playlist."), T = !0, o.hasMap = !0, E.map = new f({
|
||||
hint: !0,
|
||||
uri: n.URI,
|
||||
byterange: {length: n["BYTERANGE-LENGTH"], offset: n["BYTERANGE-START"] || 0}
|
||||
}); else if ("EXT-X-PART" === s || "EXT-X-PRELOAD-HINT" === s && "PART" === n.TYPE) {
|
||||
"EXT-X-PART" !== s || n.DURATION || r("EXT-X-PART: DURATION attribute is mandatory"), "EXT-X-PRELOAD-HINT" === s && (u = !0);
|
||||
const t = new y({
|
||||
hint: "EXT-X-PRELOAD-HINT" === s,
|
||||
uri: n.URI,
|
||||
byterange: "EXT-X-PART" === s ? n.BYTERANGE : {
|
||||
length: n["BYTERANGE-LENGTH"],
|
||||
offset: n["BYTERANGE-START"] || 0
|
||||
},
|
||||
duration: n.DURATION,
|
||||
independent: n.INDEPENDENT,
|
||||
gap: n.GAP
|
||||
});
|
||||
E.parts.push(t)
|
||||
}
|
||||
} else r("EXT-X-PART / EXT-X-PRELOAD-HINT: URI attribute is mandatory"); else r("EXT-X-PRELOAD-HINT: TYPE attribute is mandatory")
|
||||
}
|
||||
return E
|
||||
}
|
||||
|
||||
function W(t, e, s, i, n, a, o) {
|
||||
const E = new C({uri: e, mediaSequenceNumber: n, discontinuitySequence: a});
|
||||
for (let e = s; e <= i; e++) {
|
||||
const {name: s, attributes: i} = t[e];
|
||||
"EXTINF" === s ? r("A prefetch segment must not be advertised with an EXTINF tag.") : "EXT-X-DISCONTINUITY" === s ? r("A prefetch segment must not be advertised with an EXT-X-DISCONTINUITY tag.") : "EXT-X-PREFETCH-DISCONTINUITY" === s ? E.discontinuity = !0 : "EXT-X-KEY" === s ? (G(o, i), E.key = new A({
|
||||
method: i.METHOD,
|
||||
uri: i.URI,
|
||||
iv: i.IV,
|
||||
format: i.KEYFORMAT,
|
||||
formatVersion: i.KEYFORMATVERSIONS
|
||||
})) : "EXT-X-MAP" === s && r("Prefetch segments must not be advertised with an EXT-X-MAP tag.")
|
||||
}
|
||||
return E
|
||||
}
|
||||
|
||||
function q(t, e) {
|
||||
var s;
|
||||
const i = new D;
|
||||
let n = -1, a = 0, o = !1, E = !1, T = 0, u = null, c = null, l = !1;
|
||||
for (const [s, h] of t.entries()) {
|
||||
const {name: X, value: p, attributes: I, category: N} = h;
|
||||
if ("Segment" !== N) {
|
||||
if ("EXT-X-VERSION" === X) void 0 === i.version ? i.version = p : r("A Playlist file MUST NOT contain more than one EXT-X-VERSION tag."); else if ("EXT-X-TARGETDURATION" === X) i.targetDuration = e.targetDuration = p; else if ("EXT-X-MEDIA-SEQUENCE" === X) i.segments.length > 0 && r("The EXT-X-MEDIA-SEQUENCE tag MUST appear before the first Media Segment in the Playlist."), i.mediaSequenceBase = a = p; else if ("EXT-X-DISCONTINUITY-SEQUENCE" === X) i.segments.length > 0 && r("The EXT-X-DISCONTINUITY-SEQUENCE tag MUST appear before the first Media Segment in the Playlist."), o && r("The EXT-X-DISCONTINUITY-SEQUENCE tag MUST appear before any EXT-X-DISCONTINUITY tag."), i.discontinuitySequenceBase = T = p; else if ("EXT-X-ENDLIST" === X) i.endlist = !0; else if ("EXT-X-PLAYLIST-TYPE" === X) i.playlistType = p; else if ("EXT-X-I-FRAMES-ONLY" === X) e.compatibleVersion < 4 && (e.compatibleVersion = 4), i.isIFrame = !0; else if ("EXT-X-INDEPENDENT-SEGMENTS" === X) i.independentSegments && r("EXT-X-INDEPENDENT-SEGMENTS tag MUST NOT appear more than once in a Playlist"), i.independentSegments = !0; else if ("EXT-X-START" === X) i.start && r("EXT-X-START tag MUST NOT appear more than once in a Playlist"), "number" != typeof I["TIME-OFFSET"] && r("EXT-X-START: TIME-OFFSET attribute is REQUIRED"), i.start = {
|
||||
offset: I["TIME-OFFSET"],
|
||||
precise: I.PRECISE || !1
|
||||
}; else if ("EXT-X-SERVER-CONTROL" === X) I["CAN-BLOCK-RELOAD"] || r("EXT-X-SERVER-CONTROL: CAN-BLOCK-RELOAD=YES is mandatory for Low-Latency HLS"), i.lowLatencyCompatibility = {
|
||||
canBlockReload: I["CAN-BLOCK-RELOAD"],
|
||||
canSkipUntil: I["CAN-SKIP-UNTIL"],
|
||||
holdBack: I["HOLD-BACK"],
|
||||
partHoldBack: I["PART-HOLD-BACK"]
|
||||
}; else if ("EXT-X-PART-INF" === X) I["PART-TARGET"] || r("EXT-X-PART-INF: PART-TARGET attribute is mandatory"), i.partTargetDuration = I["PART-TARGET"]; else if ("EXT-X-RENDITION-REPORT" === X) I.URI || r("EXT-X-RENDITION-REPORT: URI attribute is mandatory"), 0 === I.URI.search(/^[a-z]+:/) && r("EXT-X-RENDITION-REPORT: URI must be relative to the playlist uri"), i.renditionReports.push(new U({
|
||||
uri: I.URI,
|
||||
lastMSN: I["LAST-MSN"],
|
||||
lastPart: I["LAST-PART"]
|
||||
})); else if ("EXT-X-SKIP" === X) I["SKIPPED-SEGMENTS"] || r("EXT-X-SKIP: SKIPPED-SEGMENTS attribute is mandatory"), e.compatibleVersion < 9 && (e.compatibleVersion = 9), i.skip = I["SKIPPED-SEGMENTS"], a += i.skip; else if ("EXT-X-PREFETCH" === X) {
|
||||
const r = W(t, p, -1 === n ? s : n, s - 1, a++, T, e);
|
||||
r && (r.discontinuity && (r.discontinuitySequence++, T = r.discontinuitySequence), r.key ? u = r.key : r.key = u, i.prefetchSegments.push(r)), E = !0, n = -1
|
||||
} else if ("string" == typeof h) {
|
||||
-1 === n && r("A URI line is not preceded by any segment tags"), i.targetDuration || r("The EXT-X-TARGETDURATION tag is REQUIRED"), E && r("These segments must appear after all complete segments.");
|
||||
const o = k(t, h, n, s - 1, a++, T, e);
|
||||
o && ([T, u, c] = x(i, o, T, u, c), !l && o.parts.length > 0 && (l = !0)), n = -1
|
||||
}
|
||||
} else -1 === n && (n = s), "EXT-X-DISCONTINUITY" === X && (o = !0)
|
||||
}
|
||||
if (-1 !== n) {
|
||||
const o = k(t, "", n, t.length - 1, a++, T, e);
|
||||
if (o) {
|
||||
const {parts: t} = o;
|
||||
t.length > 0 && !i.endlist && !(null === (s = p(t, -1)) || void 0 === s ? void 0 : s.hint) && r("If the Playlist contains EXT-X-PART tags and does not contain an EXT-X-ENDLIST tag, the Playlist must contain an EXT-X-PRELOAD-HINT tag with a TYPE=PART attribute"), x(i, o, u, c), !l && o.parts.length > 0 && (l = !0)
|
||||
}
|
||||
}
|
||||
return function (t) {
|
||||
const e = new Map, s = new Map;
|
||||
let i = !1, n = !1;
|
||||
for (let a = t.length - 1; a >= 0; a--) {
|
||||
const {programDateTime: o, dateRange: E} = t[a];
|
||||
if (o && (n = !0), E && E.start) {
|
||||
i = !0, E.endOnNext && (E.end || E.duration) && r("An EXT-X-DATERANGE tag with an END-ON-NEXT=YES attribute MUST NOT contain DURATION or END-DATE attributes.");
|
||||
const t = E.start.getTime(), n = E.duration || 0;
|
||||
E.end && E.duration && t + 1e3 * n !== E.end.getTime() && r("END-DATE MUST be equal to the value of the START-DATE attribute plus the value of the DURATION"), E.endOnNext && (E.end = e.get(E.classId)), e.set(E.classId, E.start);
|
||||
const a = E.end ? E.end.getTime() : E.start.getTime() + 1e3 * (E.duration || 0), o = s.get(E.classId);
|
||||
if (o) {
|
||||
for (const e of o) (e.start <= t && e.end > t || e.start >= t && e.start < a) && r("DATERANGE tags with the same CLASS should not overlap");
|
||||
o.push({start: t, end: a})
|
||||
} else E.classId && s.set(E.classId, [{start: t, end: a}])
|
||||
}
|
||||
}
|
||||
i && !n && r("If a Playlist contains an EXT-X-DATERANGE tag, it MUST also contain at least one EXT-X-PROGRAM-DATE-TIME tag.")
|
||||
}(i.segments), i.lowLatencyCompatibility && function ({
|
||||
lowLatencyCompatibility: t,
|
||||
targetDuration: e,
|
||||
partTargetDuration: s,
|
||||
segments: i,
|
||||
renditionReports: n
|
||||
}, a) {
|
||||
const {canSkipUntil: o, holdBack: E, partHoldBack: T} = t;
|
||||
o < 6 * e && r("The Skip Boundary must be at least six times the EXT-X-TARGETDURATION.");
|
||||
E < 3 * e && r("HOLD-BACK must be at least three times the EXT-X-TARGETDURATION.");
|
||||
if (a) {
|
||||
void 0 === s && r("EXT-X-PART-INF is required if a Playlist contains one or more EXT-X-PART tags"), void 0 === T && r("EXT-X-PART: PART-HOLD-BACK attribute is mandatory"), T < s && r("PART-HOLD-BACK must be at least PART-TARGET");
|
||||
for (const [t, {parts: e}] of i.entries()) {
|
||||
e.length > 0 && t < i.length - 3 && r("Remove EXT-X-PART tags from the Playlist after they are greater than three target durations from the end of the Playlist.");
|
||||
for (const [t, {duration: i}] of e.entries()) void 0 !== i && (i > s && r("PART-TARGET is the maximum duration of any Partial Segment"), t < e.length - 1 && i < .85 * s && r("All Partial Segments except the last part of a segment must have a duration of at least 85% of PART-TARGET"))
|
||||
}
|
||||
}
|
||||
for (const t of n) {
|
||||
const e = i.at(-1);
|
||||
null !== t.lastMSN && void 0 !== t.lastMSN || (t.lastMSN = e.mediaSequenceNumber), (null === t.lastPart || void 0 === t.lastPart) && e.parts.length > 0 && (t.lastPart = e.parts.length - 1)
|
||||
}
|
||||
}(i, l), i
|
||||
}
|
||||
|
||||
function x(t, e, s, i, n) {
|
||||
const {discontinuity: a, key: o, map: E, byterange: T, uri: u} = e;
|
||||
if (a && (e.discontinuitySequence = s + 1), o || (e.key = i), E || (e.map = n), T && -1 === T.offset) {
|
||||
const {segments: e} = t;
|
||||
if (e.length > 0) {
|
||||
const t = p(e, -1);
|
||||
t.byterange && t.uri === u ? T.offset = t.byterange.offset + t.byterange.length : r("If offset of EXT-X-BYTERANGE is not present, a previous Media Segment MUST be a sub-range of the same media resource")
|
||||
} else r("If offset of EXT-X-BYTERANGE is not present, a previous Media Segment MUST appear in the Playlist file")
|
||||
}
|
||||
return t.segments.push(e), [e.discontinuitySequence, e.key, e.map]
|
||||
}
|
||||
|
||||
function j(t, e) {
|
||||
const [s, i] = function (t) {
|
||||
const e = t.indexOf(":");
|
||||
return -1 === e ? [t.slice(1).trim(), null] : [t.slice(1, e).trim(), t.slice(e + 1).trim()]
|
||||
}(t), n = function (t) {
|
||||
switch (t) {
|
||||
case"EXTM3U":
|
||||
case"EXT-X-VERSION":
|
||||
return "Basic";
|
||||
case"EXTINF":
|
||||
case"EXT-X-BYTERANGE":
|
||||
case"EXT-X-DISCONTINUITY":
|
||||
case"EXT-X-PREFETCH-DISCONTINUITY":
|
||||
case"EXT-X-KEY":
|
||||
case"EXT-X-MAP":
|
||||
case"EXT-X-PROGRAM-DATE-TIME":
|
||||
case"EXT-X-DATERANGE":
|
||||
case"EXT-X-CUE-OUT":
|
||||
case"EXT-X-CUE-IN":
|
||||
case"EXT-X-CUE-OUT-CONT":
|
||||
case"EXT-X-CUE":
|
||||
case"EXT-OATCLS-SCTE35":
|
||||
case"EXT-X-ASSET":
|
||||
case"EXT-X-SCTE35":
|
||||
case"EXT-X-PART":
|
||||
case"EXT-X-PRELOAD-HINT":
|
||||
return "Segment";
|
||||
case"EXT-X-TARGETDURATION":
|
||||
case"EXT-X-MEDIA-SEQUENCE":
|
||||
case"EXT-X-DISCONTINUITY-SEQUENCE":
|
||||
case"EXT-X-ENDLIST":
|
||||
case"EXT-X-PLAYLIST-TYPE":
|
||||
case"EXT-X-I-FRAMES-ONLY":
|
||||
case"EXT-X-SERVER-CONTROL":
|
||||
case"EXT-X-PART-INF":
|
||||
case"EXT-X-PREFETCH":
|
||||
case"EXT-X-RENDITION-REPORT":
|
||||
case"EXT-X-SKIP":
|
||||
return "MediaPlaylist";
|
||||
case"EXT-X-MEDIA":
|
||||
case"EXT-X-STREAM-INF":
|
||||
case"EXT-X-I-FRAME-STREAM-INF":
|
||||
case"EXT-X-SESSION-DATA":
|
||||
case"EXT-X-SESSION-KEY":
|
||||
return "MasterPlaylist";
|
||||
case"EXT-X-INDEPENDENT-SEGMENTS":
|
||||
case"EXT-X-START":
|
||||
return "MediaorMasterPlaylist";
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}(s);
|
||||
if (function (t, e) {
|
||||
if ("Segment" === t || "MediaPlaylist" === t) return void 0 === e.isMasterPlaylist ? void (e.isMasterPlaylist = !1) : void (e.isMasterPlaylist && w());
|
||||
if ("MasterPlaylist" === t) {
|
||||
if (void 0 === e.isMasterPlaylist) return void (e.isMasterPlaylist = !0);
|
||||
!1 === e.isMasterPlaylist && w()
|
||||
}
|
||||
}(n, e), "Unknown" === n) return null;
|
||||
"MediaPlaylist" === n && "EXT-X-RENDITION-REPORT" !== s && "EXT-X-PREFETCH" !== s && (e.hash[s] && r("There MUST NOT be more than one Media Playlist tag of each type in any Media Playlist"), e.hash[s] = !0);
|
||||
const [a, E] = function (t, e) {
|
||||
switch (t) {
|
||||
case"EXTM3U":
|
||||
case"EXT-X-DISCONTINUITY":
|
||||
case"EXT-X-ENDLIST":
|
||||
case"EXT-X-I-FRAMES-ONLY":
|
||||
case"EXT-X-INDEPENDENT-SEGMENTS":
|
||||
case"EXT-X-CUE-IN":
|
||||
return [null, null];
|
||||
case"EXT-X-VERSION":
|
||||
case"EXT-X-TARGETDURATION":
|
||||
case"EXT-X-MEDIA-SEQUENCE":
|
||||
case"EXT-X-DISCONTINUITY-SEQUENCE":
|
||||
return [o(e), null];
|
||||
case"EXT-X-CUE-OUT":
|
||||
return Number.isNaN(Number(e)) ? [null, V(e)] : [o(e), null];
|
||||
case"EXT-X-KEY":
|
||||
case"EXT-X-MAP":
|
||||
case"EXT-X-DATERANGE":
|
||||
case"EXT-X-MEDIA":
|
||||
case"EXT-X-STREAM-INF":
|
||||
case"EXT-X-I-FRAME-STREAM-INF":
|
||||
case"EXT-X-SESSION-DATA":
|
||||
case"EXT-X-SESSION-KEY":
|
||||
case"EXT-X-START":
|
||||
case"EXT-X-SERVER-CONTROL":
|
||||
case"EXT-X-PART-INF":
|
||||
case"EXT-X-PART":
|
||||
case"EXT-X-PRELOAD-HINT":
|
||||
case"EXT-X-RENDITION-REPORT":
|
||||
case"EXT-X-SKIP":
|
||||
return [null, V(e)];
|
||||
case"EXTINF":
|
||||
return [L(e), null];
|
||||
case"EXT-X-BYTERANGE":
|
||||
return [v(e), null];
|
||||
case"EXT-X-PROGRAM-DATE-TIME":
|
||||
return [new Date(e), null];
|
||||
default:
|
||||
return [e, null]
|
||||
}
|
||||
}(s, i);
|
||||
return {name: s, category: n, value: a, attributes: E}
|
||||
}
|
||||
|
||||
function Q(t, e) {
|
||||
let s;
|
||||
return e.isMasterPlaylist ? s = function (t, e) {
|
||||
const s = new O;
|
||||
let i = !1;
|
||||
for (const [n, {
|
||||
name: a,
|
||||
value: o,
|
||||
attributes: E
|
||||
}] of t.entries()) if ("EXT-X-VERSION" === a) s.version = o; else if ("EXT-X-STREAM-INF" === a) {
|
||||
const a = t[n + 1];
|
||||
("string" != typeof a || a.startsWith("#EXT")) && r("EXT-X-STREAM-INF must be followed by a URI line");
|
||||
const o = H(t, E, a, !1, e);
|
||||
o && ("number" == typeof o.score && (i = !0, o.score < 0 && r("SCORE attribute on EXT-X-STREAM-INF must be positive decimal-floating-point number.")), s.variants.push(o))
|
||||
} else if ("EXT-X-I-FRAME-STREAM-INF" === a) {
|
||||
const i = H(t, E, E.URI, !0, e);
|
||||
i && s.variants.push(i)
|
||||
} else if ("EXT-X-SESSION-DATA" === a) {
|
||||
const t = new d({id: E["DATA-ID"], value: E.VALUE, uri: E.URI, language: E.LANGUAGE});
|
||||
s.sessionDataList.some((e => e.id === t.id && e.language === t.language)) && r("A Playlist MUST NOT contain more than one EXT-X-SESSION-DATA tag with the same DATA-ID attribute and the same LANGUAGE attribute."), s.sessionDataList.push(t)
|
||||
} else if ("EXT-X-SESSION-KEY" === a) {
|
||||
"NONE" === E.METHOD && r("EXT-X-SESSION-KEY: The value of the METHOD attribute MUST NOT be NONE");
|
||||
const t = new A({
|
||||
method: E.METHOD,
|
||||
uri: E.URI,
|
||||
iv: E.IV,
|
||||
format: E.KEYFORMAT,
|
||||
formatVersion: E.KEYFORMATVERSIONS
|
||||
});
|
||||
s.sessionKeyList.some((e => K(e, t))) && r("A Master Playlist MUST NOT contain more than one EXT-X-SESSION-KEY tag with the same METHOD, URI, IV, KEYFORMAT, and KEYFORMATVERSIONS attribute values."), G(e, E), s.sessionKeyList.push(t)
|
||||
} else "EXT-X-INDEPENDENT-SEGMENTS" === a ? (s.independentSegments && r("EXT-X-INDEPENDENT-SEGMENTS tag MUST NOT appear more than once in a Playlist"), s.independentSegments = !0) : "EXT-X-START" === a && (s.start && r("EXT-X-START tag MUST NOT appear more than once in a Playlist"), "number" != typeof E["TIME-OFFSET"] && r("EXT-X-START: TIME-OFFSET attribute is REQUIRED"), s.start = {
|
||||
offset: E["TIME-OFFSET"],
|
||||
precise: E.PRECISE || !1
|
||||
});
|
||||
if (i) for (const t of s.variants) "number" != typeof t.score && r("If any Variant Stream contains the SCORE attribute, then all Variant Streams in the Master Playlist SHOULD have a SCORE attribute");
|
||||
if (e.isClosedCaptionsNone) for (const t of s.variants) t.closedCaptions.length > 0 && r("If there is a variant with CLOSED-CAPTIONS attribute of NONE, all EXT-X-STREAM-INF tags MUST have this attribute with a value of NONE");
|
||||
return s
|
||||
}(t, e) : (s = q(t, e), !s.isIFrame && e.hasMap && e.compatibleVersion < 6 && (e.compatibleVersion = 6)), e.compatibleVersion > 1 && (!s.version || s.version < e.compatibleVersion) && r(`EXT-X-VERSION needs to be ${e.compatibleVersion} or higher.`), s
|
||||
}
|
||||
|
||||
function _(t) {
|
||||
const e = {
|
||||
version: void 0,
|
||||
isMasterPlaylist: void 0,
|
||||
hasMap: !1,
|
||||
targetDuration: 0,
|
||||
compatibleVersion: 1,
|
||||
isClosedCaptionsNone: !1,
|
||||
hash: {}
|
||||
}, s = function (t, e) {
|
||||
const s = [];
|
||||
for (const i of t.split("\n")) {
|
||||
const t = i.trim();
|
||||
if (t) if (t.startsWith("#")) {
|
||||
if (t.startsWith("#EXT")) {
|
||||
const i = j(t, e);
|
||||
i && s.push(i)
|
||||
}
|
||||
} else s.push(t)
|
||||
}
|
||||
return 0 !== s.length && "EXTM3U" === s[0].name || r("The EXTM3U tag MUST be the first line."), s
|
||||
}(t, e), i = Q(s, e);
|
||||
return i.source = t, i
|
||||
}
|
||||
|
||||
const z = ["#EXTINF", "#EXT-X-BYTERANGE", "#EXT-X-DISCONTINUITY", "#EXT-X-STREAM-INF", "#EXT-X-CUE-OUT", "#EXT-X-CUE-IN", "#EXT-X-KEY", "#EXT-X-MAP"],
|
||||
Z = ["#EXT-X-MEDIA"];
|
||||
|
||||
class J extends Array {
|
||||
constructor(t) {
|
||||
super(), this.baseUri = t
|
||||
}
|
||||
|
||||
push(...t) {
|
||||
for (const e of t) if (e.startsWith("#")) if (z.some((t => e.startsWith(t)))) super.push(e); else {
|
||||
if (this.includes(e)) {
|
||||
if (Z.some((t => e.startsWith(t)))) continue;
|
||||
r(`Redundant item (${e})`)
|
||||
}
|
||||
super.push(e)
|
||||
} else super.push(e);
|
||||
return this.length
|
||||
}
|
||||
}
|
||||
|
||||
function tt(t, e) {
|
||||
let s = 1e3;
|
||||
e && (s = Math.pow(10, e));
|
||||
const i = Math.round(t * s) / s;
|
||||
return e ? i.toFixed(e) : i
|
||||
}
|
||||
|
||||
function et(t) {
|
||||
const e = [`DATA-ID="${t.id}"`];
|
||||
return t.language && e.push(`LANGUAGE="${t.language}"`), t.value ? e.push(`VALUE="${t.value}"`) : t.uri && e.push(`URI="${t.uri}"`), `#EXT-X-SESSION-DATA:${e.join(",")}`
|
||||
}
|
||||
|
||||
function st(t, e) {
|
||||
const s = e ? "#EXT-X-SESSION-KEY" : "#EXT-X-KEY", i = [`METHOD=${t.method}`];
|
||||
return t.uri && i.push(`URI="${t.uri}"`), t.iv && (16 !== t.iv.length && r("IV must be a 128-bit unsigned integer"), i.push(`IV=${T(t.iv)}`)), t.format && i.push(`KEYFORMAT="${t.format}"`), t.formatVersion && i.push(`KEYFORMATVERSIONS="${t.formatVersion}"`), `${s}:${i.join(",")}`
|
||||
}
|
||||
|
||||
function it(t, e) {
|
||||
const s = e.isIFrameOnly ? "#EXT-X-I-FRAME-STREAM-INF" : "#EXT-X-STREAM-INF", i = [`BANDWIDTH=${e.bandwidth}`];
|
||||
if (e.averageBandwidth && i.push(`AVERAGE-BANDWIDTH=${e.averageBandwidth}`), e.isIFrameOnly && i.push(`URI="${e.uri}"`), e.codecs && i.push(`CODECS="${e.codecs}"`), e.resolution && i.push(`RESOLUTION=${e.resolution.width}x${e.resolution.height}`), e.frameRate && i.push(`FRAME-RATE=${tt(e.frameRate, 3)}`), e.hdcpLevel && i.push(`HDCP-LEVEL=${e.hdcpLevel}`), e.audio.length > 0) {
|
||||
i.push(`AUDIO="${e.audio[0].groupId}"`);
|
||||
for (const s of e.audio) t.push(nt(s))
|
||||
}
|
||||
if (e.video.length > 0) {
|
||||
i.push(`VIDEO="${e.video[0].groupId}"`);
|
||||
for (const s of e.video) t.push(nt(s))
|
||||
}
|
||||
if (e.subtitles.length > 0) {
|
||||
i.push(`SUBTITLES="${e.subtitles[0].groupId}"`);
|
||||
for (const s of e.subtitles) t.push(nt(s))
|
||||
}
|
||||
if (X().allowClosedCaptionsNone && 0 === e.closedCaptions.length) i.push("CLOSED-CAPTIONS=NONE"); else if (e.closedCaptions.length > 0) {
|
||||
i.push(`CLOSED-CAPTIONS="${e.closedCaptions[0].groupId}"`);
|
||||
for (const s of e.closedCaptions) t.push(nt(s))
|
||||
}
|
||||
if (e.score && i.push(`SCORE=${e.score}`), e.allowedCpc) {
|
||||
const t = [];
|
||||
for (const {format: s, cpcList: i} of e.allowedCpc) t.push(`${s}:${i.join("/")}`);
|
||||
i.push(`ALLOWED-CPC="${t.join(",")}"`)
|
||||
}
|
||||
e.videoRange && i.push(`VIDEO-RANGE=${e.videoRange}`), e.stableVariantId && i.push(`STABLE-VARIANT-ID="${e.stableVariantId}"`), e.programId && i.push(`PROGRAM-ID=${e.programId}`), t.push(`${s}:${i.join(",")}`), e.isIFrameOnly || t.push(`${e.uri}`)
|
||||
}
|
||||
|
||||
function nt(t) {
|
||||
const e = [`TYPE=${t.type}`, `GROUP-ID="${t.groupId}"`, `NAME="${t.name}"`];
|
||||
return void 0 !== t.isDefault && e.push("DEFAULT=" + (t.isDefault ? "YES" : "NO")), void 0 !== t.autoselect && e.push("AUTOSELECT=" + (t.autoselect ? "YES" : "NO")), void 0 !== t.forced && e.push("FORCED=" + (t.forced ? "YES" : "NO")), t.language && e.push(`LANGUAGE="${t.language}"`), t.assocLanguage && e.push(`ASSOC-LANGUAGE="${t.assocLanguage}"`), t.instreamId && e.push(`INSTREAM-ID="${t.instreamId}"`), t.characteristics && e.push(`CHARACTERISTICS="${t.characteristics}"`), t.channels && e.push(`CHANNELS="${t.channels}"`), t.uri && e.push(`URI="${t.uri}"`), `#EXT-X-MEDIA:${e.join(",")}`
|
||||
}
|
||||
|
||||
function at(t, e, s, i, n = 1, a = null) {
|
||||
let r = !1, o = "";
|
||||
if (e.discontinuity && t.push("#EXT-X-DISCONTINUITY"), e.key) {
|
||||
const i = st(e.key);
|
||||
i !== s && (t.push(i), s = i)
|
||||
}
|
||||
if (e.map) {
|
||||
const s = function (t) {
|
||||
const e = [`URI="${t.uri}"`];
|
||||
t.byterange && e.push(`BYTERANGE="${rt(t.byterange)}"`);
|
||||
return `#EXT-X-MAP:${e.join(",")}`
|
||||
}(e.map);
|
||||
s !== i && (t.push(s), i = s)
|
||||
}
|
||||
if (e.programDateTime && t.push(`#EXT-X-PROGRAM-DATE-TIME:${l(e.programDateTime)}`), e.dateRange && t.push(function (t) {
|
||||
const e = [`ID="${t.id}"`];
|
||||
t.start && e.push(`START-DATE="${l(t.start)}"`);
|
||||
t.end && e.push(`END-DATE="${l(t.end)}"`);
|
||||
t.duration && e.push(`DURATION=${t.duration}`);
|
||||
t.plannedDuration && e.push(`PLANNED-DURATION=${t.plannedDuration}`);
|
||||
t.classId && e.push(`CLASS="${t.classId}"`);
|
||||
t.endOnNext && e.push("END-ON-NEXT=YES");
|
||||
for (const s of Object.keys(t.attributes)) s.startsWith("X-") ? "number" == typeof t.attributes[s] ? e.push(`${s}=${t.attributes[s]}`) : e.push(`${s}="${t.attributes[s]}"`) : s.startsWith("SCTE35-") && e.push(`${s}=${T(t.attributes[s])}`);
|
||||
return `#EXT-X-DATERANGE:${e.join(",")}`
|
||||
}(e.dateRange)), e.markers.length > 0 && (o = function (t, e) {
|
||||
let s = "";
|
||||
for (const i of e) if ("OUT" === i.type) s = "OUT", t.push(`#EXT-X-CUE-OUT:DURATION=${i.duration}`); else if ("IN" === i.type) s = "IN", t.push("#EXT-X-CUE-IN"); else if ("RAW" === i.type) {
|
||||
const e = i.value ? `:${i.value}` : "";
|
||||
t.push(`#${i.tagName}${e}`)
|
||||
}
|
||||
return s
|
||||
}(t, e.markers)), e.parts.length > 0 && (r = function (t, e) {
|
||||
let s = !1;
|
||||
for (const i of e) if (i.hint) {
|
||||
const e = [];
|
||||
if (e.push("TYPE=PART", `URI="${i.uri}"`), i.byterange) {
|
||||
const {offset: t, length: s} = i.byterange;
|
||||
e.push(`BYTERANGE-START=${t}`), s && e.push(`BYTERANGE-LENGTH=${s}`)
|
||||
}
|
||||
t.push(`#EXT-X-PRELOAD-HINT:${e.join(",")}`), s = !0
|
||||
} else {
|
||||
const e = [];
|
||||
e.push(`DURATION=${i.duration}`, `URI="${i.uri}"`), i.byterange && e.push(`BYTERANGE=${rt(i.byterange)}`), i.independent && e.push("INDEPENDENT=YES"), i.gap && e.push("GAP=YES"), t.push(`#EXT-X-PART:${e.join(",")}`)
|
||||
}
|
||||
return s
|
||||
}(t, e.parts)), r) return [s, i];
|
||||
const E = n < 3 ? Math.round(e.duration) : tt(e.duration, function (t) {
|
||||
const e = t.toString(10), s = e.indexOf(".");
|
||||
return -1 === s ? 0 : e.length - s - 1
|
||||
}(e.duration));
|
||||
return t.push(`#EXTINF:${E},${unescape(encodeURIComponent(e.title || ""))}`), e.byterange && t.push(`#EXT-X-BYTERANGE:${rt(e.byterange)}`), null != a ? Array.prototype.push.call(t, a(e)) : Array.prototype.push.call(t, `${e.uri}`), [s, i, o]
|
||||
}
|
||||
|
||||
function rt({offset: t, length: e}) {
|
||||
return `${e}@${t}`
|
||||
}
|
||||
|
||||
function ot(t, e = null) {
|
||||
n(t), s("Not a playlist", "playlist" === t.type);
|
||||
const i = new J(t.uri);
|
||||
return i.push("#EXTM3U"), t.version && i.push(`#EXT-X-VERSION:${t.version}`), t.independentSegments && i.push("#EXT-X-INDEPENDENT-SEGMENTS"), t.start && i.push(`#EXT-X-START:TIME-OFFSET=${tt(t.start.offset)}${t.start.precise ? ",PRECISE=YES" : ""}`), t.isMasterPlaylist ? function (t, e) {
|
||||
for (const s of e.sessionDataList) t.push(et(s));
|
||||
for (const s of e.sessionKeyList) t.push(st(s, !0));
|
||||
for (const s of e.variants) it(t, s)
|
||||
}(i, t) : function (t, e, s = null) {
|
||||
let i = "", n = "", a = !1;
|
||||
if (e.targetDuration && t.push(`#EXT-X-TARGETDURATION:${e.targetDuration}`), e.lowLatencyCompatibility) {
|
||||
const {canBlockReload: s, canSkipUntil: i, holdBack: n, partHoldBack: a} = e.lowLatencyCompatibility,
|
||||
r = [];
|
||||
r.push("CAN-BLOCK-RELOAD=" + (s ? "YES" : "NO")), void 0 !== i && r.push(`CAN-SKIP-UNTIL=${i}`), void 0 !== n && r.push(`HOLD-BACK=${n}`), void 0 !== a && r.push(`PART-HOLD-BACK=${a}`), t.push(`#EXT-X-SERVER-CONTROL:${r.join(",")}`)
|
||||
}
|
||||
e.partTargetDuration && t.push(`#EXT-X-PART-INF:PART-TARGET=${e.partTargetDuration}`), e.mediaSequenceBase && t.push(`#EXT-X-MEDIA-SEQUENCE:${e.mediaSequenceBase}`), e.discontinuitySequenceBase && t.push(`#EXT-X-DISCONTINUITY-SEQUENCE:${e.discontinuitySequenceBase}`), e.playlistType && t.push(`#EXT-X-PLAYLIST-TYPE:${e.playlistType}`), e.isIFrame && t.push("#EXT-X-I-FRAMES-ONLY"), e.skip > 0 && t.push(`#EXT-X-SKIP:SKIPPED-SEGMENTS=${e.skip}`);
|
||||
for (const r of e.segments) {
|
||||
let o = "";
|
||||
[i, n, o] = at(t, r, i, n, e.version, s), "OUT" === o ? a = !0 : "IN" === o && a && (a = !1)
|
||||
}
|
||||
"VOD" === e.playlistType && a && t.push("#EXT-X-CUE-IN"), e.prefetchSegments.length > 2 && r("The server must deliver no more than two prefetch segments");
|
||||
for (const s of e.prefetchSegments) s.discontinuity && t.push("#EXT-X-PREFETCH-DISCONTINUITY"), t.push(`#EXT-X-PREFETCH:${s.uri}`);
|
||||
e.endlist && t.push("#EXT-X-ENDLIST");
|
||||
for (const s of e.renditionReports) {
|
||||
const e = [];
|
||||
e.push(`URI="${s.uri}"`, `LAST-MSN=${s.lastMSN}`), void 0 !== s.lastPart && e.push(`LAST-PART=${s.lastPart}`), t.push(`#EXT-X-RENDITION-REPORT:${e.join(",")}`)
|
||||
}
|
||||
}(i, t, e), i.join("\n")
|
||||
}
|
||||
|
||||
export {X as getOptions, _ as parse, h as setOptions, ot as stringify, M as types};
|
||||
441
cat/tjs/lib/iso-2022-jp.js
Normal file
@ -0,0 +1,441 @@
|
||||
import { inRange, decoderError, encoderError, isASCIICodePoint,
|
||||
end_of_stream, finished, floor } from './text_decoder_utils.js'
|
||||
import index, { indexCodePointFor, indexPointerFor } from './text_decoder_indexes.js'
|
||||
|
||||
// 13.2 iso-2022-jp
|
||||
|
||||
// 13.2.1 iso-2022-jp decoder
|
||||
/**
|
||||
* @implements {Decoder}
|
||||
*/
|
||||
export class ISO2022JPDecoder {
|
||||
constructor(options) {
|
||||
const { fatal } = options
|
||||
this.fatal = fatal
|
||||
/** @enum */
|
||||
this.states = {
|
||||
ASCII: 0,
|
||||
Roman: 1,
|
||||
Katakana: 2,
|
||||
LeadByte: 3,
|
||||
TrailByte: 4,
|
||||
EscapeStart: 5,
|
||||
Escape: 6,
|
||||
}
|
||||
// iso-2022-jp's decoder has an associated iso-2022-jp decoder
|
||||
// state (initially ASCII), iso-2022-jp decoder output state
|
||||
// (initially ASCII), iso-2022-jp lead (initially 0x00), and
|
||||
// iso-2022-jp output flag (initially unset).
|
||||
this.iso2022jp_decoder_state = this.states.ASCII
|
||||
this.iso2022jp_decoder_output_state = this.states.ASCII,
|
||||
this.iso2022jp_lead = 0x00
|
||||
this.iso2022jp_output_flag = false
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// switching on iso-2022-jp decoder state:
|
||||
switch (this.iso2022jp_decoder_state) {
|
||||
default:
|
||||
case this.states.ASCII:
|
||||
// ASCII
|
||||
// Based on byte:
|
||||
|
||||
// 0x1B
|
||||
if (bite === 0x1B) {
|
||||
// Set iso-2022-jp decoder state to escape start and return
|
||||
// continue.
|
||||
this.iso2022jp_decoder_state = this.states.EscapeStart
|
||||
return null
|
||||
}
|
||||
|
||||
// 0x00 to 0x7F, excluding 0x0E, 0x0F, and 0x1B
|
||||
if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E
|
||||
&& bite !== 0x0F && bite !== 0x1B) {
|
||||
// Unset the iso-2022-jp output flag and return a code point
|
||||
// whose value is byte.
|
||||
this.iso2022jp_output_flag = false
|
||||
return bite
|
||||
}
|
||||
|
||||
// end-of-stream
|
||||
if (bite === end_of_stream) {
|
||||
// Return finished.
|
||||
return finished
|
||||
}
|
||||
|
||||
// Otherwise
|
||||
// Unset the iso-2022-jp output flag and return error.
|
||||
this.iso2022jp_output_flag = false
|
||||
return decoderError(this.fatal)
|
||||
|
||||
case this.states.Roman:
|
||||
// Roman
|
||||
// Based on byte:
|
||||
|
||||
// 0x1B
|
||||
if (bite === 0x1B) {
|
||||
// Set iso-2022-jp decoder state to escape start and return
|
||||
// continue.
|
||||
this.iso2022jp_decoder_state = this.states.EscapeStart
|
||||
return null
|
||||
}
|
||||
|
||||
// 0x5C
|
||||
if (bite === 0x5C) {
|
||||
// Unset the iso-2022-jp output flag and return code point
|
||||
// U+00A5.
|
||||
this.iso2022jp_output_flag = false
|
||||
return 0x00A5
|
||||
}
|
||||
|
||||
// 0x7E
|
||||
if (bite === 0x7E) {
|
||||
// Unset the iso-2022-jp output flag and return code point
|
||||
// U+203E.
|
||||
this.iso2022jp_output_flag = false
|
||||
return 0x203E
|
||||
}
|
||||
|
||||
// 0x00 to 0x7F, excluding 0x0E, 0x0F, 0x1B, 0x5C, and 0x7E
|
||||
if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E && bite !== 0x0F
|
||||
&& bite !== 0x1B && bite !== 0x5C && bite !== 0x7E) {
|
||||
// Unset the iso-2022-jp output flag and return a code point
|
||||
// whose value is byte.
|
||||
this.iso2022jp_output_flag = false
|
||||
return bite
|
||||
}
|
||||
|
||||
// end-of-stream
|
||||
if (bite === end_of_stream) {
|
||||
// Return finished.
|
||||
return finished
|
||||
}
|
||||
|
||||
// Otherwise
|
||||
// Unset the iso-2022-jp output flag and return error.
|
||||
this.iso2022jp_output_flag = false
|
||||
return decoderError(this.fatal)
|
||||
|
||||
case this.states.Katakana:
|
||||
// Katakana
|
||||
// Based on byte:
|
||||
|
||||
// 0x1B
|
||||
if (bite === 0x1B) {
|
||||
// Set iso-2022-jp decoder state to escape start and return
|
||||
// continue.
|
||||
this.iso2022jp_decoder_state = this.states.EscapeStart
|
||||
return null
|
||||
}
|
||||
|
||||
// 0x21 to 0x5F
|
||||
if (inRange(bite, 0x21, 0x5F)) {
|
||||
// Unset the iso-2022-jp output flag and return a code point
|
||||
// whose value is 0xFF61 − 0x21 + byte.
|
||||
this.iso2022jp_output_flag = false
|
||||
return 0xFF61 - 0x21 + bite
|
||||
}
|
||||
|
||||
// end-of-stream
|
||||
if (bite === end_of_stream) {
|
||||
// Return finished.
|
||||
return finished
|
||||
}
|
||||
|
||||
// Otherwise
|
||||
// Unset the iso-2022-jp output flag and return error.
|
||||
this.iso2022jp_output_flag = false
|
||||
return decoderError(this.fatal)
|
||||
|
||||
case this.states.LeadByte:
|
||||
// Lead byte
|
||||
// Based on byte:
|
||||
|
||||
// 0x1B
|
||||
if (bite === 0x1B) {
|
||||
// Set iso-2022-jp decoder state to escape start and return
|
||||
// continue.
|
||||
this.iso2022jp_decoder_state = this.states.EscapeStart
|
||||
return null
|
||||
}
|
||||
|
||||
// 0x21 to 0x7E
|
||||
if (inRange(bite, 0x21, 0x7E)) {
|
||||
// Unset the iso-2022-jp output flag, set iso-2022-jp lead
|
||||
// to byte, iso-2022-jp decoder state to trail byte, and
|
||||
// return continue.
|
||||
this.iso2022jp_output_flag = false
|
||||
this.iso2022jp_lead = bite
|
||||
this.iso2022jp_decoder_state = this.states.TrailByte
|
||||
return null
|
||||
}
|
||||
|
||||
// end-of-stream
|
||||
if (bite === end_of_stream) {
|
||||
// Return finished.
|
||||
return finished
|
||||
}
|
||||
|
||||
// Otherwise
|
||||
// Unset the iso-2022-jp output flag and return error.
|
||||
this.iso2022jp_output_flag = false
|
||||
return decoderError(this.fatal)
|
||||
|
||||
case this.states.TrailByte:
|
||||
// Trail byte
|
||||
// Based on byte:
|
||||
|
||||
// 0x1B
|
||||
if (bite === 0x1B) {
|
||||
// Set iso-2022-jp decoder state to escape start and return
|
||||
// continue.
|
||||
this.iso2022jp_decoder_state = this.states.EscapeStart
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 0x21 to 0x7E
|
||||
if (inRange(bite, 0x21, 0x7E)) {
|
||||
// 1. Set the iso-2022-jp decoder state to lead byte.
|
||||
this.iso2022jp_decoder_state = this.states.LeadByte
|
||||
|
||||
// 2. Let pointer be (iso-2022-jp lead − 0x21) × 94 + byte − 0x21.
|
||||
const pointer = (this.iso2022jp_lead - 0x21) * 94 + bite - 0x21
|
||||
|
||||
// 3. Let code point be the index code point for pointer in
|
||||
// index jis0208.
|
||||
const code_point = indexCodePointFor(pointer, index('jis0208'))
|
||||
|
||||
// 4. If code point is null, return error.
|
||||
if (code_point === null)
|
||||
return decoderError(this.fatal)
|
||||
|
||||
// 5. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
|
||||
// end-of-stream
|
||||
if (bite === end_of_stream) {
|
||||
// Set the iso-2022-jp decoder state to lead byte, prepend
|
||||
// byte to stream, and return error.
|
||||
this.iso2022jp_decoder_state = this.states.LeadByte
|
||||
stream.prepend(bite)
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// Otherwise
|
||||
// Set iso-2022-jp decoder state to lead byte and return
|
||||
// error.
|
||||
this.iso2022jp_decoder_state = this.states.LeadByte
|
||||
return decoderError(this.fatal)
|
||||
|
||||
case this.states.EscapeStart:
|
||||
// Escape start
|
||||
|
||||
// 1. If byte is either 0x24 or 0x28, set iso-2022-jp lead to
|
||||
// byte, iso-2022-jp decoder state to escape, and return
|
||||
// continue.
|
||||
if (bite === 0x24 || bite === 0x28) {
|
||||
this.iso2022jp_lead = bite
|
||||
this.iso2022jp_decoder_state = this.states.Escape
|
||||
return null
|
||||
}
|
||||
|
||||
// 2. Prepend byte to stream.
|
||||
stream.prepend(bite)
|
||||
|
||||
// 3. Unset the iso-2022-jp output flag, set iso-2022-jp
|
||||
// decoder state to iso-2022-jp decoder output state, and
|
||||
// return error.
|
||||
this.iso2022jp_output_flag = false
|
||||
this.iso2022jp_decoder_state = this.iso2022jp_decoder_output_state
|
||||
return decoderError(this.fatal)
|
||||
|
||||
case this.states.Escape: {
|
||||
// Escape
|
||||
|
||||
// 1. Let lead be iso-2022-jp lead and set iso-2022-jp lead to
|
||||
// 0x00.
|
||||
const lead = this.iso2022jp_lead
|
||||
this.iso2022jp_lead = 0x00
|
||||
|
||||
// 2. Let state be null.
|
||||
let state = null
|
||||
|
||||
// 3. If lead is 0x28 and byte is 0x42, set state to ASCII.
|
||||
if (lead === 0x28 && bite === 0x42)
|
||||
state = this.states.ASCII
|
||||
|
||||
// 4. If lead is 0x28 and byte is 0x4A, set state to Roman.
|
||||
if (lead === 0x28 && bite === 0x4A)
|
||||
state = this.states.Roman
|
||||
|
||||
// 5. If lead is 0x28 and byte is 0x49, set state to Katakana.
|
||||
if (lead === 0x28 && bite === 0x49)
|
||||
state = this.states.Katakana
|
||||
|
||||
// 6. If lead is 0x24 and byte is either 0x40 or 0x42, set
|
||||
// state to lead byte.
|
||||
if (lead === 0x24 && (bite === 0x40 || bite === 0x42))
|
||||
state = this.states.LeadByte
|
||||
|
||||
// 7. If state is non-null, run these substeps:
|
||||
if (state !== null) {
|
||||
// 1. Set iso-2022-jp decoder state and iso-2022-jp decoder
|
||||
// output state to this.states.
|
||||
this.iso2022jp_decoder_state = this.iso2022jp_decoder_state = state
|
||||
|
||||
// 2. Let output flag be the iso-2022-jp output flag.
|
||||
const output_flag = this.iso2022jp_output_flag
|
||||
|
||||
// 3. Set the iso-2022-jp output flag.
|
||||
this.iso2022jp_output_flag = true
|
||||
|
||||
// 4. Return continue, if output flag is unset, and error
|
||||
// otherwise.
|
||||
return !output_flag ? null : decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 8. Prepend lead and byte to stream.
|
||||
stream.prepend([lead, bite])
|
||||
|
||||
// 9. Unset the iso-2022-jp output flag, set iso-2022-jp
|
||||
// decoder state to iso-2022-jp decoder output state and
|
||||
// return error.
|
||||
this.iso2022jp_output_flag = false
|
||||
this.iso2022jp_decoder_state = this.iso2022jp_decoder_output_state
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 13.2.2 iso-2022-jp encoder
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class ISO2022JPEncoder {
|
||||
constructor() {
|
||||
// iso-2022-jp's encoder has an associated iso-2022-jp encoder
|
||||
// state which is one of ASCII, Roman, and jis0208 (initially
|
||||
// ASCII).
|
||||
/** @enum */
|
||||
this.states = {
|
||||
ASCII: 0,
|
||||
Roman: 1,
|
||||
jis0208: 2,
|
||||
}
|
||||
this.iso2022jp_state = this.states.ASCII
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
*/
|
||||
handler(stream, code_point) {
|
||||
// 1. If code point is end-of-stream and iso-2022-jp encoder
|
||||
// state is not ASCII, prepend code point to stream, set
|
||||
// iso-2022-jp encoder state to ASCII, and return three bytes
|
||||
// 0x1B 0x28 0x42.
|
||||
if (code_point === end_of_stream &&
|
||||
this.iso2022jp_state !== this.states.ASCII) {
|
||||
stream.prepend(code_point)
|
||||
this.iso2022jp_state = this.states.ASCII
|
||||
return [0x1B, 0x28, 0x42]
|
||||
}
|
||||
|
||||
// 2. If code point is end-of-stream and iso-2022-jp encoder
|
||||
// state is ASCII, return finished.
|
||||
if (code_point === end_of_stream && this.iso2022jp_state === this.states.ASCII)
|
||||
return finished
|
||||
|
||||
// 3. If ISO-2022-JP encoder state is ASCII or Roman, and code
|
||||
// point is U+000E, U+000F, or U+001B, return error with U+FFFD.
|
||||
if ((this.iso2022jp_state === this.states.ASCII ||
|
||||
this.iso2022jp_state === this.states.Roman) &&
|
||||
(code_point === 0x000E || code_point === 0x000F ||
|
||||
code_point === 0x001B)) {
|
||||
return encoderError(0xFFFD)
|
||||
}
|
||||
|
||||
// 4. If iso-2022-jp encoder state is ASCII and code point is an
|
||||
// ASCII code point, return a byte whose value is code point.
|
||||
if (this.iso2022jp_state === this.states.ASCII &&
|
||||
isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 5. If iso-2022-jp encoder state is Roman and code point is an
|
||||
// ASCII code point, excluding U+005C and U+007E, or is U+00A5
|
||||
// or U+203E, run these substeps:
|
||||
if (this.iso2022jp_state === this.states.Roman &&
|
||||
((isASCIICodePoint(code_point) &&
|
||||
code_point !== 0x005C && code_point !== 0x007E) ||
|
||||
(code_point == 0x00A5 || code_point == 0x203E))) {
|
||||
// 1. If code point is an ASCII code point, return a byte
|
||||
// whose value is code point.
|
||||
if (isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 2. If code point is U+00A5, return byte 0x5C.
|
||||
if (code_point === 0x00A5)
|
||||
return 0x5C
|
||||
|
||||
// 3. If code point is U+203E, return byte 0x7E.
|
||||
if (code_point === 0x203E)
|
||||
return 0x7E
|
||||
}
|
||||
|
||||
// 6. If code point is an ASCII code point, and iso-2022-jp
|
||||
// encoder state is not ASCII, prepend code point to stream, set
|
||||
// iso-2022-jp encoder state to ASCII, and return three bytes
|
||||
// 0x1B 0x28 0x42.
|
||||
if (isASCIICodePoint(code_point) &&
|
||||
this.iso2022jp_state !== this.states.ASCII) {
|
||||
stream.prepend(code_point)
|
||||
this.iso2022jp_state = this.states.ASCII
|
||||
return [0x1B, 0x28, 0x42]
|
||||
}
|
||||
|
||||
// 7. If code point is either U+00A5 or U+203E, and iso-2022-jp
|
||||
// encoder state is not Roman, prepend code point to stream, set
|
||||
// iso-2022-jp encoder state to Roman, and return three bytes
|
||||
// 0x1B 0x28 0x4A.
|
||||
if ((code_point === 0x00A5 || code_point === 0x203E) &&
|
||||
this.iso2022jp_state !== this.states.Roman) {
|
||||
stream.prepend(code_point)
|
||||
this.iso2022jp_state = this.states.Roman
|
||||
return [0x1B, 0x28, 0x4A]
|
||||
}
|
||||
|
||||
// 8. If code point is U+2212, set it to U+FF0D.
|
||||
if (code_point === 0x2212)
|
||||
code_point = 0xFF0D
|
||||
|
||||
// 9. Let pointer be the index pointer for code point in index
|
||||
// jis0208.
|
||||
const pointer = indexPointerFor(code_point, index('jis0208'))
|
||||
|
||||
// 10. If pointer is null, return error with code point.
|
||||
if (pointer === null)
|
||||
return encoderError(code_point)
|
||||
|
||||
// 11. If iso-2022-jp encoder state is not jis0208, prepend code
|
||||
// point to stream, set iso-2022-jp encoder state to jis0208,
|
||||
// and return three bytes 0x1B 0x24 0x42.
|
||||
if (this.iso2022jp_state !== this.states.jis0208) {
|
||||
stream.prepend(code_point)
|
||||
this.iso2022jp_state = this.states.jis0208
|
||||
return [0x1B, 0x24, 0x42]
|
||||
}
|
||||
|
||||
// 12. Let lead be floor(pointer / 94) + 0x21.
|
||||
const lead = floor(pointer / 94) + 0x21
|
||||
|
||||
// 13. Let trail be pointer % 94 + 0x21.
|
||||
const trail = pointer % 94 + 0x21
|
||||
|
||||
// 14. Return two bytes whose values are lead and trail.
|
||||
return [lead, trail]
|
||||
}
|
||||
}
|
||||
86
cat/tjs/lib/log.js
Normal file
@ -0,0 +1,86 @@
|
||||
const level_list = ["DEBUG", "INFO", "WARNING", "ERROR"];
|
||||
const file_path = "log"
|
||||
|
||||
class JadeLogging {
|
||||
|
||||
constructor(app_name, level = "DEBUG") {
|
||||
this.app_name = app_name
|
||||
this.level = level
|
||||
this.level_index = level_list.indexOf(level)
|
||||
}
|
||||
|
||||
|
||||
format(level, message) {
|
||||
let max_format = 80
|
||||
switch (level) {
|
||||
case "INFO":
|
||||
max_format = max_format + 1
|
||||
break
|
||||
case "WARNING":
|
||||
max_format = max_format - 2
|
||||
break
|
||||
default :
|
||||
break
|
||||
}
|
||||
if (message.length < max_format) {
|
||||
if ((max_format - message.length) % 2 === 0) {
|
||||
message = "#".repeat(Math.floor((max_format - message.length) / 2)) + message + "#".repeat(Math.floor((max_format - message.length) / 2))
|
||||
} else {
|
||||
message = "#".repeat(Math.floor((max_format - message.length) / 2)) + message + "#".repeat(Math.floor((max_format - message.length) / 2) + 1)
|
||||
}
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
getTime() {
|
||||
const timestamp = new Date();
|
||||
// 获取当前时间戳
|
||||
return timestamp.toLocaleDateString().replace(/\//g, "-") + " " + timestamp.toTimeString().substr(0, 8) + "," + timestamp.getMilliseconds().toString()
|
||||
}
|
||||
|
||||
formatMessage(log_level, message, is_format) {
|
||||
// 获取北京时间
|
||||
// 格式化消息
|
||||
//2023-12-13 15:15:21,409 - 阿里玩偶 -
|
||||
//2023-12-14T01:43:31.278Z
|
||||
//2023-12-13 15:15:21,409 - 阿里玩偶 - INFO:
|
||||
//2023-12-13 15:15:21,409 - 阿里玩偶 - ERROR:
|
||||
if (is_format) {
|
||||
message = this.format(log_level, message)
|
||||
}
|
||||
return `${this.getTime()} - ${this.app_name} - ${log_level}: ${message}`
|
||||
|
||||
}
|
||||
|
||||
async log(message) {
|
||||
console.debug(message)
|
||||
await local.set(file_path,this.getTime(), message);
|
||||
}
|
||||
|
||||
async info(message, is_format=false) {
|
||||
if (this.level_index <= 1) {
|
||||
await this.log(this.formatMessage("INFO", message, is_format))
|
||||
}
|
||||
}
|
||||
|
||||
async warning(message, is_format=false) {
|
||||
if (this.level_index <= 2) {
|
||||
await this.log(this.formatMessage("WARNING", message, is_format))
|
||||
}
|
||||
}
|
||||
|
||||
async error(message, is_format=false) {
|
||||
if (this.level_index <= 3) {
|
||||
await this.log(this.formatMessage("ERROR", message, is_format))
|
||||
}
|
||||
}
|
||||
|
||||
async debug(message, is_format=false) {
|
||||
if (this.level_index <= 0) {
|
||||
await this.log(this.formatMessage("DEBUG", message, is_format))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 测试日志记录函数
|
||||
export {JadeLogging}
|
||||
129
cat/tjs/lib/misc.js
Normal file
@ -0,0 +1,129 @@
|
||||
var charStr = 'abacdefghjklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789';
|
||||
|
||||
export function rand(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
export function randStr(len, withNum, onlyNum) {
|
||||
var _str = '';
|
||||
let containsNum = withNum === undefined ? true : withNum;
|
||||
for (var i = 0; i < len; i++) {
|
||||
let idx = onlyNum ? rand(charStr.length - 10, charStr.length - 1) : rand(0, containsNum ? charStr.length - 1 : charStr.length - 11);
|
||||
_str += charStr[idx];
|
||||
}
|
||||
return _str;
|
||||
}
|
||||
|
||||
export function randUUID() {
|
||||
return randStr(8).toLowerCase() + '-' + randStr(4).toLowerCase() + '-' + randStr(4).toLowerCase() + '-' + randStr(4).toLowerCase() + '-' + randStr(12).toLowerCase();
|
||||
}
|
||||
|
||||
export function randMAC() {
|
||||
return randStr(2).toUpperCase() + ':' + randStr(2).toUpperCase() + ':' + randStr(2).toUpperCase() + ':' + randStr(2).toUpperCase() + ':' + randStr(2).toUpperCase() + ':' + randStr(2).toUpperCase();
|
||||
}
|
||||
|
||||
const deviceBrands = ['Huawei', 'Xiaomi'];
|
||||
const deviceModels = [
|
||||
['MHA-AL00', 'HUAWEI Mate 9', 'MHA-TL00', 'HUAWEI Mate 9', 'LON-AL00', 'HUAWEI Mate 9 Pro', 'ALP-AL00', 'HUAWEI Mate 10', 'ALP-TL00', 'HUAWEI Mate 10', 'BLA-AL00', 'HUAWEI Mate 10 Pro', 'BLA-TL00', 'HUAWEI Mate 10 Pro', 'HMA-AL00', 'HUAWEI Mate 20', 'HMA-TL00', 'HUAWEI Mate 20', 'LYA-AL00', 'HUAWEI Mate 20 Pro', 'LYA-AL10', 'HUAWEI Mate 20 Pro', 'LYA-TL00', 'HUAWEI Mate 20 Pro', 'EVR-AL00', 'HUAWEI Mate 20 X', 'EVR-TL00', 'HUAWEI Mate 20 X', 'EVR-AN00', 'HUAWEI Mate 20 X', 'TAS-AL00', 'HUAWEI Mate 30', 'TAS-TL00', 'HUAWEI Mate 30', 'TAS-AN00', 'HUAWEI Mate 30', 'TAS-TN00', 'HUAWEI Mate 30', 'LIO-AL00', 'HUAWEI Mate 30 Pro', 'LIO-TL00', 'HUAWEI Mate 30 Pro', 'LIO-AN00', 'HUAWEI Mate 30 Pro', 'LIO-TN00', 'HUAWEI Mate 30 Pro', 'LIO-AN00m', 'HUAWEI Mate 30E Pro', 'OCE-AN10', 'HUAWEI Mate 40', 'OCE-AN50', 'HUAWEI Mate 40E', 'OCE-AL50', 'HUAWEI Mate 40E', 'NOH-AN00', 'HUAWEI Mate 40 Pro', 'NOH-AN01', 'HUAWEI Mate 40 Pro', 'NOH-AL00', 'HUAWEI Mate 40 Pro', 'NOH-AL10', 'HUAWEI Mate 40 Pro', 'NOH-AN50', 'HUAWEI Mate 40E Pro', 'NOP-AN00', 'HUAWEI Mate 40 Pro', 'CET-AL00', 'HUAWEI Mate 50', 'CET-AL60', 'HUAWEI Mate 50E', 'DCO-AL00', 'HUAWEI Mate 50 Pro', 'TAH-AN00', 'HUAWEI Mate X', 'TAH-AN00m', 'HUAWEI Mate Xs', 'TET-AN00', 'HUAWEI Mate X2', 'TET-AN10', 'HUAWEI Mate X2', 'TET-AN50', 'HUAWEI Mate X2', 'TET-AL00', 'HUAWEI Mate X2', 'PAL-AL00', 'HUAWEI Mate Xs 2', 'PAL-AL10', 'HUAWEI Mate Xs 2', 'EVA-AL00', 'HUAWEI P9', 'EVA-AL10', 'HUAWEI P9', 'EVA-TL00', 'HUAWEI P9', 'EVA-DL00', 'HUAWEI P9', 'EVA-CL00', 'HUAWEI P9', 'VIE-AL10', 'HUAWEI P9 Plus', 'VTR-AL00', 'HUAWEI P10', 'VTR-TL00', 'HUAWEI P10', 'VKY-AL00', 'HUAWEI P10 Plus', 'VKY-TL00', 'HUAWEI P10 Plus', 'EML-AL00', 'HUAWEI P20', 'EML-TL00', 'HUAWEI P20', 'CLT-AL00', 'HUAWEI P20 Pro', 'CLT-AL01', 'HUAWEI P20 Pro', 'CLT-AL00l', 'HUAWEI P20 Pro', 'CLT-TL00', 'HUAWEI P20 Pro', 'CLT-TL01', 'HUAWEI P20 Pro', 'ELE-AL00', 'HUAWEI P30', 'ELE-TL00', 'HUAWEI P30', 'VOG-AL00', 'HUAWEI P30 Pro', 'VOG-AL10', 'HUAWEI P30 Pro', 'VOG-TL00', 'HUAWEI P30 Pro', 'ANA-AL00', 'HUAWEI P40', 'ANA-AN00', 'HUAWEI P40', 'ANA-TN00', 'HUAWEI P40', 'ELS-AN00', 'HUAWEI P40 Pro', 'ELS-TN00', 'HUAWEI P40 Pro', 'ELS-AN10', 'HUAWEI P40 Pro', 'ELS-TN10', 'HUAWEI P40 Pro', 'ABR-AL00', 'HUAWEI P50', 'ABR-AL80', 'HUAWEI P50', 'ABR-AL60', 'HUAWEI P50E', 'ABR-AL90', 'HUAWEI P50E', 'JAD-AL00', 'HUAWEI P50 Pro', 'JAD-AL80', 'HUAWEI P50 Pro', 'JAD-AL50', 'HUAWEI P50 Pro', 'JAD-AL60', 'HUAWEI P50 Pro', 'BAL-AL00', 'HUAWEI P50 Pocket', 'BAL-AL60', 'HUAWEI Pocket S', 'PIC-AL00', 'HUAWEI nova 2', 'PIC-TL00', 'HUAWEI nova 2', 'BAC-AL00', 'HUAWEI nova 2 Plus', 'BAC-TL00', 'HUAWEI nova 2 Plus', 'HWI-AL00', 'HUAWEI nova 2s', 'HWI-TL00', 'HUAWEI nova 2s', 'ANE-AL00', 'HUAWEI nova 3e', 'ANE-TL00', 'HUAWEI nova 3e', 'PAR-AL00', 'HUAWEI nova 3', 'PAR-TL00', 'HUAWEI nova 3', 'INE-AL00', 'HUAWEI nova 3i', 'INE-TL00', 'HUAWEI nova 3i', 'VCE-AL00', 'HUAWEI nova 4', 'VCE-TL00', 'HUAWEI nova 4', 'MAR-AL00', 'HUAWEI nova 4e', 'MAR-TL00', 'HUAWEI nova 4e', 'SEA-AL00', 'HUAWEI nova 5', 'SEA-TL00', 'HUAWEI nova 5', 'SEA-AL10', 'HUAWEI nova 5 Pro', 'SEA-TL10', 'HUAWEI nova 5 Pro', 'GLK-AL00', 'HUAWEI nova 5i', 'GLK-TL00', 'HUAWEI nova 5i', 'GLK-LX1U', 'HUAWEI nova 5i', 'SPN-TL00', 'HUAWEI nova 5i Pro', 'SPN-AL00', 'HUAWEI nova 5z', 'WLZ-AL10', 'HUAWEI nova 6', 'WLZ-AN00', 'HUAWEI nova 6', 'JNY-AL10', 'HUAWEI nova 6 SE', 'JNY-TL10', 'HUAWEI nova 6 SE', 'JEF-AN00', 'HUAWEI nova 7', 'JEF-AN20', 'HUAWEI nova 7', 'JEF-TN00', 'HUAWEI nova 7', 'JEF-TN20', 'HUAWEI nova 7', 'JER-AN10', 'HUAWEI nova 7 Pro', 'JER-AN20', 'HUAWEI nova 7 Pro', 'JER-TN10', 'HUAWEI nova 7 Pro', 'JER-TN20', 'HUAWEI nova 7 Pro', 'CDY-AN00', 'HUAWEI nova 7 SE', 'CDY-AN20', 'HUAWEI nova 7 SE', 'CDY-TN00', 'HUAWEI nova 7 SE', 'CDY-TN20', 'HUAWEI nova 7 SE', 'ANG-AN00', 'HUAWEI nova 8', 'BRQ-AN00', 'HUAWEI nova 8 Pro', 'BRQ-AL00', 'HUAWEI nova 8 Pro', 'JSC-AN00', 'HUAWEI nova 8 SE', 'JSC-TN00', 'HUAWEI nova 8 SE', 'JSC-AL50', 'HUAWEI nova 8 SE', 'NAM-AL00', 'HUAWEI nova 9', 'RTE-AL00', 'HUAWEI nova 9 Pro', 'JLN-AL00', 'HUAWEI nova 9 SE', 'NCO-AL00', 'HUAWEI nova 10', 'GLA-AL00', 'HUAWEI nova 10 Pro', 'CHA-AL80', 'HUAWEI nova 10z'],
|
||||
['M2001J2C', 'Xiaomi 10', 'M2001J2G', 'Xiaomi 10', 'M2001J2I', 'Xiaomi 10', 'M2011K2C', 'Xiaomi 11', 'M2011K2G', 'Xiaomi 11', '2201123C', 'Xiaomi 12', '2201123G', 'Xiaomi 12', '2112123AC', 'Xiaomi 12X', '2112123AG', 'Xiaomi 12X', '2201122C', 'Xiaomi 12 Pro', '2201122G', 'Xiaomi 12 Pro'],
|
||||
];
|
||||
export function randDevice() {
|
||||
let brandIdx = rand(0, deviceBrands.length - 1);
|
||||
let brand = deviceBrands[brandIdx];
|
||||
let modelIdx = rand(0, deviceModels[brandIdx].length / 2 - 1);
|
||||
let model = deviceModels[brandIdx][modelIdx * 2 + 1];
|
||||
let release = rand(8, 13);
|
||||
let buildId = randStr(3, false).toUpperCase() + rand(11, 99) + randStr(1, false).toUpperCase();
|
||||
return {
|
||||
brand: brand,
|
||||
model: model,
|
||||
release: release,
|
||||
buildId: buildId,
|
||||
};
|
||||
}
|
||||
|
||||
export function randDeviceWithId(len) {
|
||||
let device = randDevice();
|
||||
device['id'] = randStr(len);
|
||||
return device;
|
||||
}
|
||||
|
||||
export const MOBILE_UA = 'Mozilla/5.0 (Linux; Android 11; M2007J3SC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045714 Mobile Safari/537.36';
|
||||
export const PC_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36';
|
||||
export const UA = 'Mozilla/5.0';
|
||||
export const UC_UA = 'Mozilla/5.0 (Linux; U; Android 9; zh-CN; MI 9 Build/PKQ1.181121.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.5.5.1035 Mobile Safari/537.36';
|
||||
export const IOS_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1';
|
||||
export const MAC_UA = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36 SE 2.X MetaSr 1.0';
|
||||
|
||||
export function formatPlayUrl(src, name) {
|
||||
if (src.trim() == name.trim()) {
|
||||
return name;
|
||||
}
|
||||
return name
|
||||
.trim()
|
||||
.replaceAll(src, '')
|
||||
.replace(/<|>|《|》/g, '')
|
||||
.replace(/\$|#/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function formatPlayUrl2(src, name) {
|
||||
var idx = name.indexOf('$');
|
||||
if (idx <= 0) {
|
||||
return formatPlayUrl(src, name);
|
||||
}
|
||||
return formatPlayUrl(src, name.substring(0, idx)) + name.substring(idx);
|
||||
}
|
||||
|
||||
export function stripHtmlTag(src) {
|
||||
return src
|
||||
.replace(/<\/?[^>]+(>|$)/g, '')
|
||||
.replace(/&.{1,5};/g, '')
|
||||
.replace(/\s{2,}/g, ' ');
|
||||
}
|
||||
|
||||
export function fixUrl(base, src) {
|
||||
try {
|
||||
if (src.startsWith('//')) {
|
||||
let parse = new URL(base);
|
||||
let host = src.substring(2, src.indexOf('/', 2));
|
||||
if (!host.includes('.')) {
|
||||
src = parse.protocol + '://' + parse.host + src.substring(1);
|
||||
} else {
|
||||
src = parse.protocol + ':' + src;
|
||||
}
|
||||
} else if (!src.includes('://')) {
|
||||
let parse = new URL(base);
|
||||
src = parse.protocol + '://' + parse.host + src;
|
||||
}
|
||||
} catch (error) {}
|
||||
return src;
|
||||
}
|
||||
|
||||
export function jsonParse(input, json) {
|
||||
try {
|
||||
let url = json.url || '';
|
||||
if (url.startsWith('//')) {
|
||||
url = 'https:' + url;
|
||||
}
|
||||
if (!url.startsWith('http')) {
|
||||
return {};
|
||||
}
|
||||
let headers = json['headers'] || {};
|
||||
let ua = (json['user-agent'] || '').trim();
|
||||
if (ua.length > 0) {
|
||||
headers['User-Agent'] = ua;
|
||||
}
|
||||
let referer = (json['referer'] || '').trim();
|
||||
if (referer.length > 0) {
|
||||
headers['Referer'] = referer;
|
||||
}
|
||||
return {
|
||||
header: headers,
|
||||
url: url,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
255
cat/tjs/lib/nivid_object.js
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* @File : nivid_object.js
|
||||
* @Author : jade
|
||||
* @Date : 2023/12/20 9:50
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {Crypto} from "./cat.js";
|
||||
|
||||
let DesKey = "diao.com"
|
||||
|
||||
class ChannelResponse {
|
||||
// classes
|
||||
constructor() {
|
||||
this.channelMsg = ""
|
||||
this.channelStatus = 0
|
||||
this.channelList = []
|
||||
this.channelFilters = {}
|
||||
}
|
||||
|
||||
fromJsonString(json_str, remove18ChannelCode = 0) {
|
||||
let json_dic = JSON.parse(json_str)
|
||||
this.channelMsg = json_dic.msg
|
||||
this.channelStatus = json_dic.status
|
||||
let channel_list = []
|
||||
for (const channel_info of json_dic.list) {
|
||||
let new_channel_info = new ChannelInfo()
|
||||
switch (remove18ChannelCode) {
|
||||
case 0:
|
||||
new_channel_info.fromJson(channel_info)
|
||||
channel_list.push(new_channel_info)
|
||||
break
|
||||
case 1:
|
||||
if (channel_info.channelName !== "午夜场" && channel_info.channelName !== "午夜直播") {
|
||||
new_channel_info.fromJson(channel_info)
|
||||
channel_list.push(new_channel_info)
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
if (channel_info.channelName === "午夜场" || channel_info.channelName === "午夜直播") {
|
||||
new_channel_info.fromJson(channel_info)
|
||||
channel_list.push(new_channel_info)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
this.channelList = channel_list
|
||||
this.channelFilters = json_dic.filter
|
||||
}
|
||||
|
||||
setChannelFilters(filter_str) {
|
||||
this.channelFilters = JSON.parse(filter_str)
|
||||
}
|
||||
|
||||
getValues(typeList, name_key, id_key) {
|
||||
let values = []
|
||||
values.push({"n": "全部", "v": "0"})
|
||||
for (const obj of typeList) {
|
||||
values.push({"n": obj[name_key], "v": obj[id_key].toString()})
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
getFilters() {
|
||||
let filters = {}
|
||||
for (const channel_info of this.channelList) {
|
||||
filters[channel_info.channelId] = []
|
||||
let sortMapList = this.channelFilters["sortsMap"][parseInt(channel_info.channelId)]
|
||||
let sortValues = this.getValues(sortMapList, "title", "id")
|
||||
filters[channel_info.channelId].push({"key": "1", "name": "排序", "value": sortValues})
|
||||
let typeMapList = this.channelFilters["typesMap"][parseInt(channel_info.channelId)]
|
||||
let typeValues = this.getValues(typeMapList, "showTypeName", "showTypeId")
|
||||
filters[channel_info.channelId].push({"key": "2", "name": "类型", "value": typeValues})
|
||||
let areaValues = this.getValues(this.channelFilters["regions"], "regionName", "regionId")
|
||||
filters[channel_info.channelId].push({"key": "3", "name": "地区", "value": areaValues})
|
||||
let langValues = this.getValues(this.channelFilters["langs"], "langName", "langId")
|
||||
filters[channel_info.channelId].push({"key": "4", "name": "语言", "value": langValues})
|
||||
let yearValues = this.getValues(this.channelFilters["yearRanges"], "name", "code")
|
||||
filters[channel_info.channelId].push({"key": "5", "name": "年份", "value": yearValues})
|
||||
}
|
||||
return filters
|
||||
}
|
||||
|
||||
getChannelFilters() {
|
||||
return this.channelFilters
|
||||
}
|
||||
|
||||
|
||||
|
||||
getChannelMsg() {
|
||||
return this.channelMsg
|
||||
}
|
||||
|
||||
getChannelStatus() {
|
||||
return this.channelStatus
|
||||
}
|
||||
|
||||
getChannelList() {
|
||||
return this.channelList
|
||||
}
|
||||
|
||||
getClassList() {
|
||||
let classes = []
|
||||
for (const channel_info of this.channelList) {
|
||||
classes.push({"type_id": channel_info.channelId, "type_name": channel_info.channelName})
|
||||
}
|
||||
return classes
|
||||
}
|
||||
|
||||
|
||||
async save() {
|
||||
await local.set("niba", "niba_channel", this.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.channelMsg = ""
|
||||
this.channelStatus = 0
|
||||
this.channelList = []
|
||||
}
|
||||
|
||||
async clearCache() {
|
||||
this.clear()
|
||||
await local.set("niba", "niba_channel", "{}");
|
||||
}
|
||||
|
||||
toString() {
|
||||
const params = {
|
||||
msg: this.getChannelMsg(),
|
||||
status: this.getChannelStatus(),
|
||||
list: this.getChannelList(),
|
||||
filter: this.getChannelFilters()
|
||||
};
|
||||
return JSON.stringify(params);
|
||||
}
|
||||
}
|
||||
|
||||
async function getChannelCache() {
|
||||
return await local.get("niba", "niba_channel");
|
||||
}
|
||||
|
||||
class ChannelInfo {
|
||||
constructor() {
|
||||
this.channelId = 0
|
||||
this.channelName = ""
|
||||
}
|
||||
|
||||
fromJsonString(json_str) {
|
||||
let json_dic = JSON.parse(json_str)
|
||||
this.channelId = json_dic.channelId
|
||||
this.channelName = json_dic.channelName
|
||||
}
|
||||
|
||||
fromJson(json) {
|
||||
this.channelId = json.channelId
|
||||
this.channelName = json.channelName
|
||||
}
|
||||
|
||||
getChannelName() {
|
||||
return this.channelName
|
||||
}
|
||||
|
||||
getChannelId() {
|
||||
return this.channelId
|
||||
}
|
||||
}
|
||||
|
||||
function isNumeric(str) {
|
||||
return !isNaN(parseInt(str));
|
||||
}
|
||||
|
||||
function getVod(video_dic_list, play_foramt_list, showIdCode) {
|
||||
let episode_list = [], episode_str_list = [];
|
||||
for (const video_dic of video_dic_list) {
|
||||
let video_name = ""
|
||||
if (isNumeric((video_dic['episodeName']))) {
|
||||
video_name = "第" + video_dic["episodeName"] + "集"
|
||||
} else {
|
||||
video_name = video_dic["episodeName"]
|
||||
}
|
||||
episode_list.push(video_name + "$" + video_dic["playIdCode"] + "@" + showIdCode);
|
||||
}
|
||||
|
||||
for (let index = 0; index < play_foramt_list.length; index++) {
|
||||
episode_str_list.push(episode_list.join("#"));
|
||||
}
|
||||
return {
|
||||
vod_play_url: episode_str_list.join("$$$"),
|
||||
vod_play_from: play_foramt_list.map(item => item).join("$$$"),
|
||||
};
|
||||
}
|
||||
|
||||
function getHeader() {
|
||||
return {
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
|
||||
"Referer": "https://m.nivod.tv/",
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function md5(text) {
|
||||
return Crypto.MD5(text).toString()
|
||||
}
|
||||
|
||||
//加密
|
||||
async function createSign(body = null) {
|
||||
let params = {
|
||||
"_ts": Date.now(), "app_version": "1.0",
|
||||
"platform": "3", "market_id": "web_nivod",
|
||||
"device_code": "web", "versioncode": 1,
|
||||
"oid": "8ca275aa5e12ba504b266d4c70d95d77a0c2eac5726198ea"
|
||||
}
|
||||
/**
|
||||
* __QUERY::_ts=1702973558399&app_version=1.0&device_code=web&market_id=web_nivod&oid=8ca275aa5e12ba504b266d4c70d95d77a0c2eac5726198ea&platform=3&versioncode=1&__BODY::__KEY::2x_Give_it_a_shot
|
||||
*/
|
||||
let params_list = []
|
||||
for (const key of Object.keys(params).sort()) {
|
||||
params_list.push(`${key}=${params[key]}`)
|
||||
}
|
||||
let body_str = "&__BODY::"
|
||||
if (body !== null) {
|
||||
let body_list = []
|
||||
for (const key of Object.keys(body).sort()) {
|
||||
body_list.push(`${key}=${body[key]}`)
|
||||
}
|
||||
body_str = body_str + body_list.join("&") + "&"
|
||||
}
|
||||
|
||||
|
||||
let params_str = "__QUERY::" + params_list.join("&") + body_str + "__KEY::2x_Give_it_a_shot"
|
||||
let sign_code = md5(params_str)
|
||||
params_list.push(`sign=${sign_code}`)
|
||||
return "?" + params_list.join("&")
|
||||
|
||||
}
|
||||
|
||||
//解密
|
||||
function desDecrypt(content) {
|
||||
// 定义密钥
|
||||
const key = Crypto.enc.Utf8.parse(DesKey); // 密钥需要进行字节数转换
|
||||
/*
|
||||
const encrypted = Crypto.DES.encrypt(content, key, {
|
||||
mode: Crypto.mode.ECB, // 使用ECB模式
|
||||
padding: Crypto.pad.Pkcs7, // 使用Pkcs7填充
|
||||
}).ciphertext.toString();
|
||||
*/
|
||||
return Crypto.DES.decrypt({ciphertext: Crypto.enc.Hex.parse(content)}, key, {
|
||||
mode: Crypto.mode.ECB,
|
||||
padding: Crypto.pad.Pkcs7,
|
||||
}).toString(Crypto.enc.Utf8);
|
||||
}
|
||||
|
||||
export {getChannelCache, desDecrypt, createSign, ChannelResponse, getHeader, getVod};
|
||||
146
cat/tjs/lib/pipiXiaObject.js
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* @File : pipiXiaObject.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/2/4 14:33
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
const UID = "DCC147D11943AF75"
|
||||
const chrsz = 8;
|
||||
const hexcase = 0;
|
||||
|
||||
function hex_md5(s) {
|
||||
return binl2hex(core_md5(str2binl(s), s.length * chrsz))
|
||||
}
|
||||
function str2binl(str) {
|
||||
var bin = Array();
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for (var i = 0; i < str.length * chrsz; i += chrsz)
|
||||
bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (i % 32);
|
||||
return bin
|
||||
}
|
||||
function core_md5(x, len) {
|
||||
x[len >> 5] |= 0x80 << ((len) % 32);
|
||||
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
||||
var a = 1732584193;
|
||||
var b = -271733879;
|
||||
var c = -1732584194;
|
||||
var d = 271733878;
|
||||
for (var i = 0; i < x.length; i += 16) {
|
||||
var olda = a;
|
||||
var oldb = b;
|
||||
var oldc = c;
|
||||
var oldd = d;
|
||||
a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
|
||||
d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
|
||||
c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
|
||||
b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
|
||||
a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
|
||||
d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
|
||||
c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
|
||||
b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
|
||||
a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
|
||||
d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
|
||||
c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
|
||||
b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
|
||||
a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
|
||||
d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
|
||||
c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
|
||||
b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
|
||||
a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
|
||||
d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
|
||||
c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
|
||||
b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
|
||||
a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
|
||||
d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
|
||||
c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
|
||||
b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
|
||||
a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
|
||||
d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
|
||||
c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
|
||||
b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
|
||||
a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
|
||||
d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
|
||||
c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
|
||||
b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
|
||||
a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
|
||||
d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
|
||||
c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
|
||||
b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
|
||||
a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
|
||||
d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
|
||||
c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
|
||||
b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
|
||||
a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
|
||||
d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
|
||||
c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
|
||||
b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
|
||||
a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
|
||||
d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
|
||||
c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
|
||||
b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
|
||||
a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
|
||||
d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
|
||||
c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
|
||||
b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
|
||||
a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
|
||||
d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
|
||||
c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
|
||||
b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
|
||||
a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
|
||||
d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
|
||||
c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
|
||||
b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
|
||||
a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
|
||||
d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
|
||||
c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
|
||||
b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
|
||||
a = safe_add(a, olda);
|
||||
b = safe_add(b, oldb);
|
||||
c = safe_add(c, oldc);
|
||||
d = safe_add(d, oldd)
|
||||
}
|
||||
return Array(a, b, c, d)
|
||||
}
|
||||
function md5_gg(a, b, c, d, x, s, t) {
|
||||
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t)
|
||||
}
|
||||
|
||||
function md5_ff(a, b, c, d, x, s, t) {
|
||||
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t)
|
||||
}
|
||||
|
||||
function md5_hh(a, b, c, d, x, s, t) {
|
||||
return md5_cmn(b ^ c ^ d, a, b, x, s, t)
|
||||
}
|
||||
function md5_ii(a, b, c, d, x, s, t) {
|
||||
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t)
|
||||
}
|
||||
function md5_cmn(q, a, b, x, s, t) {
|
||||
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b)
|
||||
}
|
||||
function safe_add(x, y) {
|
||||
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xFFFF)
|
||||
}
|
||||
function bit_rol(num, cnt) {
|
||||
return (num << cnt) | (num >>> (32 - cnt))
|
||||
}
|
||||
function binl2hex(binarray) {
|
||||
let hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
let str = "";
|
||||
for (let i = 0; i < binarray.length * 4; i++) {
|
||||
str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) + hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF)
|
||||
}
|
||||
return str
|
||||
}
|
||||
function pipixiaMd5(date_time) {
|
||||
return hex_md5('DS' + date_time + UID)
|
||||
}
|
||||
export {pipixiaMd5}
|
||||
|
||||
|
||||
// let key = pipixiaMd5(Math.floor(new Date() / 1000),"DCC147D11943AF75")
|
||||
// let x = 0
|
||||
170
cat/tjs/lib/shift-jis.js
Normal file
@ -0,0 +1,170 @@
|
||||
import { inRange, decoderError, encoderError, floor, isASCIICodePoint, isASCIIByte,
|
||||
end_of_stream, finished } from './text_decoder_utils.js'
|
||||
import index, { indexCodePointFor, indexShiftJISPointerFor } from './text_decoder_indexes.js'
|
||||
|
||||
|
||||
// 13.3 Shift_JIS
|
||||
|
||||
// 13.3.1 Shift_JIS decoder
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {Decoder}
|
||||
* @param {{fatal: boolean}} options
|
||||
*/
|
||||
export class ShiftJISDecoder {
|
||||
constructor(options) {
|
||||
const { fatal } = options
|
||||
this.fatal = fatal
|
||||
// Shift_JIS's decoder has an associated Shift_JIS lead (initially
|
||||
// 0x00).
|
||||
this.Shift_JIS_lead = 0x00
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// 1. If byte is end-of-stream and Shift_JIS lead is not 0x00,
|
||||
// set Shift_JIS lead to 0x00 and return error.
|
||||
if (bite === end_of_stream && this.Shift_JIS_lead !== 0x00) {
|
||||
this.Shift_JIS_lead = 0x00
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 2. If byte is end-of-stream and Shift_JIS lead is 0x00,
|
||||
// return finished.
|
||||
if (bite === end_of_stream && this.Shift_JIS_lead === 0x00)
|
||||
return finished
|
||||
|
||||
// 3. If Shift_JIS lead is not 0x00, let lead be Shift_JIS lead,
|
||||
// let pointer be null, set Shift_JIS lead to 0x00, and then run
|
||||
// these substeps:
|
||||
if (this.Shift_JIS_lead !== 0x00) {
|
||||
var lead = this.Shift_JIS_lead
|
||||
var pointer = null
|
||||
this.Shift_JIS_lead = 0x00
|
||||
|
||||
// 1. Let offset be 0x40, if byte is less than 0x7F, and 0x41
|
||||
// otherwise.
|
||||
var offset = (bite < 0x7F) ? 0x40 : 0x41
|
||||
|
||||
// 2. Let lead offset be 0x81, if lead is less than 0xA0, and
|
||||
// 0xC1 otherwise.
|
||||
var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1
|
||||
|
||||
// 3. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80
|
||||
// to 0xFC, inclusive, set pointer to (lead − lead offset) ×
|
||||
// 188 + byte − offset.
|
||||
if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC))
|
||||
pointer = (lead - lead_offset) * 188 + bite - offset
|
||||
|
||||
// 4. If pointer is in the range 8836 to 10715, inclusive,
|
||||
// return a code point whose value is 0xE000 − 8836 + pointer.
|
||||
if (inRange(pointer, 8836, 10715))
|
||||
return 0xE000 - 8836 + pointer
|
||||
|
||||
// 5. Let code point be null, if pointer is null, and the
|
||||
// index code point for pointer in index jis0208 otherwise.
|
||||
var code_point = (pointer === null) ? null :
|
||||
indexCodePointFor(pointer, index('jis0208'))
|
||||
|
||||
// 6. If code point is null and byte is an ASCII byte, prepend
|
||||
// byte to stream.
|
||||
if (code_point === null && isASCIIByte(bite))
|
||||
stream.prepend(bite)
|
||||
|
||||
// 7. If code point is null, return error.
|
||||
if (code_point === null)
|
||||
return decoderError(this.fatal)
|
||||
|
||||
// 8. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
|
||||
// 4. If byte is an ASCII byte or 0x80, return a code point
|
||||
// whose value is byte.
|
||||
if (isASCIIByte(bite) || bite === 0x80)
|
||||
return bite
|
||||
|
||||
// 5. If byte is in the range 0xA1 to 0xDF, inclusive, return a
|
||||
// code point whose value is 0xFF61 − 0xA1 + byte.
|
||||
if (inRange(bite, 0xA1, 0xDF))
|
||||
return 0xFF61 - 0xA1 + bite
|
||||
|
||||
// 6. If byte is in the range 0x81 to 0x9F, inclusive, or 0xE0
|
||||
// to 0xFC, inclusive, set Shift_JIS lead to byte and return
|
||||
// continue.
|
||||
if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) {
|
||||
this.Shift_JIS_lead = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 7. Return error.
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
}
|
||||
|
||||
// 13.3.2 Shift_JIS encoder
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {Encoder}
|
||||
* @param {{fatal: boolean}} options
|
||||
*/
|
||||
export class ShiftJISEncoder {
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
*/
|
||||
handler(stream, code_point) {
|
||||
// 1. If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is an ASCII code point or U+0080, return a
|
||||
// byte whose value is code point.
|
||||
if (isASCIICodePoint(code_point) || code_point === 0x0080)
|
||||
return code_point
|
||||
|
||||
// 3. If code point is U+00A5, return byte 0x5C.
|
||||
if (code_point === 0x00A5)
|
||||
return 0x5C
|
||||
|
||||
// 4. If code point is U+203E, return byte 0x7E.
|
||||
if (code_point === 0x203E)
|
||||
return 0x7E
|
||||
|
||||
// 5. If code point is in the range U+FF61 to U+FF9F, inclusive,
|
||||
// return a byte whose value is code point − 0xFF61 + 0xA1.
|
||||
if (inRange(code_point, 0xFF61, 0xFF9F))
|
||||
return code_point - 0xFF61 + 0xA1
|
||||
|
||||
// 6. If code point is U+2212, set it to U+FF0D.
|
||||
if (code_point === 0x2212)
|
||||
code_point = 0xFF0D
|
||||
|
||||
// 7. Let pointer be the index Shift_JIS pointer for code point.
|
||||
var pointer = indexShiftJISPointerFor(code_point)
|
||||
|
||||
// 8. If pointer is null, return error with code point.
|
||||
if (pointer === null)
|
||||
return encoderError(code_point)
|
||||
|
||||
// 9. Let lead be floor(pointer / 188).
|
||||
var lead = floor(pointer / 188)
|
||||
|
||||
// 10. Let lead offset be 0x81, if lead is less than 0x1F, and
|
||||
// 0xC1 otherwise.
|
||||
var lead_offset = (lead < 0x1F) ? 0x81 : 0xC1
|
||||
|
||||
// 11. Let trail be pointer % 188.
|
||||
var trail = pointer % 188
|
||||
|
||||
// 12. Let offset be 0x40, if trail is less than 0x3F, and 0x41
|
||||
// otherwise.
|
||||
var offset = (trail < 0x3F) ? 0x40 : 0x41
|
||||
|
||||
// 13. Return two bytes whose values are lead + lead offset and
|
||||
// trail + offset.
|
||||
return [lead + lead_offset, trail + offset]
|
||||
}
|
||||
}
|
||||
53
cat/tjs/lib/similarity.js
Normal file
@ -0,0 +1,53 @@
|
||||
function compareTwoStrings(first, second) {
|
||||
if ((first = first.replace(/\s+/g, "")) === (second = second.replace(/\s+/g, ""))) return 1;
|
||||
if (first.length < 2 || second.length < 2) return 0;
|
||||
var firstBigrams = new Map;
|
||||
for (let i = 0; i < first.length - 1; i++) {
|
||||
var bigram = first.substring(i, i + 2), count = firstBigrams.has(bigram) ? firstBigrams.get(bigram) + 1 : 1;
|
||||
firstBigrams.set(bigram, count)
|
||||
}
|
||||
let intersectionSize = 0;
|
||||
for (let i = 0; i < second.length - 1; i++) {
|
||||
const bigram = second.substring(i, i + 2), count = firstBigrams.has(bigram) ? firstBigrams.get(bigram) : 0;
|
||||
0 < count && (firstBigrams.set(bigram, count - 1), intersectionSize++)
|
||||
}
|
||||
return 2 * intersectionSize / (first.length + second.length - 2)
|
||||
}
|
||||
|
||||
function findBestMatch(mainString, targetStrings) {
|
||||
var ratings = [];
|
||||
let bestMatchIndex = 0;
|
||||
for (let i = 0; i < targetStrings.length; i++) {
|
||||
var currentTargetString = targetStrings[i], currentRating = compareTwoStrings(mainString, currentTargetString);
|
||||
ratings.push({
|
||||
target: currentTargetString,
|
||||
rating: currentRating
|
||||
}), currentRating > ratings[bestMatchIndex].rating && (bestMatchIndex = i)
|
||||
}
|
||||
return {ratings: ratings, bestMatch: ratings[bestMatchIndex], bestMatchIndex: bestMatchIndex}
|
||||
}
|
||||
|
||||
function lcs(str1, str2) {
|
||||
if (!str1 || !str2) return {length: 0, sequence: "", offset: 0};
|
||||
for (var sequence = "", str1Length = str1.length, str2Length = str2.length, num = new Array(str1Length), maxlen = 0, lastSubsBegin = 0, i = 0; i < str1Length; i++) {
|
||||
for (var subArray = new Array(str2Length), j = 0; j < str2Length; j++) subArray[j] = 0;
|
||||
num[i] = subArray
|
||||
}
|
||||
for (var thisSubsBegin = null, i = 0; i < str1Length; i++) for (j = 0; j < str2Length; j++) str1[i] !== str2[j] ? num[i][j] = 0 : (num[i][j] = 0 === i || 0 === j ? 1 : 1 + num[i - 1][j - 1], num[i][j] > maxlen && (maxlen = num[i][j], lastSubsBegin === (thisSubsBegin = i - num[i][j] + 1) ? sequence += str1[i] : (lastSubsBegin = thisSubsBegin, sequence = "", sequence += str1.substr(lastSubsBegin, i + 1 - lastSubsBegin))));
|
||||
return {length: maxlen, sequence: sequence, offset: thisSubsBegin}
|
||||
}
|
||||
|
||||
function findBestLCS(mainString, targetStrings) {
|
||||
var results = [];
|
||||
let bestMatchIndex = 0;
|
||||
for (let i = 0; i < targetStrings.length; i++) {
|
||||
var currentTargetString = targetStrings[i], currentLCS = lcs(mainString, currentTargetString);
|
||||
results.push({
|
||||
target: currentTargetString,
|
||||
lcs: currentLCS
|
||||
}), currentLCS.length > results[bestMatchIndex].lcs.length && (bestMatchIndex = i)
|
||||
}
|
||||
return {allLCS: results, bestMatch: results[bestMatchIndex], bestMatchIndex: bestMatchIndex}
|
||||
}
|
||||
|
||||
export {compareTwoStrings, findBestMatch, findBestLCS};
|
||||
86
cat/tjs/lib/single-byte.js
Normal file
@ -0,0 +1,86 @@
|
||||
import { end_of_stream, finished, isASCIIByte, decoderError, encoderError, isASCIICodePoint } from './text_decoder_utils.js'
|
||||
import { indexPointerFor } from './text_decoder_indexes.js'
|
||||
|
||||
//
|
||||
// 10. Legacy single-byte encodings
|
||||
//
|
||||
|
||||
// 10.1 single-byte decoder
|
||||
/**
|
||||
* @implements {Decoder}
|
||||
*/
|
||||
export class SingleByteDecoder {
|
||||
/**
|
||||
* @param {!Array.<number>} index The encoding index.
|
||||
* @param {{fatal: boolean}} options
|
||||
*/
|
||||
constructor(index, options) {
|
||||
const { fatal } = options
|
||||
this.fatal = fatal
|
||||
this.index = index
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// 1. If byte is end-of-stream, return finished.
|
||||
if (bite === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If byte is an ASCII byte, return a code point whose value
|
||||
// is byte.
|
||||
if (isASCIIByte(bite))
|
||||
return bite
|
||||
|
||||
// 3. Let code point be the index code point for byte − 0x80 in
|
||||
// index single-byte.
|
||||
var code_point = this.index[bite - 0x80]
|
||||
|
||||
// 4. If code point is null, return error.
|
||||
if (code_point === null)
|
||||
return decoderError(this.fatal)
|
||||
|
||||
// 5. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
}
|
||||
|
||||
// 10.2 single-byte encoder
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class SingleByteEncoder {
|
||||
/**
|
||||
* @param {!Array.<?number>} index The encoding index.
|
||||
*/
|
||||
constructor(index) {
|
||||
this.index = index
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
* @return {(number|!Array.<number>)} Byte(s) to emit.
|
||||
*/
|
||||
handler(stream, code_point) {
|
||||
// 1. If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is an ASCII code point, return a byte whose
|
||||
// value is code point.
|
||||
if (isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 3. Let pointer be the index pointer for code point in index
|
||||
// single-byte.
|
||||
const pointer = indexPointerFor(code_point, this.index)
|
||||
|
||||
// 4. If pointer is null, return error with code point.
|
||||
if (pointer === null)
|
||||
encoderError(code_point)
|
||||
|
||||
// 5. Return a byte whose value is pointer + 0x80.
|
||||
return pointer + 0x80
|
||||
}
|
||||
}
|
||||
119
cat/tjs/lib/table.js
Normal file
@ -0,0 +1,119 @@
|
||||
import Encodings from './encodings.js'
|
||||
import { UTF8Decoder, UTF8Encoder } from './utf8.js'
|
||||
import { UTF16Decoder, UTF16Encoder } from './utf16.js'
|
||||
import { GB18030Decoder, GB18030Encoder } from './gb18030.js'
|
||||
import { Big5Decoder, Big5Encoder } from './big5.js'
|
||||
import { EUCJPDecoder, EUCJPEncoder } from './euc-jp.js'
|
||||
import { EUCKRDecoder, EUCKREncoder } from './euc-kr.js'
|
||||
import { ISO2022JPDecoder, ISO2022JPEncoder } from './iso-2022-jp.js'
|
||||
import { XUserDefinedDecoder, XUserDefinedEncoder } from './x-user-defined.js'
|
||||
import { ShiftJISDecoder, ShiftJISEncoder } from './shift-jis.js'
|
||||
import { SingleByteDecoder, SingleByteEncoder } from './single-byte.js'
|
||||
import index from './text_decoder_indexes.js';
|
||||
|
||||
// 5.2 Names and labels
|
||||
|
||||
// TODO: Define @typedef for Encoding: {name:string,labels:Array.<string>}
|
||||
// https://github.com/google/closure-compiler/issues/247
|
||||
|
||||
|
||||
// Label to encoding registry.
|
||||
/** @type {Object.<string,{name:string,labels:Array.<string>}>} */
|
||||
export const label_to_encoding = {}
|
||||
Encodings.forEach(({ encodings }) => {
|
||||
encodings.forEach((encoding) => {
|
||||
encoding.labels.forEach((label) => {
|
||||
label_to_encoding[label] = encoding
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Registry of of encoder/decoder factories, by encoding name.
|
||||
export const encoders = {
|
||||
'UTF-8'() { // 9.1 utf-8
|
||||
return new UTF8Encoder()
|
||||
},
|
||||
'GBK'(options) { // 11.1.2 gbk encoder;
|
||||
// gbk's encoder is gb18030's encoder with its gbk flag set.
|
||||
return new GB18030Encoder(options, true)
|
||||
},
|
||||
'gb18030'() {
|
||||
return new GB18030Encoder()
|
||||
},
|
||||
'Big5'() {
|
||||
return new Big5Encoder()
|
||||
},
|
||||
'EUC-JP'() {
|
||||
return new EUCJPEncoder()
|
||||
},
|
||||
'EUC-KR'() {
|
||||
return new EUCKREncoder()
|
||||
},
|
||||
'ISO-2022-JP'() {
|
||||
return new ISO2022JPEncoder()
|
||||
},
|
||||
'UTF-16BE'() { // 15.3 utf-16be
|
||||
return new UTF16Encoder(true)
|
||||
},
|
||||
'UTF-16LE'() { // 15.4 utf-16le
|
||||
return new UTF16Encoder()
|
||||
},
|
||||
'x-user-defined'() {
|
||||
return new XUserDefinedEncoder()
|
||||
},
|
||||
'Shift_JIS'() {
|
||||
return new ShiftJISEncoder()
|
||||
},
|
||||
}
|
||||
|
||||
/** @type {Object.<string, function({fatal:boolean}): Decoder>} */
|
||||
export const decoders = {
|
||||
'UTF-8'(options) { // 9.1.1 utf-8 decoder
|
||||
return new UTF8Decoder(options)
|
||||
},
|
||||
'GBK'(options) { // 11.1.1 gbk decoder; gbk's decoder is gb18030's decoder.
|
||||
return new GB18030Decoder(options)
|
||||
},
|
||||
'gb18030'(options) {
|
||||
return new GB18030Decoder(options)
|
||||
},
|
||||
'Big5'(options) {
|
||||
return new Big5Decoder(options)
|
||||
},
|
||||
'EUC-JP'(options) {
|
||||
return new EUCJPDecoder(options)
|
||||
},
|
||||
'EUC-KR'(options) {
|
||||
return new EUCKRDecoder(options)
|
||||
},
|
||||
'ISO-2022-JP'(options) {
|
||||
return new ISO2022JPDecoder(options)
|
||||
},
|
||||
'UTF-16BE'(options) { // 15.3.1 utf-16be decoder
|
||||
return new UTF16Decoder(true, options)
|
||||
},
|
||||
'UTF-16LE'(options) { // 15.4.1 utf-16le decoder
|
||||
return new UTF16Decoder(false, options)
|
||||
},
|
||||
'x-user-defined'() {
|
||||
return new XUserDefinedDecoder()
|
||||
},
|
||||
'Shift_JIS'(options) {
|
||||
return new ShiftJISDecoder(options)
|
||||
},
|
||||
}
|
||||
|
||||
Encodings.forEach(({ heading, encodings }) => {
|
||||
if (heading != 'Legacy single-byte encodings')
|
||||
return
|
||||
encodings.forEach((encoding) => {
|
||||
const name = encoding.name
|
||||
const idx = index(name.toLowerCase())
|
||||
decoders[name] = (options) => {
|
||||
return new SingleByteDecoder(idx, options)
|
||||
}
|
||||
encoders[name] = (options) => {
|
||||
return new SingleByteEncoder(idx, options)
|
||||
}
|
||||
})
|
||||
})
|
||||
84
cat/tjs/lib/tencentDanmu.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* @File : tencentDanmu.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/3/13 13:17
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
|
||||
import {DammuSpider} from "./danmuSpider.js";
|
||||
import {VodDetail} from "./vod.js";
|
||||
import * as Utils from "./utils.js";
|
||||
|
||||
class TencentDammuSpider extends DammuSpider {
|
||||
constructor() {
|
||||
super()
|
||||
this.siteUrl = "https://v.qq.com"
|
||||
this.reconnectTimes = 0
|
||||
this.maxReconnectTimes = 5
|
||||
}
|
||||
|
||||
getAppName() {
|
||||
return "腾讯视频"
|
||||
}
|
||||
|
||||
async parseVodShortListFromDoc($) {
|
||||
let vodElements = $("[class=\"_infos\"]")
|
||||
let vod_list = []
|
||||
for (const vodElement of vodElements) {
|
||||
let vodDetail = new VodDetail()
|
||||
let titleElement = $(vodElement).find("[class=\"result_title\"]")
|
||||
let infoItemEvenElenet = $(vodElement).find("[class=\"info_item info_item_even\"]")
|
||||
let infoItemOddElement = $(vodElement).find("[class=\"info_item info_item_odd\"]")
|
||||
let descElement = $(vodElement).find("[class=\"info_item info_item_desc\"]")
|
||||
vodDetail.vod_name = $($(titleElement).find("[class=\"hl\"]")).text()
|
||||
vodDetail.vod_year = $($(titleElement).find("[class=\"sub\"]")).text().replaceAll("\n","").replaceAll("(","").replaceAll(")","").replaceAll("\t","").split("/").slice(-1)[0]
|
||||
vodDetail.type_name = $($(titleElement).find("[class=\"type\"]")).text()
|
||||
vodDetail.vod_director = $($($(infoItemEvenElenet).find("[class=\"content\"]")).find("span")).text()
|
||||
let actorList = $( $(infoItemOddElement.slice(-1)[0]).find("[class=\"content\"]")).find("a")
|
||||
let vodActorList = []
|
||||
for (const actorElement of actorList){
|
||||
vodActorList.push($(actorElement).text())
|
||||
}
|
||||
vodDetail.vod_actor = vodActorList.join(" * ")
|
||||
vodDetail.vod_content = $($(descElement).find("[class=\"desc_text\"]")[0]).text()
|
||||
let url = $(vodElement).find("a")[0].attribs.href
|
||||
if (url.indexOf("cover") > -1){
|
||||
let detail$ = await this.getHtml(url)
|
||||
let video_ids = JSON.parse(Utils.getStrByRegex(/"video_ids":(.*?),/,detail$.html()))
|
||||
vodDetail.vod_id = video_ids[0]
|
||||
vod_list.push(vodDetail)
|
||||
}
|
||||
}
|
||||
return vod_list
|
||||
}
|
||||
|
||||
async search(wd) {
|
||||
await this.jadeLog.debug(`正在搜索:${wd}`, true)
|
||||
let searchUrl = this.siteUrl + `/x/search/?q=${wd}`
|
||||
let $ = await this.getHtml(searchUrl)
|
||||
return this.parseVodShortListFromDoc($)
|
||||
}
|
||||
|
||||
parseDammu(id){
|
||||
|
||||
}
|
||||
|
||||
|
||||
async getDammu(voddetail, episodeId) {
|
||||
let vod_list = await this.search(voddetail.vod_name)
|
||||
for (const searchVodDetail of vod_list){
|
||||
if (voddetail.vod_director === searchVodDetail.vod_director){
|
||||
await this.jadeLog.debug("搜索匹配成功",true)
|
||||
return
|
||||
}
|
||||
}
|
||||
await this.jadeLog.warning(`搜索匹配失败,原:${JSON.stringify(voddetail)},搜索:${JSON.stringify(vod_list)}`)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
export {TencentDammuSpider}
|
||||
|
||||
|
||||
118
cat/tjs/lib/text_decoder_index.js
Normal file
@ -0,0 +1,118 @@
|
||||
import { end_of_stream } from './text_decoder_utils.js'
|
||||
import { label_to_encoding } from './table.js'
|
||||
|
||||
export default class Stream {
|
||||
/**
|
||||
* A stream represents an ordered sequence of tokens.
|
||||
* @param {!(Array.<number>|Uint8Array)} tokens Array of tokens that provide
|
||||
* the stream.
|
||||
*/
|
||||
constructor(tokens) {
|
||||
this.tokens = [...tokens]
|
||||
// Reversed as push/pop is more efficient than shift/unshift.
|
||||
this.tokens.reverse()
|
||||
}
|
||||
/**
|
||||
* @returns True if end-of-stream has been hit.
|
||||
*/
|
||||
endOfStream() {
|
||||
return !this.tokens.length
|
||||
}
|
||||
/**
|
||||
* When a token is read from a stream, the first token in the
|
||||
* stream must be returned and subsequently removed, and
|
||||
* end-of-stream must be returned otherwise.
|
||||
*
|
||||
* @return Get the next token from the stream, or end_of_stream.
|
||||
*/
|
||||
read() {
|
||||
if (!this.tokens.length)
|
||||
return end_of_stream
|
||||
return this.tokens.pop()
|
||||
}
|
||||
/**
|
||||
* When one or more tokens are prepended to a stream, those tokens
|
||||
* must be inserted, in given order, before the first token in the
|
||||
* stream.
|
||||
*
|
||||
* @param {(number|!Array.<number>)} token The token(s) to prepend to the
|
||||
* stream.
|
||||
*/
|
||||
prepend(token) {
|
||||
if (Array.isArray(token)) {
|
||||
var tokens = /**@type {!Array.<number>}*/(token)
|
||||
while (tokens.length)
|
||||
this.tokens.push(tokens.pop())
|
||||
} else {
|
||||
this.tokens.push(token)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* When one or more tokens are pushed to a stream, those tokens
|
||||
* must be inserted, in given order, after the last token in the
|
||||
* stream.
|
||||
*
|
||||
* @param {(number|!Array.<number>)} token The tokens(s) to push to the
|
||||
* stream.
|
||||
*/
|
||||
push(token) {
|
||||
if (Array.isArray(token)) {
|
||||
const tokens = /**@type {!Array.<number>}*/(token)
|
||||
while (tokens.length)
|
||||
this.tokens.unshift(tokens.shift())
|
||||
} else {
|
||||
this.tokens.unshift(token)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const DEFAULT_ENCODING = 'utf-8'
|
||||
|
||||
|
||||
/**
|
||||
* Returns the encoding for the label.
|
||||
* @param {string} label The encoding label.
|
||||
*/
|
||||
export function getEncoding(label) {
|
||||
// 1. Remove any leading and trailing ASCII whitespace from label.
|
||||
label = String(label).trim().toLowerCase()
|
||||
|
||||
// 2. If label is an ASCII case-insensitive match for any of the
|
||||
// labels listed in the table below, return the corresponding
|
||||
// encoding, and failure otherwise.
|
||||
if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) {
|
||||
return label_to_encoding[label]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 5. Encodings
|
||||
//
|
||||
|
||||
// 5.1 Encoders and decoders
|
||||
|
||||
// /** @interface */
|
||||
// function Decoder() {}
|
||||
// Decoder.prototype = {
|
||||
// /**
|
||||
// * @param {Stream} stream The stream of bytes being decoded.
|
||||
// * @param {number} bite The next byte read from the stream.
|
||||
// * @return {?(number|!Array.<number>)} The next code point(s)
|
||||
// * decoded, or null if not enough data exists in the input
|
||||
// * stream to decode a complete code point, or |finished|.
|
||||
// */
|
||||
// handler: function(stream, bite) {},
|
||||
// }
|
||||
|
||||
// /** @interface */
|
||||
// function Encoder() {}
|
||||
// Encoder.prototype = {
|
||||
// /**
|
||||
// * @param {Stream} stream The stream of code points being encoded.
|
||||
// * @param {number} code_point Next code point read from the stream.
|
||||
// * @return {(number|!Array.<number>)} Byte(s) to emit, or |finished|.
|
||||
// */
|
||||
// handler: function(stream, code_point) {},
|
||||
// }
|
||||
153
cat/tjs/lib/text_decoder_indexes.js
Normal file
@ -0,0 +1,153 @@
|
||||
import { inRange } from './text_decoder_utils.js'
|
||||
import Indexes from './encoding-indexes.js'
|
||||
|
||||
//
|
||||
// 6. Indexes
|
||||
//
|
||||
|
||||
/**
|
||||
* @param {number} pointer The |pointer| to search for.
|
||||
* @param {(!Array.<?number>|undefined)} index The |index| to search within.
|
||||
* @return {?number} The code point corresponding to |pointer| in |index|,
|
||||
* or null if |code point| is not in |index|.
|
||||
*/
|
||||
export function indexCodePointFor(pointer, i) {
|
||||
if (!i) return null
|
||||
return i[pointer] || null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} code_point The |code point| to search for.
|
||||
* @param {!Array.<?number>} i The |index| to search within.
|
||||
* @return {?number} The first pointer corresponding to |code point| in
|
||||
* |index|, or null if |code point| is not in |index|.
|
||||
*/
|
||||
export function indexPointerFor(code_point, i) {
|
||||
var pointer = i.indexOf(code_point)
|
||||
return pointer === -1 ? null : pointer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name Name of the index.
|
||||
*/
|
||||
export default function index(name) {
|
||||
return Indexes[name]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} pointer The |pointer| to search for in the gb18030 index.
|
||||
* @return The code point corresponding to |pointer| in |index|,
|
||||
* or null if |code point| is not in the gb18030 index.
|
||||
*/
|
||||
export function indexGB18030RangesCodePointFor(pointer) {
|
||||
// 1. If pointer is greater than 39419 and less than 189000, or
|
||||
// pointer is greater than 1237575, return null.
|
||||
if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575))
|
||||
return null
|
||||
|
||||
// 2. If pointer is 7457, return code point U+E7C7.
|
||||
if (pointer === 7457) return 0xE7C7
|
||||
|
||||
// 3. Let offset be the last pointer in index gb18030 ranges that
|
||||
// is equal to or less than pointer and let code point offset be
|
||||
// its corresponding code point.
|
||||
var offset = 0
|
||||
var code_point_offset = 0
|
||||
var idx = index('gb18030-ranges')
|
||||
var i
|
||||
for (i = 0; i < idx.length; ++i) {
|
||||
/** @type {!Array.<number>} */
|
||||
var entry = idx[i]
|
||||
if (entry[0] <= pointer) {
|
||||
offset = entry[0]
|
||||
code_point_offset = entry[1]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Return a code point whose value is code point offset +
|
||||
// pointer − offset.
|
||||
return code_point_offset + pointer - offset
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} code_point The |code point| to locate in the gb18030 index.
|
||||
* @return {number} The first pointer corresponding to |code point| in the
|
||||
* gb18030 index.
|
||||
*/
|
||||
export function indexGB18030RangesPointerFor(code_point) {
|
||||
// 1. If code point is U+E7C7, return pointer 7457.
|
||||
if (code_point === 0xE7C7) return 7457
|
||||
|
||||
// 2. Let offset be the last code point in index gb18030 ranges
|
||||
// that is equal to or less than code point and let pointer offset
|
||||
// be its corresponding pointer.
|
||||
var offset = 0
|
||||
var pointer_offset = 0
|
||||
var idx = index('gb18030-ranges')
|
||||
var i
|
||||
for (i = 0; i < idx.length; ++i) {
|
||||
/** @type {!Array.<number>} */
|
||||
var entry = idx[i]
|
||||
if (entry[1] <= code_point) {
|
||||
offset = entry[1]
|
||||
pointer_offset = entry[0]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Return a pointer whose value is pointer offset + code point
|
||||
// − offset.
|
||||
return pointer_offset + code_point - offset
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} code_point The |code_point| to search for in the Shift_JIS
|
||||
* index.
|
||||
* @return {?number} The code point corresponding to |pointer| in |index|,
|
||||
* or null if |code point| is not in the Shift_JIS index.
|
||||
*/
|
||||
export function indexShiftJISPointerFor(code_point) {
|
||||
// 1. Let index be index jis0208 excluding all entries whose
|
||||
// pointer is in the range 8272 to 8835, inclusive.
|
||||
shift_jis_index = shift_jis_index ||
|
||||
index('jis0208').map((cp, pointer) => {
|
||||
return inRange(pointer, 8272, 8835) ? null : cp
|
||||
})
|
||||
const index_ = shift_jis_index
|
||||
|
||||
// 2. Return the index pointer for code point in index.
|
||||
return index_.indexOf(code_point)
|
||||
}
|
||||
var shift_jis_index
|
||||
|
||||
/**
|
||||
* @param {number} code_point The |code_point| to search for in the big5
|
||||
* index.
|
||||
* @return {?number} The code point corresponding to |pointer| in |index|,
|
||||
* or null if |code point| is not in the big5 index.
|
||||
*/
|
||||
export function indexBig5PointerFor(code_point) {
|
||||
// 1. Let index be index Big5 excluding all entries whose pointer
|
||||
big5_index_no_hkscs = big5_index_no_hkscs ||
|
||||
index('big5').map((cp, pointer) => {
|
||||
return (pointer < (0xA1 - 0x81) * 157) ? null : cp
|
||||
})
|
||||
var index_ = big5_index_no_hkscs
|
||||
|
||||
// 2. If code point is U+2550, U+255E, U+2561, U+256A, U+5341, or
|
||||
// U+5345, return the last pointer corresponding to code point in
|
||||
// index.
|
||||
if (code_point === 0x2550 || code_point === 0x255E ||
|
||||
code_point === 0x2561 || code_point === 0x256A ||
|
||||
code_point === 0x5341 || code_point === 0x5345) {
|
||||
return index_.lastIndexOf(code_point)
|
||||
}
|
||||
|
||||
// 3. Return the index pointer for code point in index.
|
||||
return indexPointerFor(code_point, index_)
|
||||
}
|
||||
|
||||
var big5_index_no_hkscs
|
||||
180
cat/tjs/lib/text_decoder_utils.js
Normal file
@ -0,0 +1,180 @@
|
||||
//
|
||||
// Utilities
|
||||
//
|
||||
/**
|
||||
* @param {number} a The number to test.
|
||||
* @param {number} min The minimum value in the range, inclusive.
|
||||
* @param {number} max The maximum value in the range, inclusive.
|
||||
* @return {boolean} True if a >= min and a <= max.
|
||||
*/
|
||||
export function inRange(a, min, max) {
|
||||
return min <= a && a <= max
|
||||
}
|
||||
|
||||
export const floor = Math.floor
|
||||
|
||||
/**
|
||||
* @param {string} string Input string of UTF-16 code units.
|
||||
* @return {!Array.<number>} Code points.
|
||||
*/
|
||||
export function stringToCodePoints(string) {
|
||||
// https://heycam.github.io/webidl/#dfn-obtain-unicode
|
||||
|
||||
// 1. Let S be the DOMString value.
|
||||
var s = String(string)
|
||||
|
||||
// 2. Let n be the length of S.
|
||||
var n = s.length
|
||||
|
||||
// 3. Initialize i to 0.
|
||||
var i = 0
|
||||
|
||||
// 4. Initialize U to be an empty sequence of Unicode characters.
|
||||
var u = []
|
||||
|
||||
// 5. While i < n:
|
||||
while (i < n) {
|
||||
// 1. Let c be the code unit in S at index i.
|
||||
var c = s.charCodeAt(i)
|
||||
|
||||
// 2. Depending on the value of c:
|
||||
|
||||
// c < 0xD800 or c > 0xDFFF
|
||||
if (c < 0xD800 || c > 0xDFFF) {
|
||||
// Append to U the Unicode character with code point c.
|
||||
u.push(c)
|
||||
}
|
||||
|
||||
// 0xDC00 ≤ c ≤ 0xDFFF
|
||||
else if (0xDC00 <= c && c <= 0xDFFF) {
|
||||
// Append to U a U+FFFD REPLACEMENT CHARACTER.
|
||||
u.push(0xFFFD)
|
||||
}
|
||||
|
||||
// 0xD800 ≤ c ≤ 0xDBFF
|
||||
else if (0xD800 <= c && c <= 0xDBFF) {
|
||||
// 1. If i = n−1, then append to U a U+FFFD REPLACEMENT
|
||||
// CHARACTER.
|
||||
if (i === n - 1) {
|
||||
u.push(0xFFFD)
|
||||
}
|
||||
// 2. Otherwise, i < n−1:
|
||||
else {
|
||||
// 1. Let d be the code unit in S at index i+1.
|
||||
var d = s.charCodeAt(i + 1)
|
||||
|
||||
// 2. If 0xDC00 ≤ d ≤ 0xDFFF, then:
|
||||
if (0xDC00 <= d && d <= 0xDFFF) {
|
||||
// 1. Let a be c & 0x3FF.
|
||||
var a = c & 0x3FF
|
||||
|
||||
// 2. Let b be d & 0x3FF.
|
||||
var b = d & 0x3FF
|
||||
|
||||
// 3. Append to U the Unicode character with code point
|
||||
// 2^16+2^10*a+b.
|
||||
u.push(0x10000 + (a << 10) + b)
|
||||
|
||||
// 4. Set i to i+1.
|
||||
i += 1
|
||||
}
|
||||
|
||||
// 3. Otherwise, d < 0xDC00 or d > 0xDFFF. Append to U a
|
||||
// U+FFFD REPLACEMENT CHARACTER.
|
||||
else {
|
||||
u.push(0xFFFD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Set i to i+1.
|
||||
i += 1
|
||||
}
|
||||
|
||||
// 6. Return U.
|
||||
return u
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Array.<number>} code_points Array of code points.
|
||||
* @return {string} string String of UTF-16 code units.
|
||||
*/
|
||||
export function codePointsToString(code_points) {
|
||||
var s = ''
|
||||
for (var i = 0; i < code_points.length; ++i) {
|
||||
var cp = code_points[i]
|
||||
if (cp <= 0xFFFF) {
|
||||
s += String.fromCharCode(cp)
|
||||
} else {
|
||||
cp -= 0x10000
|
||||
s += String.fromCharCode((cp >> 10) + 0xD800,
|
||||
(cp & 0x3FF) + 0xDC00)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} fatal If true, decoding errors raise an exception.
|
||||
* @param {number=} opt_code_point Override the standard fallback code point.
|
||||
* @return The code point to insert on a decoding error.
|
||||
*/
|
||||
export function decoderError(fatal, opt_code_point) {
|
||||
if (fatal)
|
||||
throw TypeError('Decoder error')
|
||||
return opt_code_point || 0xFFFD
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} code_point The code point that could not be encoded.
|
||||
* @return {number} Always throws, no value is actually returned.
|
||||
*/
|
||||
export function encoderError(code_point) {
|
||||
throw TypeError('The code point ' + code_point + ' could not be encoded.')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} code_unit
|
||||
* @param {boolean} utf16be
|
||||
*/
|
||||
export function convertCodeUnitToBytes(code_unit, utf16be) {
|
||||
// 1. Let byte1 be code unit >> 8.
|
||||
const byte1 = code_unit >> 8
|
||||
|
||||
// 2. Let byte2 be code unit & 0x00FF.
|
||||
const byte2 = code_unit & 0x00FF
|
||||
|
||||
// 3. Then return the bytes in order:
|
||||
// utf-16be flag is set: byte1, then byte2.
|
||||
if (utf16be)
|
||||
return [byte1, byte2]
|
||||
// utf-16be flag is unset: byte2, then byte1.
|
||||
return [byte2, byte1]
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 4. Terminology
|
||||
//
|
||||
|
||||
/**
|
||||
* An ASCII byte is a byte in the range 0x00 to 0x7F, inclusive.
|
||||
* @param {number} a The number to test.
|
||||
* @return {boolean} True if a is in the range 0x00 to 0x7F, inclusive.
|
||||
*/
|
||||
export function isASCIIByte(a) {
|
||||
return 0x00 <= a && a <= 0x7F
|
||||
}
|
||||
|
||||
/**
|
||||
* An ASCII code point is a code point in the range U+0000 to
|
||||
* U+007F, inclusive.
|
||||
*/
|
||||
export const isASCIICodePoint = isASCIIByte
|
||||
|
||||
/**
|
||||
* End-of-stream is a special token that signifies no more tokens are in the stream.
|
||||
*/
|
||||
export const end_of_stream = -1
|
||||
|
||||
export const finished = -1
|
||||
139
cat/tjs/lib/utf16.js
Normal file
@ -0,0 +1,139 @@
|
||||
import { inRange, decoderError, end_of_stream, finished, convertCodeUnitToBytes } from './text_decoder_utils.js'
|
||||
|
||||
// 15.2.1 shared utf-16 decoder
|
||||
|
||||
/**
|
||||
* @implements {Decoder}
|
||||
*/
|
||||
export class UTF16Decoder {
|
||||
/**
|
||||
* @param {boolean} utf16_be True if big-endian, false if little-endian.
|
||||
* @param {{fatal: boolean}} options
|
||||
*/
|
||||
constructor(utf16_be, options) {
|
||||
const { fatal } = options
|
||||
this.utf16_be = utf16_be
|
||||
this.fatal = fatal
|
||||
this.utf16_lead_byte = null
|
||||
this.utf16_lead_surrogate = null
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// 1. If byte is end-of-stream and either utf-16 lead byte or
|
||||
// utf-16 lead surrogate is not null, set utf-16 lead byte and
|
||||
// utf-16 lead surrogate to null, and return error.
|
||||
if (bite === end_of_stream && (this.utf16_lead_byte !== null ||
|
||||
this.utf16_lead_surrogate !== null)) {
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 2. If byte is end-of-stream and utf-16 lead byte and utf-16
|
||||
// lead surrogate are null, return finished.
|
||||
if (bite === end_of_stream && this.utf16_lead_byte === null &&
|
||||
this.utf16_lead_surrogate === null) {
|
||||
return finished
|
||||
}
|
||||
|
||||
// 3. If utf-16 lead byte is null, set utf-16 lead byte to byte
|
||||
// and return continue.
|
||||
if (this.utf16_lead_byte === null) {
|
||||
this.utf16_lead_byte = bite
|
||||
return null
|
||||
}
|
||||
|
||||
// 4. Let code unit be the result of:
|
||||
let code_unit
|
||||
if (this.utf16_be) {
|
||||
// utf-16be decoder flag is set
|
||||
// (utf-16 lead byte << 8) + byte.
|
||||
code_unit = (this.utf16_lead_byte << 8) + bite
|
||||
} else {
|
||||
// utf-16be decoder flag is unset
|
||||
// (byte << 8) + utf-16 lead byte.
|
||||
code_unit = (bite << 8) + this.utf16_lead_byte
|
||||
}
|
||||
// Then set utf-16 lead byte to null.
|
||||
this.utf16_lead_byte = null
|
||||
|
||||
// 5. If utf-16 lead surrogate is not null, let lead surrogate
|
||||
// be utf-16 lead surrogate, set utf-16 lead surrogate to null,
|
||||
// and then run these substeps:
|
||||
if (this.utf16_lead_surrogate !== null) {
|
||||
const lead_surrogate = this.utf16_lead_surrogate
|
||||
this.utf16_lead_surrogate = null
|
||||
|
||||
// 1. If code unit is in the range U+DC00 to U+DFFF,
|
||||
// inclusive, return a code point whose value is 0x10000 +
|
||||
// ((lead surrogate − 0xD800) << 10) + (code unit − 0xDC00).
|
||||
if (inRange(code_unit, 0xDC00, 0xDFFF)) {
|
||||
return 0x10000 + (lead_surrogate - 0xD800) * 0x400 +
|
||||
(code_unit - 0xDC00)
|
||||
}
|
||||
|
||||
// 2. Prepend the sequence resulting of converting code unit
|
||||
// to bytes using utf-16be decoder flag to stream and return
|
||||
// error.
|
||||
stream.prepend(convertCodeUnitToBytes(code_unit, this.utf16_be))
|
||||
return decoderError(this.fatal)
|
||||
}
|
||||
|
||||
// 6. If code unit is in the range U+D800 to U+DBFF, inclusive,
|
||||
// set utf-16 lead surrogate to code unit and return continue.
|
||||
if (inRange(code_unit, 0xD800, 0xDBFF)) {
|
||||
this.utf16_lead_surrogate = code_unit
|
||||
return null
|
||||
}
|
||||
|
||||
// 7. If code unit is in the range U+DC00 to U+DFFF, inclusive,
|
||||
// return error.
|
||||
if (inRange(code_unit, 0xDC00, 0xDFFF))
|
||||
return decoderError(this.fatal)
|
||||
|
||||
// 8. Return code point code unit.
|
||||
return code_unit
|
||||
}
|
||||
}
|
||||
|
||||
// 15.2.2 shared utf-16 encoder
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class UTF16Encoder {
|
||||
/**
|
||||
* @param {boolean} [utf16_be] True if big-endian, false if little-endian.
|
||||
*/
|
||||
constructor(utf16_be = false) {
|
||||
this.utf16_be = utf16_be
|
||||
}
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
*/
|
||||
handler(stream, code_point) {
|
||||
// 1. If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is in the range U+0000 to U+FFFF, inclusive,
|
||||
// return the sequence resulting of converting code point to
|
||||
// bytes using utf-16be encoder flag.
|
||||
if (inRange(code_point, 0x0000, 0xFFFF))
|
||||
return convertCodeUnitToBytes(code_point, this.utf16_be)
|
||||
|
||||
// 3. Let lead be ((code point − 0x10000) >> 10) + 0xD800,
|
||||
// converted to bytes using utf-16be encoder flag.
|
||||
const lead = convertCodeUnitToBytes(
|
||||
((code_point - 0x10000) >> 10) + 0xD800, this.utf16_be)
|
||||
|
||||
// 4. Let trail be ((code point − 0x10000) & 0x3FF) + 0xDC00,
|
||||
// converted to bytes using utf-16be encoder flag.
|
||||
const trail = convertCodeUnitToBytes(
|
||||
((code_point - 0x10000) & 0x3FF) + 0xDC00, this.utf16_be)
|
||||
|
||||
// 5. Return a byte sequence of lead followed by trail.
|
||||
return lead.concat(trail)
|
||||
}
|
||||
}
|
||||
208
cat/tjs/lib/utf8.js
Normal file
@ -0,0 +1,208 @@
|
||||
import { inRange, decoderError, isASCIICodePoint,
|
||||
end_of_stream, finished } from './text_decoder_utils.js'
|
||||
|
||||
/**
|
||||
* @implements {Decoder}
|
||||
*/
|
||||
export class UTF8Decoder {
|
||||
/**
|
||||
* @param {{fatal: boolean}} options
|
||||
*/
|
||||
constructor(options) {
|
||||
const { fatal } = options
|
||||
|
||||
// utf-8's decoder's has an associated utf-8 code point, utf-8
|
||||
// bytes seen, and utf-8 bytes needed (all initially 0), a utf-8
|
||||
// lower boundary (initially 0x80), and a utf-8 upper boundary
|
||||
// (initially 0xBF).
|
||||
let /** @type {number} */ utf8_code_point = 0,
|
||||
/** @type {number} */ utf8_bytes_seen = 0,
|
||||
/** @type {number} */ utf8_bytes_needed = 0,
|
||||
/** @type {number} */ utf8_lower_boundary = 0x80,
|
||||
/** @type {number} */ utf8_upper_boundary = 0xBF
|
||||
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
* @return {?(number|!Array.<number>)} The next code point(s)
|
||||
* decoded, or null if not enough data exists in the input
|
||||
* stream to decode a complete code point.
|
||||
*/
|
||||
this.handler = function(stream, bite) {
|
||||
// 1. If byte is end-of-stream and utf-8 bytes needed is not 0,
|
||||
// set utf-8 bytes needed to 0 and return error.
|
||||
if (bite === end_of_stream && utf8_bytes_needed !== 0) {
|
||||
utf8_bytes_needed = 0
|
||||
return decoderError(fatal)
|
||||
}
|
||||
|
||||
// 2. If byte is end-of-stream, return finished.
|
||||
if (bite === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 3. If utf-8 bytes needed is 0, based on byte:
|
||||
if (utf8_bytes_needed === 0) {
|
||||
// 0x00 to 0x7F
|
||||
if (inRange(bite, 0x00, 0x7F)) {
|
||||
// Return a code point whose value is byte.
|
||||
return bite
|
||||
}
|
||||
|
||||
// 0xC2 to 0xDF
|
||||
else if (inRange(bite, 0xC2, 0xDF)) {
|
||||
// 1. Set utf-8 bytes needed to 1.
|
||||
utf8_bytes_needed = 1
|
||||
|
||||
// 2. Set UTF-8 code point to byte & 0x1F.
|
||||
utf8_code_point = bite & 0x1F
|
||||
}
|
||||
|
||||
// 0xE0 to 0xEF
|
||||
else if (inRange(bite, 0xE0, 0xEF)) {
|
||||
// 1. If byte is 0xE0, set utf-8 lower boundary to 0xA0.
|
||||
if (bite === 0xE0)
|
||||
utf8_lower_boundary = 0xA0
|
||||
// 2. If byte is 0xED, set utf-8 upper boundary to 0x9F.
|
||||
if (bite === 0xED)
|
||||
utf8_upper_boundary = 0x9F
|
||||
// 3. Set utf-8 bytes needed to 2.
|
||||
utf8_bytes_needed = 2
|
||||
// 4. Set UTF-8 code point to byte & 0xF.
|
||||
utf8_code_point = bite & 0xF
|
||||
}
|
||||
|
||||
// 0xF0 to 0xF4
|
||||
else if (inRange(bite, 0xF0, 0xF4)) {
|
||||
// 1. If byte is 0xF0, set utf-8 lower boundary to 0x90.
|
||||
if (bite === 0xF0)
|
||||
utf8_lower_boundary = 0x90
|
||||
// 2. If byte is 0xF4, set utf-8 upper boundary to 0x8F.
|
||||
if (bite === 0xF4)
|
||||
utf8_upper_boundary = 0x8F
|
||||
// 3. Set utf-8 bytes needed to 3.
|
||||
utf8_bytes_needed = 3
|
||||
// 4. Set UTF-8 code point to byte & 0x7.
|
||||
utf8_code_point = bite & 0x7
|
||||
}
|
||||
|
||||
// Otherwise
|
||||
else {
|
||||
// Return error.
|
||||
return decoderError(fatal)
|
||||
}
|
||||
|
||||
// Return continue.
|
||||
return null
|
||||
}
|
||||
|
||||
// 4. If byte is not in the range utf-8 lower boundary to utf-8
|
||||
// upper boundary, inclusive, run these substeps:
|
||||
if (!inRange(bite, utf8_lower_boundary, utf8_upper_boundary)) {
|
||||
// 1. Set utf-8 code point, utf-8 bytes needed, and utf-8
|
||||
// bytes seen to 0, set utf-8 lower boundary to 0x80, and set
|
||||
// utf-8 upper boundary to 0xBF.
|
||||
utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0
|
||||
utf8_lower_boundary = 0x80
|
||||
utf8_upper_boundary = 0xBF
|
||||
|
||||
// 2. Prepend byte to stream.
|
||||
stream.prepend(bite)
|
||||
|
||||
// 3. Return error.
|
||||
return decoderError(fatal)
|
||||
}
|
||||
|
||||
// 5. Set utf-8 lower boundary to 0x80 and utf-8 upper boundary
|
||||
// to 0xBF.
|
||||
utf8_lower_boundary = 0x80
|
||||
utf8_upper_boundary = 0xBF
|
||||
|
||||
// 6. Set UTF-8 code point to (UTF-8 code point << 6) | (byte &
|
||||
// 0x3F)
|
||||
utf8_code_point = (utf8_code_point << 6) | (bite & 0x3F)
|
||||
|
||||
// 7. Increase utf-8 bytes seen by one.
|
||||
utf8_bytes_seen += 1
|
||||
|
||||
// 8. If utf-8 bytes seen is not equal to utf-8 bytes needed,
|
||||
// continue.
|
||||
if (utf8_bytes_seen !== utf8_bytes_needed)
|
||||
return null
|
||||
|
||||
// 9. Let code point be utf-8 code point.
|
||||
var code_point = utf8_code_point
|
||||
|
||||
// 10. Set utf-8 code point, utf-8 bytes needed, and utf-8 bytes
|
||||
// seen to 0.
|
||||
utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0
|
||||
|
||||
// 11. Return a code point whose value is code point.
|
||||
return code_point
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9.1.2 utf-8 encoder
|
||||
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class UTF8Encoder {
|
||||
constructor() {
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
* @return {(number|!Array.<number>)} Byte(s) to emit.
|
||||
*/
|
||||
this.handler = function(stream, code_point) {
|
||||
// 1. If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is an ASCII code point, return a byte whose
|
||||
// value is code point.
|
||||
if (isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 3. Set count and offset based on the range code point is in:
|
||||
var count, offset
|
||||
// U+0080 to U+07FF, inclusive:
|
||||
if (inRange(code_point, 0x0080, 0x07FF)) {
|
||||
// 1 and 0xC0
|
||||
count = 1
|
||||
offset = 0xC0
|
||||
}
|
||||
// U+0800 to U+FFFF, inclusive:
|
||||
else if (inRange(code_point, 0x0800, 0xFFFF)) {
|
||||
// 2 and 0xE0
|
||||
count = 2
|
||||
offset = 0xE0
|
||||
}
|
||||
// U+10000 to U+10FFFF, inclusive:
|
||||
else if (inRange(code_point, 0x10000, 0x10FFFF)) {
|
||||
// 3 and 0xF0
|
||||
count = 3
|
||||
offset = 0xF0
|
||||
}
|
||||
|
||||
// 4. Let bytes be a byte sequence whose first byte is (code
|
||||
// point >> (6 × count)) + offset.
|
||||
var bytes = [(code_point >> (6 * count)) + offset]
|
||||
|
||||
// 5. Run these substeps while count is greater than 0:
|
||||
while (count > 0) {
|
||||
// 1. Set temp to code point >> (6 × (count − 1)).
|
||||
var temp = code_point >> (6 * (count - 1))
|
||||
|
||||
// 2. Append to bytes 0x80 | (temp & 0x3F).
|
||||
bytes.push(0x80 | (temp & 0x3F))
|
||||
|
||||
// 3. Decrease count by one.
|
||||
count -= 1
|
||||
}
|
||||
|
||||
// 6. Return bytes bytes, in order.
|
||||
return bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
228
cat/tjs/lib/utils.js
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* @File : utils.js
|
||||
* @Author : jade
|
||||
* @Date : 2024/1/25 15:01
|
||||
* @Email : jadehh@1ive.com
|
||||
* @Software : Samples
|
||||
* @Desc :
|
||||
*/
|
||||
import {Crypto} from "./cat.js";
|
||||
import {TextDecoder} from "./TextDecoder.js";
|
||||
// import {TextDecoder} from "text-decoding";
|
||||
|
||||
let CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36";
|
||||
const MOBILEUA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1';
|
||||
let RESOURCEURL = "https://gh.con.sh/https://raw.githubusercontent.com/jadehh/TV/js"
|
||||
|
||||
function isSub(ext) {
|
||||
return ext === "srt" || ext === "ass" || ext === "ssa";
|
||||
}
|
||||
|
||||
function isNumeric(str) {
|
||||
return !isNaN(parseInt(str));
|
||||
}
|
||||
|
||||
function getSize(size) {
|
||||
if (size <= 0) return "";
|
||||
if (size > 1024 * 1024 * 1024 * 1024.0) {
|
||||
size /= (1024 * 1024 * 1024 * 1024.0);
|
||||
return size.toFixed(2) + "TB";
|
||||
} else if (size > 1024 * 1024 * 1024.0) {
|
||||
size /= (1024 * 1024 * 1024.0);
|
||||
return size.toFixed(2) + "GB";
|
||||
} else if (size > 1024 * 1024.0) {
|
||||
size /= (1024 * 1024.0);
|
||||
return size.toFixed(2) + "MB";
|
||||
} else {
|
||||
size /= 1024.0;
|
||||
return size.toFixed(2) + "KB";
|
||||
}
|
||||
}
|
||||
|
||||
function removeExt(text) {
|
||||
return text.indexOf('.') > -1 ? text.substring(0, text.lastIndexOf(".")) : text;
|
||||
}
|
||||
|
||||
async function log(str) {
|
||||
console.debug(str);
|
||||
}
|
||||
|
||||
function isVideoFormat(url) {
|
||||
var RULE = /http((?!http).){12,}?\.(m3u8|mp4|flv|avi|mkv|rm|wmv|mpg|m4a|mp3)\?.*|http((?!http).){12,}\.(m3u8|mp4|flv|avi|mkv|rm|wmv|mpg|m4a|mp3)|http((?!http).)*?video\/tos*/;
|
||||
if (url.indexOf("url=http") > -1 || url.indexOf(".js") > -1 || url.indexOf(".css") > -1 || url.indexOf(".html") > -1) {
|
||||
return false;
|
||||
}
|
||||
return RULE.test(url);
|
||||
}
|
||||
|
||||
function jsonParse(input, json) {
|
||||
var jsonPlayData = JSON.parse(json);
|
||||
var url = jsonPlayData.url;
|
||||
if (url.startsWith("//")) {
|
||||
url = "https:" + url;
|
||||
}
|
||||
if (!url.startsWith("http")) {
|
||||
return null;
|
||||
}
|
||||
if (url === input) {
|
||||
if (!isVideoFormat(url)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
var headers = {};
|
||||
var ua = jsonPlayData["user-agent"] || "";
|
||||
if (ua.trim().length > 0) {
|
||||
headers["User-Agent"] = " " + ua;
|
||||
}
|
||||
var referer = jsonPlayData.referer || "";
|
||||
if (referer.trim().length > 0) {
|
||||
headers["Referer"] = " " + referer;
|
||||
}
|
||||
var taskResult = {
|
||||
header: headers, url: url
|
||||
};
|
||||
return taskResult;
|
||||
}
|
||||
|
||||
function debug(obj) {
|
||||
for (var a in obj) {
|
||||
if (typeof (obj[a]) == "object") {
|
||||
debug(obj[a]); //递归遍历
|
||||
} else {
|
||||
console.debug(a + "=" + obj[a]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function objectToStr(params = null, isBase64Encode = false) {
|
||||
let params_str_list = []
|
||||
if (params !== null) {
|
||||
for (const key of Object.keys(params)) {
|
||||
if (isBase64Encode) {
|
||||
params_str_list.push(`${key}=${encodeURIComponent(params[key])}`)
|
||||
} else {
|
||||
params_str_list.push(`${key}=${params[key]}`)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return params_str_list.join("&")
|
||||
}
|
||||
|
||||
function sleep(delay) {
|
||||
const start = (new Date()).getTime();
|
||||
while ((new Date()).getTime() - start < delay * 1000) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
function getStrByRegex(pattern, str) {
|
||||
let matcher = pattern.exec(str);
|
||||
if (matcher !== null) {
|
||||
if (matcher.length >= 1) {
|
||||
if (matcher.length >= 1) return matcher[1]
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function getStrByRegexDefault(pattern, str) {
|
||||
let matcher = pattern.exec(str);
|
||||
if (matcher !== null) {
|
||||
if (matcher.length >= 1) {
|
||||
if (matcher.length >= 1) return matcher[1]
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function base64Encode(text) {
|
||||
return Crypto.enc.Base64.stringify(Crypto.enc.Utf8.parse(text));
|
||||
}
|
||||
|
||||
function base64Decode(text) {
|
||||
return Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(text));
|
||||
}
|
||||
|
||||
function unescape(code) {
|
||||
return code.replace(/\\('|\\)/g, "$1").replace(/[\r\t\n]/g, " ");
|
||||
}
|
||||
|
||||
function decode(buffer, encode_type) {
|
||||
let decoder = new TextDecoder(encode_type)
|
||||
return decoder.decode(buffer)
|
||||
}
|
||||
|
||||
function getHost(url) {
|
||||
let url_list = url.split("/")
|
||||
return url_list[0] + "//" + url_list[2]
|
||||
}
|
||||
|
||||
function unquote(str) {
|
||||
return str.replace(/^"(.*)"$/, '$1');
|
||||
}
|
||||
|
||||
function md5Encode(text) {
|
||||
return Crypto.MD5(Crypto.enc.Utf8.parse(text)).toString();
|
||||
}
|
||||
|
||||
|
||||
function getUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
}) + "-" + new Date().getTime().toString(16)
|
||||
|
||||
}
|
||||
|
||||
function objToList(list, key, split_value = "*") {
|
||||
let value_list = []
|
||||
for (const dic of list) {
|
||||
value_list.push(dic[key])
|
||||
}
|
||||
return value_list.join(split_value)
|
||||
}
|
||||
|
||||
function getPropertiesAndMethods(obj) {
|
||||
let str = ""
|
||||
for (let key in obj) {
|
||||
if (typeof obj[key] === 'function') {
|
||||
str = str + "方法名:" + key + '()' + "\n";
|
||||
} else {
|
||||
str = str + "属性名:"+(key + ': ' + obj[key]) + "\n";
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
let patternAli = /(https:\/\/www\.aliyundrive\.com\/s\/[^"]+|https:\/\/www\.alipan\.com\/s\/[^"]+)/
|
||||
|
||||
export {
|
||||
isSub,
|
||||
getSize,
|
||||
removeExt,
|
||||
log,
|
||||
isVideoFormat,
|
||||
jsonParse,
|
||||
debug,
|
||||
CHROME,
|
||||
objectToStr,
|
||||
sleep,
|
||||
getStrByRegex,
|
||||
RESOURCEURL,
|
||||
base64Encode,
|
||||
base64Decode,
|
||||
patternAli,
|
||||
unescape,
|
||||
decode,
|
||||
MOBILEUA,
|
||||
isNumeric,
|
||||
getHost,
|
||||
unquote,
|
||||
md5Encode,
|
||||
getStrByRegexDefault,
|
||||
getUUID,
|
||||
objToList,
|
||||
getPropertiesAndMethods
|
||||
};
|
||||
52
cat/tjs/lib/vod.js
Normal file
@ -0,0 +1,52 @@
|
||||
// LocalAddress = "http://192.168.29.156:8099"
|
||||
import * as Utils from "./utils.js";
|
||||
export class VodShort {
|
||||
constructor() {
|
||||
this.vod_id = "" //id
|
||||
this.vod_name = "" //名称
|
||||
this.vod_pic = Utils.RESOURCEURL + "/resources/ali.jpg" //图片
|
||||
this.vod_remarks = "" //备注
|
||||
}
|
||||
|
||||
to_dict() {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
|
||||
load_dic(json_str) {
|
||||
let obj = JSON.parse(json_str)
|
||||
for (let propName in obj) {
|
||||
this[propName] = obj[propName];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class VodDetail extends VodShort {
|
||||
constructor() {
|
||||
super();
|
||||
this.type_name = "" // 类别
|
||||
this.vod_year = "" // 年份
|
||||
this.vod_area = "" // 地区
|
||||
this.vod_actor = "" // 导演
|
||||
this.vod_director = "" // 演员
|
||||
this.vod_content = "" // 剧情
|
||||
this.vod_play_from = "" // 播放格式
|
||||
this.vod_play_url = "" // 播放连接
|
||||
}
|
||||
|
||||
to_short() {
|
||||
let vodShort = new VodShort()
|
||||
vodShort.load_dic(this.to_dict())
|
||||
return vodShort
|
||||
}
|
||||
|
||||
load_dic(json_str) {
|
||||
let obj = JSON.parse(json_str)
|
||||
for (let propName in JSON.parse(this.to_dict())) {
|
||||
this[propName] = obj[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
56
cat/tjs/lib/x-user-defined.js
Normal file
@ -0,0 +1,56 @@
|
||||
import { inRange, encoderError, end_of_stream, finished, isASCIIByte, isASCIICodePoint } from './text_decoder_utils.js'
|
||||
|
||||
// 15.5 x-user-defined
|
||||
|
||||
// 15.5.1 x-user-defined decoder
|
||||
/**
|
||||
* @implements {Decoder}
|
||||
*/
|
||||
export class XUserDefinedDecoder {
|
||||
/**
|
||||
* @param {Stream} stream The stream of bytes being decoded.
|
||||
* @param {number} bite The next byte read from the stream.
|
||||
*/
|
||||
handler(stream, bite) {
|
||||
// 1. If byte is end-of-stream, return finished.
|
||||
if (bite === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If byte is an ASCII byte, return a code point whose value
|
||||
// is byte.
|
||||
if (isASCIIByte(bite))
|
||||
return bite
|
||||
|
||||
// 3. Return a code point whose value is 0xF780 + byte − 0x80.
|
||||
return 0xF780 + bite - 0x80
|
||||
}
|
||||
}
|
||||
|
||||
// 15.5.2 x-user-defined encoder
|
||||
/**
|
||||
* @implements {Encoder}
|
||||
*/
|
||||
export class XUserDefinedEncoder {
|
||||
/**
|
||||
* @param {Stream} stream Input stream.
|
||||
* @param {number} code_point Next code point read from the stream.
|
||||
*/
|
||||
handler(stream, code_point) {
|
||||
// 1.If code point is end-of-stream, return finished.
|
||||
if (code_point === end_of_stream)
|
||||
return finished
|
||||
|
||||
// 2. If code point is an ASCII code point, return a byte whose
|
||||
// value is code point.
|
||||
if (isASCIICodePoint(code_point))
|
||||
return code_point
|
||||
|
||||
// 3. If code point is in the range U+F780 to U+F7FF, inclusive,
|
||||
// return a byte whose value is code point − 0xF780 + 0x80.
|
||||
if (inRange(code_point, 0xF780, 0xF7FF))
|
||||
return code_point - 0xF780 + 0x80
|
||||
|
||||
// 4. Return error with code point.
|
||||
return encoderError(code_point)
|
||||
}
|
||||
}
|
||||
307
cat/tjs/open_config.json
Normal file
@ -0,0 +1,307 @@
|
||||
{
|
||||
"video": {
|
||||
"sites": [
|
||||
{
|
||||
"key": "aiyingshi",
|
||||
"name": "🚀┃爱影视┃🚀",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/aiyingshi.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "gitcafe",
|
||||
"name": "🦊┃阿里纸条┃🦊",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/gitcafe.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
},
|
||||
{
|
||||
"key": "huya",
|
||||
"name": "🐯┃虎牙直播┃🐯",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/huya.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "nivod",
|
||||
"name": "👑┃泥视频┃墙👑",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/nivod.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"code\": 1}"
|
||||
},
|
||||
{
|
||||
"key": "feifan",
|
||||
"name": "🥗┃非凡资源┃🥗",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/feifan.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "nangua",
|
||||
"name": "🎃┃南瓜影视┃🎃",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/nangua.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "mp4movie",
|
||||
"name": "🍚┃Mp4电影┃🍚",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/mp4movie.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "jiujiuliu",
|
||||
"name": "🥃┃九九六影视┃🥃",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/jiujiuliu.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "pan_search",
|
||||
"name": "🗂️┃阿里盘搜┃🗂️",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/pan_search.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
},
|
||||
{
|
||||
"key": "dygangs",
|
||||
"name": "🏖️┃电影港┃🏖️",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/dygangs.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "douban",
|
||||
"name": "🍥┃豆瓣┃🍥",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/douban.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "star",
|
||||
"name": "☄️┃星视界┃墙☄️",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/star.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "xb6v",
|
||||
"name": "🧲┃磁力新6V┃🧲",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/xb6v.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "alipansou",
|
||||
"name": "😸┃阿里猫狸┃😸",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/alipansou.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
},
|
||||
{
|
||||
"key": "kankan70",
|
||||
"name": "📺┃70看看┃📺",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/kankan70.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "kuaikan",
|
||||
"name": "🛥︎┃快看视频┃🛥︎",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/kuaikan.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "liangzi",
|
||||
"name": "🐝┃量子资源┃🐝",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/liangzi.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"code\": 1}"
|
||||
},
|
||||
{
|
||||
"key": "mxanime",
|
||||
"name": "🍒┃MX动漫┃🍒",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/mxanime.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "yiqikan",
|
||||
"name": "🛫┃一起看┃🛫",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/yiqikan.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "wogg",
|
||||
"name": "💂┃阿里玩偶┃💂",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/wogg.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
},
|
||||
{
|
||||
"key": "ikanbot",
|
||||
"name": "🤖┃爱看机器人┃🤖",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/ikanbot.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "aliyunpanshare",
|
||||
"name": "🥏┃阿里云盘分享┃🥏",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/aliyunpanshare.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
},
|
||||
{
|
||||
"key": "newvision",
|
||||
"name": "🐼┃新视觉影院┃🐼",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/newvision.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "haiwaikan",
|
||||
"name": "☕┃海外看┃☕墙",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/haiwaikan.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "bilibili",
|
||||
"name": "🏰┃哔哩哔哩┃🏰",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/bilibili.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"cookie\": \"buvid3=02675249-8ED3-C418-87F5-59E18316459714816infoc; b_nut=1704421014; _uuid=5D435F74-F574-D9AB-62C1-B9294DE465D913102infoc; buvid_fp=e8c5650c749398e9b5cad3f3ddb5081e; buvid4=007E85D1-52C1-7E6E-07CF-837FFBC9349516677-024010502-J5vTDSZDCw4fNnXRejbSVg%3D%3D; rpdid=|()kYJmulRu0J'u~|RRJl)JR; PVID=1; SESSDATA=3be091d3%2C1720332009%2C699ed%2A11CjAcCdwXG5kY1umhCOpQHOn_WP7L9xFBfWO7KKd4BPweodpR6VyIfeNyPiRmkr5jCqsSVjg0R0dZOVVHRUo3RnhPRTZFc3JPbGdiUjFCdHpiRDhiTkticmdKTjVyS1VhbDdvNjFMSDJlbUJydUlRdjFUNGFBNkJlV2ZTa0N1Q1BEVi1QYTQzTUh3IIEC; bili_jct=b0ee7b5d3f27df893545d811d95506d4; DedeUserID=78014638; DedeUserID__ckMd5=4c8c5d65065e468a; enable_web_push=DISABLE; header_theme_version=CLOSE; home_feed_column=5; CURRENT_BLACKGAP=0; CURRENT_FNVAL=4048; b_lsid=75E916AA_18EA1A8D995; bsource=search_baidu; FEED_LIVE_VERSION=V_HEADER_LIVE_NO_POP; browser_resolution=1507-691; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTIzNjk5MTMsImlhdCI6MTcxMjExMDY1MywicGx0IjotMX0.8zQW_fNTCSBlK_JkHnzu3gDw62wuTK1qgKcbGec3swM; bili_ticket_expires=171236985\"}"
|
||||
},
|
||||
{
|
||||
"key": "changzhang",
|
||||
"name": "🏭️┃厂长直连┃🏭️",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/changzhang.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false, \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
},
|
||||
{
|
||||
"key": "jianpian",
|
||||
"name": "🌼┃荐片┃🌼",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/jianpian.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "sp360",
|
||||
"name": "🥎┃360影视┃🥎",
|
||||
"playerType": 0,
|
||||
"type": 3,
|
||||
"api": "./js/sp360.js",
|
||||
"timeout": 30,
|
||||
"ext": "{\"box\": \"CatOpen\", \"danmu\": false}"
|
||||
},
|
||||
{
|
||||
"key": "push_agent",
|
||||
"name": "┃推送┃",
|
||||
"type": 3,
|
||||
"api": "./js/push_agent.js",
|
||||
"ext": "{\"box\": \"CatOpen\", \"token\": \"6827db23e5474d02a07fd7431d3d5a5a\"}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"read": {
|
||||
"sites": [
|
||||
{
|
||||
"key": "audiomack",
|
||||
"name": "🎵┃音声┃🎵",
|
||||
"type": 3,
|
||||
"api": "./js/audiomack.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
},
|
||||
{
|
||||
"key": "copymanga",
|
||||
"name": "🧑🎨|拷贝漫画|🧑🎨",
|
||||
"type": 3,
|
||||
"api": "./js/copymanga.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
},
|
||||
{
|
||||
"key": "bqg_open",
|
||||
"name": "📚︎┃笔趣阁┃📚︎",
|
||||
"type": 10,
|
||||
"api": "./js/bqg_open.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
},
|
||||
{
|
||||
"key": "dj0898_book_open",
|
||||
"name": "🎵┃世纪DJ音乐网┃🎵",
|
||||
"type": 3,
|
||||
"api": "./js/dj0898_book_open.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
},
|
||||
{
|
||||
"key": "bookan",
|
||||
"name": "🎵┃看书┃🎵",
|
||||
"type": 3,
|
||||
"api": "./js/bookan.js",
|
||||
"ext": "{\"box\": \"CatOpen\"}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pan": {
|
||||
"sites": []
|
||||
}
|
||||
}
|
||||
BIN
cat/tjs/resources/ali.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
cat/tjs/resources/alipay.jpg
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
cat/tjs/resources/cili.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
cat/tjs/resources/dygang.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
cat/tjs/resources/dytt.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
cat/tjs/resources/error.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
cat/tjs/resources/liangzi.jpg
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
cat/tjs/resources/push.jpg
Normal file
|
After Width: | Height: | Size: 13 KiB |