@ -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": []
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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};
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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}
|
||||
@ -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
|
||||
};
|
||||
@ -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
|
||||
};
|
||||
@ -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
|
||||
};
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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}
|
||||
@ -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
|
||||
@ -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}
|
||||
@ -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};
|
||||
@ -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}
|
||||
@ -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 {};
|
||||
}
|
||||
@ -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};
|
||||
@ -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
|
||||
@ -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};
|
||||
@ -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)
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -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}
|
||||
|
||||
|
||||
@ -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) {},
|
||||
// }
|
||||
@ -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
|
||||
};
|
||||
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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": []
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 13 KiB |