cntv.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*
  2. * @File : cntv.js
  3. * @Author : jade
  4. * @Date : 2024/4/25 10:26
  5. * @Email : jadehh@1ive.com
  6. * @Software : Samples
  7. * @Desc :
  8. */
  9. import {Spider} from "./spider.js";
  10. import {_} from "../lib/cat.js";
  11. import * as Utils from "../lib/utils.js";
  12. import {VodDetail, VodShort} from "../lib/vod.js";
  13. class CNTVSpider extends Spider {
  14. constructor() {
  15. super();
  16. this.siteUrl = "https://tv.cctv.com/m/index.shtml"
  17. this.apiUrl = "https://api.app.cctv.com"
  18. this.liveJsonUrl = "https://gh.con.sh/https://github.com/jadehh/LiveSpider/blob/main/json/live.json"
  19. }
  20. getName() {
  21. return "🤵‍♂️┃中央影视┃🤵‍♂️"
  22. }
  23. getAppName() {
  24. return "中央影视"
  25. }
  26. getJSName() {
  27. return "cntv"
  28. }
  29. getType() {
  30. return 3
  31. }
  32. async spiderInit() {
  33. await super.spiderInit();
  34. this.liveJson = JSON.parse(await this.fetch(this.liveJsonUrl, null, null))
  35. }
  36. async init(cfg) {
  37. await super.init(cfg);
  38. await this.spiderInit()
  39. }
  40. async getFilterByLive(dataList) {
  41. let extend_list = []
  42. let extend_dic = {"key": "live", "name": "直播", "value": []}
  43. for (const data of dataList) {
  44. if (data["appBarTitle"] !== "最近常看") {
  45. extend_dic["value"].push({"n": data["appBarTitle"], "v": data["pageId"]})
  46. }
  47. }
  48. extend_list.push(extend_dic)
  49. return extend_list
  50. }
  51. arrayIsinclude(str, items) {
  52. let isInclude = false
  53. for (const data of items) {
  54. if (str === data["title"]) {
  55. return true
  56. }
  57. }
  58. return isInclude
  59. }
  60. async getFilterByTv(dataList) {
  61. let extend_list = []
  62. for (const data of dataList) {
  63. let add_year_status = false
  64. let extend_dic = {"key": data["classname"], "name": data["title"], "value": []}
  65. for (const extendData of data["items"]) {
  66. if (data["classname"] === "nianfen") {
  67. if (!this.arrayIsinclude("2024", data["items"]) && extendData["title"] !== "全部" && !add_year_status) {
  68. extend_dic["value"].push({"n": "2024", "v": "2024"})
  69. add_year_status = true
  70. }
  71. }
  72. extend_dic["value"].push({"n": extendData["title"], "v": extendData["title"]})
  73. }
  74. extend_list.push(extend_dic)
  75. }
  76. return extend_list
  77. }
  78. async setClasses() {
  79. let liveTypeId = "cctvlive"
  80. let liveApi = this.apiUrl + `/api/navigation/iphone/AppStore/7.9.4/${liveTypeId}`
  81. let liveJson = JSON.parse(await this.fetch(liveApi, null, this.getHeader()))
  82. let extend_list = await this.getFilterByLive(liveJson["data"]["templates"])
  83. let defaultLiveId = extend_list[0]["value"][0]["v"]
  84. this.classes.push(this.getTypeDic("直播", defaultLiveId))
  85. this.filterObj[defaultLiveId] = extend_list
  86. let tvApi = "https://cbox.cctv.com/cboxpcvip/online2022/yxg/data1.jsonp?=pk"
  87. let tvContent = await this.fetch(tvApi, null, this.getHeader())
  88. let tvJSon = JSON.parse(tvContent.replaceAll("pk(", "").replaceAll(")", ""))
  89. for (const data of tvJSon["data"]) {
  90. let typeName = data["title"]
  91. this.classes.push(this.getTypeDic(typeName, typeName))
  92. this.filterObj[typeName] = await this.getFilterByTv(data["templates"])
  93. }
  94. }
  95. parseVodShortByJson(items) {
  96. let vod_list = []
  97. for (const item of items) {
  98. let vodShort = new VodShort()
  99. vodShort.vod_pic = item["img1"]
  100. if (_.isEmpty(vodShort.vod_pic)) {
  101. vodShort.vod_pic = item["epgHorizontalPic"]
  102. vodShort.vod_id = "live-" + item["epgChnlChar"] + "-" + vodShort.vod_pic
  103. } else {
  104. vodShort.vod_id = "play-" + item["playid"] + "-" + vodShort.vod_pic
  105. vodShort.vod_pic = item["img1"]
  106. }
  107. vodShort.vod_name = item["title"]
  108. vod_list.push(vodShort)
  109. }
  110. return vod_list
  111. }
  112. parseVodShortByTvJson(items) {
  113. let vod_list = []
  114. for (const item of items) {
  115. let vodShort = new VodShort()
  116. //关键是如何获取GUID 2d3224585904496ea837f682da0c4aa6
  117. vodShort.vod_id = "url-" + item["vsetid"]
  118. vodShort.vod_name = item["title"]
  119. vodShort.vod_pic = item["image"]
  120. vodShort.vod_remarks = item["sc"]
  121. vod_list.push(vodShort)
  122. }
  123. return vod_list
  124. }
  125. async parseVodShortListFromJson(objList) {
  126. let vod_list = []
  127. let top_status = false
  128. for (const data of objList) {
  129. if (data["title"] === "今日热点") {
  130. top_status = true
  131. } else if (!_.isEmpty(data["title"])) {
  132. if (top_status) {
  133. break
  134. }
  135. }
  136. if (top_status) {
  137. vod_list = [...vod_list, ...this.parseVodShortByJson(data["items"])]
  138. }
  139. }
  140. return vod_list
  141. }
  142. async getLiveUrl(channel_id, obj) {
  143. let liveApiUrl = `https://vdn.live.cntv.cn/api2/live.do?channel=pd://cctv_p2p_hd${channel_id}&client=iosapp`
  144. let liveResponse = await req(liveApiUrl, {"headers": this.getHeader()})
  145. let liveJson = JSON.parse(liveResponse["content"])
  146. let playList = {}
  147. let channelName = obj["channelName"].split(" ")[0].replaceAll("-", "").toLowerCase()
  148. let liveUrl = this.liveJson[channelName] ?? liveJson["hls_url"]["hls2"]
  149. playList["直播"] = ["点击播放$" + liveUrl]
  150. await this.jadeLog.info(`liveJson:${JSON.stringify(liveJson)}`)
  151. let vod_items = []
  152. if (this.liveJson[channelName] !== undefined) {
  153. } else {
  154. for (const data of obj["program"]) {
  155. let episodeName = data["showTime"] + "-" + data["t"]
  156. let episodeUrl = liveUrl + `?begintimeabs=${data["st"] * 1000}&endtimeabs=${data["et"] * 1000}`
  157. vod_items.push(episodeName + "$" + episodeUrl)
  158. }
  159. }
  160. if (vod_items.length > 0){
  161. playList["点播"] = vod_items.join("#")
  162. }
  163. return playList
  164. }
  165. async getVideoUrl(guid) {
  166. return {"中央影视": ['点击播放' + '$' + guid].join("#")}
  167. }
  168. async parseVodDetailfromJson(id, obj, pic) {
  169. let vodDetail = new VodDetail()
  170. let $;
  171. let guid;
  172. if (obj["url"] !== undefined) {
  173. vodDetail.vod_name = obj["title"]
  174. vodDetail.vod_pic = obj["img"]
  175. vodDetail.type_name = obj["tags"]
  176. vodDetail.vod_year = obj["time"]
  177. vodDetail.vod_content = obj["vset_brief"]
  178. vodDetail.vod_director = obj["vset_title"]
  179. $ = await this.getHtml(obj["url"])
  180. } else {
  181. if (_.isEmpty(obj["lvUrl"])) {
  182. vodDetail.vod_name = obj["channelName"]
  183. vodDetail.vod_pic = pic
  184. } else {
  185. $ = await this.getHtml(obj["lvUrl"])
  186. vodDetail.vod_name = $('[property$=title]')[0].attribs.content
  187. vodDetail.vod_content = $('[property$=description]')[0].attribs.content
  188. let pic = $('[property$=image]')[0].attribs.content
  189. if (!pic.startsWith("http")) {
  190. pic = "https:" + pic
  191. }
  192. vodDetail.vod_pic = pic
  193. }
  194. }
  195. if (!_.isEmpty($)) {
  196. guid = Utils.getStrByRegex(/var guid = "(.*?)"/, $.html())
  197. }
  198. let playlist
  199. if (_.isEmpty(guid) && obj["url"] === undefined) {
  200. playlist = await this.getLiveUrl(id, obj)
  201. } else {
  202. playlist = await this.getVideoUrl(guid)
  203. }
  204. vodDetail.vod_play_url = _.values(playlist).join('$$$');
  205. vodDetail.vod_play_from = _.keys(playlist).join('$$$');
  206. return vodDetail
  207. }
  208. async parseVodDetailFromJsonByTv(obj) {
  209. let vodDetail = new VodDetail()
  210. vodDetail.vod_name = obj["videoSetInfo"]["title"]
  211. vodDetail.type_name = obj["videoSetInfo"]["sc"]
  212. vodDetail.vod_pic = obj["videoSetInfo"]["image"]
  213. vodDetail.vod_content = obj["videoSetInfo"]["brief"]
  214. vodDetail.vod_area = obj["videoSetInfo"]["area"]
  215. let playlist = {}
  216. let vodItems = []
  217. for (const data of obj["videoRoughCut"]) {
  218. let title = data["title"].split("》").slice(-1)[0]
  219. vodItems.push(title + "$" + data["guid"])
  220. }
  221. playlist["中央影视"] = vodItems.join("#")
  222. vodDetail.vod_play_url = _.values(playlist).join('$$$');
  223. vodDetail.vod_play_from = _.keys(playlist).join('$$$');
  224. return vodDetail
  225. }
  226. async setHomeVod() {
  227. let resJson = JSON.parse(await this.fetch(this.apiUrl + "/api/page/iphone/HandheldApplicationSink/7.0.0/158", null, this.getHeader()))
  228. this.homeVodList = await this.parseVodShortListFromJson(resJson["data"]["templates"])
  229. }
  230. getExtendValue(extend, key) {
  231. if (extend[key] !== undefined && extend[key] !== "全部") {
  232. return extend[key]
  233. }
  234. return ""
  235. }
  236. async setCategory(tid, pg, filter, extend) {
  237. if (Utils.isNumeric(tid)) {
  238. tid = extend["live"] ?? tid
  239. let url = this.apiUrl + `/api/page/iphone/HandheldApplicationSink/7.0.0/${tid}`
  240. let response = JSON.parse(await this.fetch(url, null, this.getHeader()))
  241. this.vodList = this.parseVodShortByJson(response["data"]["templates"][0]["items"])
  242. } else {
  243. let letter = this.getExtendValue(extend, "zimu")
  244. let area = this.getExtendValue(extend, "diqu")
  245. let type = this.getExtendValue(extend, "leixing")
  246. let year = this.getExtendValue(extend, "nianfen")
  247. const limit = 12
  248. let url = "https://api.cntv.cn" + `/newVideoset/getCboxVideoAlbumList`
  249. let params = {
  250. "channelid": "",
  251. "sc": type,
  252. "fc": tid,
  253. "p": pg,
  254. "n": limit,
  255. "fl": letter,
  256. "area": area,
  257. "year": year,
  258. "serviceId": "cbox"
  259. }
  260. let resJson = JSON.parse(await this.fetch(url, params, this.getHeader()))
  261. this.vodList = this.parseVodShortByTvJson(resJson["data"]["list"])
  262. }
  263. }
  264. async setDetail(id) {
  265. //区分直播还是点播
  266. let aList = id.split("-")
  267. let playType = aList[0]
  268. let pic = aList[2]
  269. id = aList[1]
  270. if (playType === "play") {
  271. let resJson = JSON.parse(await this.fetch(`https://api.cntv.cn/video/videoinfoByGuid?serviceId=cbox&guid=${id}`, null, this.getHeader()))
  272. this.vodDetail = await this.parseVodDetailfromJson(id, resJson, pic)
  273. } else if (playType === "url") {
  274. let url = `https://api.app.cctv.com/api/getVideoPageDetail?videoSetContentId=${id}`
  275. let resJson = JSON.parse(await this.fetch(url, null, this.getHeader()))
  276. this.vodDetail = await this.parseVodDetailFromJsonByTv(resJson["data"])
  277. } else {
  278. 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(");", "")
  279. this.vodDetail = await this.parseVodDetailfromJson(id, JSON.parse(content)[id], pic)
  280. }
  281. }
  282. async setSearch(wd, quick, pg) {
  283. }
  284. async setPlay(flag, id, flags) {
  285. if (id.startsWith("http")) {
  286. this.playUrl = id
  287. let headers = this.getHeader()
  288. headers["Referer"] = "https://tv.cctv.com/"
  289. this.result.header = headers
  290. } else {
  291. this.playUrl = 'https://hls.cntv.myhwcdn.cn/asp/hls/2000/0303000a/3/default/' + id + '/2000.m3u8'
  292. }
  293. }
  294. }
  295. let spider = new CNTVSpider()
  296. async function init(cfg) {
  297. await spider.init(cfg)
  298. }
  299. async function home(filter) {
  300. return await spider.home(filter)
  301. }
  302. async function homeVod() {
  303. return await spider.homeVod()
  304. }
  305. async function category(tid, pg, filter, extend) {
  306. return await spider.category(tid, pg, filter, extend)
  307. }
  308. async function detail(id) {
  309. return await spider.detail(id)
  310. }
  311. async function play(flag, id, flags) {
  312. return await spider.play(flag, id, flags)
  313. }
  314. async function search(wd, quick) {
  315. return await spider.search(wd, quick)
  316. }
  317. export function __jsEvalReturn() {
  318. return {
  319. init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
  320. };
  321. }
  322. export {spider, CNTVSpider}