364 lines
13 KiB
JavaScript
364 lines
13 KiB
JavaScript
/*
|
|
* @File : cntv.js
|
|
* @Author : jade
|
|
* @Date : 2024/4/25 10:26
|
|
* @Email : jadehh@1ive.com
|
|
* @Software : Samples
|
|
* @Desc :
|
|
*/
|
|
import {Spider} from "./spider.js";
|
|
import {_} from "../lib/cat.js";
|
|
import * as Utils from "../lib/utils.js";
|
|
import {VodDetail, VodShort} from "../lib/vod.js";
|
|
|
|
class CNTVSpider extends Spider {
|
|
constructor() {
|
|
super();
|
|
this.siteUrl = "https://tv.cctv.com/m/index.shtml"
|
|
this.apiUrl = "https://api.app.cctv.com"
|
|
this.liveJsonUrl = "https://gh.con.sh/https://github.com/jadehh/LiveSpider/blob/main/json/live.json"
|
|
|
|
}
|
|
|
|
getName() {
|
|
return "🤵♂️┃中央影视┃🤵♂️"
|
|
}
|
|
|
|
getAppName() {
|
|
return "中央影视"
|
|
}
|
|
|
|
getJSName() {
|
|
return "cntv"
|
|
}
|
|
|
|
getType() {
|
|
return 3
|
|
}
|
|
|
|
async spiderInit() {
|
|
await super.spiderInit();
|
|
this.liveJson = JSON.parse(await this.fetch(this.liveJsonUrl, null, null))
|
|
}
|
|
|
|
async init(cfg) {
|
|
await super.init(cfg);
|
|
await this.spiderInit()
|
|
}
|
|
|
|
async getFilterByLive(dataList) {
|
|
let extend_list = []
|
|
let extend_dic = {"key": "live", "name": "直播", "value": []}
|
|
for (const data of dataList) {
|
|
if (data["appBarTitle"] !== "最近常看") {
|
|
extend_dic["value"].push({"n": data["appBarTitle"], "v": data["pageId"]})
|
|
}
|
|
}
|
|
extend_list.push(extend_dic)
|
|
return extend_list
|
|
}
|
|
|
|
arrayIsinclude(str, items) {
|
|
let isInclude = false
|
|
for (const data of items) {
|
|
if (str === data["title"]) {
|
|
return true
|
|
}
|
|
}
|
|
return isInclude
|
|
}
|
|
|
|
async getFilterByTv(dataList) {
|
|
let extend_list = []
|
|
for (const data of dataList) {
|
|
let add_year_status = false
|
|
let extend_dic = {"key": data["classname"], "name": data["title"], "value": []}
|
|
for (const extendData of data["items"]) {
|
|
if (data["classname"] === "nianfen") {
|
|
if (!this.arrayIsinclude("2024", data["items"]) && extendData["title"] !== "全部" && !add_year_status) {
|
|
extend_dic["value"].push({"n": "2024", "v": "2024"})
|
|
add_year_status = true
|
|
}
|
|
}
|
|
extend_dic["value"].push({"n": extendData["title"], "v": extendData["title"]})
|
|
}
|
|
extend_list.push(extend_dic)
|
|
}
|
|
return extend_list
|
|
}
|
|
|
|
async setClasses() {
|
|
let liveTypeId = "cctvlive"
|
|
let liveApi = this.apiUrl + `/api/navigation/iphone/AppStore/7.9.4/${liveTypeId}`
|
|
let liveJson = JSON.parse(await this.fetch(liveApi, null, this.getHeader()))
|
|
let extend_list = await this.getFilterByLive(liveJson["data"]["templates"])
|
|
let defaultLiveId = extend_list[0]["value"][0]["v"]
|
|
this.classes.push(this.getTypeDic("直播", defaultLiveId))
|
|
this.filterObj[defaultLiveId] = extend_list
|
|
let tvApi = "https://cbox.cctv.com/cboxpcvip/online2022/yxg/data1.jsonp?=pk"
|
|
let tvContent = await this.fetch(tvApi, null, this.getHeader())
|
|
let tvJSon = JSON.parse(tvContent.replaceAll("pk(", "").replaceAll(")", ""))
|
|
for (const data of tvJSon["data"]) {
|
|
let typeName = data["title"]
|
|
this.classes.push(this.getTypeDic(typeName, typeName))
|
|
this.filterObj[typeName] = await this.getFilterByTv(data["templates"])
|
|
|
|
}
|
|
}
|
|
|
|
parseVodShortByJson(items) {
|
|
let vod_list = []
|
|
for (const item of items) {
|
|
let vodShort = new VodShort()
|
|
vodShort.vod_pic = item["img1"]
|
|
if (_.isEmpty(vodShort.vod_pic)) {
|
|
vodShort.vod_pic = item["epgHorizontalPic"]
|
|
vodShort.vod_id = "live-" + item["epgChnlChar"] + "-" + vodShort.vod_pic
|
|
} else {
|
|
vodShort.vod_id = "play-" + item["playid"] + "-" + vodShort.vod_pic
|
|
vodShort.vod_pic = item["img1"]
|
|
}
|
|
vodShort.vod_name = item["title"]
|
|
vod_list.push(vodShort)
|
|
}
|
|
return vod_list
|
|
}
|
|
|
|
parseVodShortByTvJson(items) {
|
|
let vod_list = []
|
|
for (const item of items) {
|
|
let vodShort = new VodShort()
|
|
//关键是如何获取GUID 2d3224585904496ea837f682da0c4aa6
|
|
vodShort.vod_id = "url-" + item["vsetid"]
|
|
vodShort.vod_name = item["title"]
|
|
vodShort.vod_pic = item["image"]
|
|
vodShort.vod_remarks = item["sc"]
|
|
vod_list.push(vodShort)
|
|
}
|
|
return vod_list
|
|
}
|
|
|
|
async parseVodShortListFromJson(objList) {
|
|
let vod_list = []
|
|
let top_status = false
|
|
for (const data of objList) {
|
|
if (data["title"] === "今日热点") {
|
|
top_status = true
|
|
} else if (!_.isEmpty(data["title"])) {
|
|
if (top_status) {
|
|
break
|
|
}
|
|
}
|
|
if (top_status) {
|
|
vod_list = [...vod_list, ...this.parseVodShortByJson(data["items"])]
|
|
}
|
|
}
|
|
return vod_list
|
|
}
|
|
|
|
async getLiveUrl(channel_id, obj) {
|
|
let liveApiUrl = `https://vdn.live.cntv.cn/api2/live.do?channel=pd://cctv_p2p_hd${channel_id}&client=iosapp`
|
|
let liveResponse = await req(liveApiUrl, {"headers": this.getHeader()})
|
|
let liveJson = JSON.parse(liveResponse["content"])
|
|
let playList = {}
|
|
let channelName = obj["channelName"].split(" ")[0].replaceAll("-", "").toLowerCase()
|
|
let liveUrl = this.liveJson[channelName] ?? liveJson["hls_url"]["hls2"]
|
|
playList["直播"] = ["点击播放$" + liveUrl]
|
|
await this.jadeLog.info(`liveJson:${JSON.stringify(liveJson)}`)
|
|
let vod_items = []
|
|
if (this.liveJson[channelName] !== undefined) {
|
|
|
|
} else {
|
|
for (const data of obj["program"]) {
|
|
let episodeName = data["showTime"] + "-" + data["t"]
|
|
let episodeUrl = liveUrl + `?begintimeabs=${data["st"] * 1000}&endtimeabs=${data["et"] * 1000}`
|
|
vod_items.push(episodeName + "$" + episodeUrl)
|
|
}
|
|
}
|
|
if (vod_items.length > 0){
|
|
playList["点播"] = vod_items.join("#")
|
|
}
|
|
return playList
|
|
}
|
|
|
|
|
|
async getVideoUrl(guid) {
|
|
return {"中央影视": ['点击播放' + '$' + guid].join("#")}
|
|
}
|
|
|
|
async parseVodDetailfromJson(id, obj, pic) {
|
|
let vodDetail = new VodDetail()
|
|
let $;
|
|
let guid;
|
|
if (obj["url"] !== undefined) {
|
|
vodDetail.vod_name = obj["title"]
|
|
vodDetail.vod_pic = obj["img"]
|
|
vodDetail.type_name = obj["tags"]
|
|
vodDetail.vod_year = obj["time"]
|
|
vodDetail.vod_content = obj["vset_brief"]
|
|
vodDetail.vod_director = obj["vset_title"]
|
|
$ = await this.getHtml(obj["url"])
|
|
} else {
|
|
if (_.isEmpty(obj["lvUrl"])) {
|
|
vodDetail.vod_name = obj["channelName"]
|
|
vodDetail.vod_pic = pic
|
|
} else {
|
|
$ = await this.getHtml(obj["lvUrl"])
|
|
vodDetail.vod_name = $('[property$=title]')[0].attribs.content
|
|
vodDetail.vod_content = $('[property$=description]')[0].attribs.content
|
|
let pic = $('[property$=image]')[0].attribs.content
|
|
if (!pic.startsWith("http")) {
|
|
pic = "https:" + pic
|
|
}
|
|
vodDetail.vod_pic = pic
|
|
}
|
|
|
|
}
|
|
if (!_.isEmpty($)) {
|
|
guid = Utils.getStrByRegex(/var guid = "(.*?)"/, $.html())
|
|
}
|
|
let playlist
|
|
if (_.isEmpty(guid) && obj["url"] === undefined) {
|
|
playlist = await this.getLiveUrl(id, obj)
|
|
} else {
|
|
playlist = await this.getVideoUrl(guid)
|
|
}
|
|
vodDetail.vod_play_url = _.values(playlist).join('$$$');
|
|
vodDetail.vod_play_from = _.keys(playlist).join('$$$');
|
|
return vodDetail
|
|
}
|
|
|
|
async parseVodDetailFromJsonByTv(obj) {
|
|
let vodDetail = new VodDetail()
|
|
vodDetail.vod_name = obj["videoSetInfo"]["title"]
|
|
vodDetail.type_name = obj["videoSetInfo"]["sc"]
|
|
vodDetail.vod_pic = obj["videoSetInfo"]["image"]
|
|
vodDetail.vod_content = obj["videoSetInfo"]["brief"]
|
|
vodDetail.vod_area = obj["videoSetInfo"]["area"]
|
|
let playlist = {}
|
|
let vodItems = []
|
|
for (const data of obj["videoRoughCut"]) {
|
|
let title = data["title"].split("》").slice(-1)[0]
|
|
vodItems.push(title + "$" + data["guid"])
|
|
}
|
|
playlist["中央影视"] = vodItems.join("#")
|
|
vodDetail.vod_play_url = _.values(playlist).join('$$$');
|
|
vodDetail.vod_play_from = _.keys(playlist).join('$$$');
|
|
return vodDetail
|
|
}
|
|
|
|
async setHomeVod() {
|
|
let resJson = JSON.parse(await this.fetch(this.apiUrl + "/api/page/iphone/HandheldApplicationSink/7.0.0/158", null, this.getHeader()))
|
|
this.homeVodList = await this.parseVodShortListFromJson(resJson["data"]["templates"])
|
|
}
|
|
|
|
getExtendValue(extend, key) {
|
|
if (extend[key] !== undefined && extend[key] !== "全部") {
|
|
return extend[key]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
async setCategory(tid, pg, filter, extend) {
|
|
if (Utils.isNumeric(tid)) {
|
|
tid = extend["live"] ?? tid
|
|
let url = this.apiUrl + `/api/page/iphone/HandheldApplicationSink/7.0.0/${tid}`
|
|
let response = JSON.parse(await this.fetch(url, null, this.getHeader()))
|
|
this.vodList = this.parseVodShortByJson(response["data"]["templates"][0]["items"])
|
|
} else {
|
|
let letter = this.getExtendValue(extend, "zimu")
|
|
let area = this.getExtendValue(extend, "diqu")
|
|
let type = this.getExtendValue(extend, "leixing")
|
|
let year = this.getExtendValue(extend, "nianfen")
|
|
const limit = 12
|
|
let url = "https://api.cntv.cn" + `/newVideoset/getCboxVideoAlbumList`
|
|
let params = {
|
|
"channelid": "",
|
|
"sc": type,
|
|
"fc": tid,
|
|
"p": pg,
|
|
"n": limit,
|
|
"fl": letter,
|
|
"area": area,
|
|
"year": year,
|
|
"serviceId": "cbox"
|
|
}
|
|
let resJson = JSON.parse(await this.fetch(url, params, this.getHeader()))
|
|
this.vodList = this.parseVodShortByTvJson(resJson["data"]["list"])
|
|
}
|
|
|
|
}
|
|
|
|
async setDetail(id) {
|
|
//区分直播还是点播
|
|
let aList = id.split("-")
|
|
let playType = aList[0]
|
|
let pic = aList[2]
|
|
id = aList[1]
|
|
if (playType === "play") {
|
|
let resJson = JSON.parse(await this.fetch(`https://api.cntv.cn/video/videoinfoByGuid?serviceId=cbox&guid=${id}`, null, this.getHeader()))
|
|
this.vodDetail = await this.parseVodDetailfromJson(id, resJson, pic)
|
|
} else if (playType === "url") {
|
|
let url = `https://api.app.cctv.com/api/getVideoPageDetail?videoSetContentId=${id}`
|
|
let resJson = JSON.parse(await this.fetch(url, null, this.getHeader()))
|
|
this.vodDetail = await this.parseVodDetailFromJsonByTv(resJson["data"])
|
|
} else {
|
|
let content = (await this.fetch(`https://api.cntv.cn/epg/epginfo3?serviceId=shiyi&c=${id}&cb=LiveTileShow.prototype.getEpg`, null, this.getHeader())).replaceAll("LiveTileShow.prototype.getEpg(", "").replaceAll(");", "")
|
|
this.vodDetail = await this.parseVodDetailfromJson(id, JSON.parse(content)[id], pic)
|
|
}
|
|
|
|
}
|
|
|
|
async setSearch(wd, quick, pg) {
|
|
|
|
}
|
|
|
|
async setPlay(flag, id, flags) {
|
|
if (id.startsWith("http")) {
|
|
this.playUrl = id
|
|
let headers = this.getHeader()
|
|
headers["Referer"] = "https://tv.cctv.com/"
|
|
this.result.header = headers
|
|
} else {
|
|
this.playUrl = 'https://hls.cntv.myhwcdn.cn/asp/hls/2000/0303000a/3/default/' + id + '/2000.m3u8'
|
|
}
|
|
}
|
|
}
|
|
|
|
let spider = new CNTVSpider()
|
|
|
|
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, CNTVSpider} |