diff --git a/jtxtv10/api/TQDJ.py b/jtxtv10/api/TQDJ.py
new file mode 100644
index 0000000..40cac38
--- /dev/null
+++ b/jtxtv10/api/TQDJ.py
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+# by @嗷呜
+import sys
+sys.path.append('..')
+from base.spider import Spider
+
+class Spider(Spider):
+
+ def init(self, extend=""):
+ pass
+
+ def getName(self):
+ return "甜圈短剧"
+
+ def isVideoFormat(self, url):
+ return True
+
+ def manualVideoCheck(self):
+ return False
+
+ def destroy(self):
+ pass
+
+ # 更新为新的域名
+ ahost = 'https://mov.cenguigui.cn'
+
+ headers = {
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
+ 'sec-ch-ua-platform': '"macOS"',
+ 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="134", "Google Chrome";v="134"',
+ 'DNT': '1',
+ 'sec-ch-ua-mobile': '?0',
+ 'Sec-Fetch-Site': 'cross-site',
+ 'Sec-Fetch-Mode': 'no-cors',
+ 'Sec-Fetch-Dest': 'video',
+ 'Sec-Fetch-Storage-Access': 'active',
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
+ }
+
+ def homeContent(self, filter):
+ result = {'class': [{'type_id': '推荐榜', 'type_name': '🔥 推荐榜'},
+ {'type_id': '新剧', 'type_name': '🎬 新剧'},
+ {'type_id': '逆袭', 'type_name': '🎬 逆袭'},
+ {'type_id': '霸总', 'type_name': '🎬 霸总'},
+ {'type_id': '现代言情', 'type_name': '🎬 现代言情'},
+ {'type_id': '打脸虐渣', 'type_name': '🎬 打脸虐渣'},
+ {'type_id': '豪门恩怨', 'type_name': '🎬 豪门恩怨'},
+ {'type_id': '神豪', 'type_name': '🎬 神豪'},
+ {'type_id': '马甲', 'type_name': '🎬 马甲'},
+ {'type_id': '都市日常', 'type_name': '🎬 都市日常'},
+ {'type_id': '战神归来', 'type_name': '🎬 战神归来'},
+ {'type_id': '小人物', 'type_name': '🎬 小人物'},
+ {'type_id': '女性成长', 'type_name': '🎬 女性成长'},
+ {'type_id': '大女主', 'type_name': '🎬 大女主'},
+ {'type_id': '穿越', 'type_name': '🎬 穿越'},
+ {'type_id': '都市修仙', 'type_name': '🎬 都市修仙'},
+ {'type_id': '强者回归', 'type_name': '🎬 强者回归'},
+ {'type_id': '亲情', 'type_name': '🎬 亲情'},
+ {'type_id': '古装', 'type_name': '🎬 古装'},
+ {'type_id': '重生', 'type_name': '🎬 重生'},
+ {'type_id': '闪婚', 'type_name': '🎬 闪婚'},
+ {'type_id': '赘婿逆袭', 'type_name': '🎬 赘婿逆袭'},
+ {'type_id': '虐恋', 'type_name': '🎬 虐恋'},
+ {'type_id': '追妻', 'type_name': '🎬 追妻'},
+ {'type_id': '天下无敌', 'type_name': '🎬 天下无敌'},
+ {'type_id': '家庭伦理', 'type_name': '🎬 家庭伦理'},
+ {'type_id': '萌宝', 'type_name': '🎬 萌宝'},
+ {'type_id': '古风权谋', 'type_name': '🎬 古风权谋'},
+ {'type_id': '职场', 'type_name': '🎬 职场'},
+ {'type_id': '奇幻脑洞', 'type_name': '🎬 奇幻脑洞'},
+ {'type_id': '异能', 'type_name': '🎬 异能'},
+ {'type_id': '无敌神医', 'type_name': '🎬 无敌神医'},
+ {'type_id': '古风言情', 'type_name': '🎬 古风言情'},
+ {'type_id': '传承觉醒', 'type_name': '🎬 传承觉醒'},
+ {'type_id': '现言甜宠', 'type_name': '🎬 现言甜宠'},
+ {'type_id': '奇幻爱情', 'type_name': '🎬 奇幻爱情'},
+ {'type_id': '乡村', 'type_name': '🎬 乡村'},
+ {'type_id': '历史古代', 'type_name': '🎬 历史古代'},
+ {'type_id': '王妃', 'type_name': '🎬 王妃'},
+ {'type_id': '高手下山', 'type_name': '🎬 高手下山'},
+ {'type_id': '娱乐圈', 'type_name': '🎬 娱乐圈'},
+ {'type_id': '强强联合', 'type_name': '🎬 强强联合'},
+ {'type_id': '破镜重圆', 'type_name': '🎬 破镜重圆'},
+ {'type_id': '暗恋成真', 'type_name': '🎬 暗恋成真'},
+ {'type_id': '民国', 'type_name': '🎬 民国'},
+ {'type_id': '欢喜冤家', 'type_name': '🎬 欢喜冤家'},
+ {'type_id': '系统', 'type_name': '🎬 系统'},
+ {'type_id': '真假千金', 'type_name': '🎬 真假千金'},
+ {'type_id': '龙王', 'type_name': '🎬 龙王'},
+ {'type_id': '校园', 'type_name': '🎬 校园'},
+ {'type_id': '穿书', 'type_name': '🎬 穿书'},
+ {'type_id': '女帝', 'type_name': '🎬 女帝'},
+ {'type_id': '团宠', 'type_name': '🎬 团宠'},
+ {'type_id': '年代爱情', 'type_name': '🎬 年代爱情'},
+ {'type_id': '玄幻仙侠', 'type_name': '🎬 玄幻仙侠'},
+ {'type_id': '青梅竹马', 'type_name': '🎬 青梅竹马'},
+ {'type_id': '悬疑推理', 'type_name': '🎬 悬疑推理'},
+ {'type_id': '皇后', 'type_name': '🎬 皇后'},
+ {'type_id': '替身', 'type_name': '🎬 替身'},
+ {'type_id': '大叔', 'type_name': '🎬 大叔'},
+ {'type_id': '喜剧', 'type_name': '🎬 喜剧'},
+ {'type_id': '剧情', 'type_name': '🎬 剧情'}]}
+ return result
+
+ def homeVideoContent(self):
+ return []
+
+ def categoryContent(self, tid, pg, filter, extend):
+ params = {
+ 'classname': tid,
+ 'offset': str((int(pg) - 1)),
+ }
+ # 更新请求路径为 /duanju/api.php
+ data = self.fetch(f'{self.ahost}/duanju/api.php', params=params, headers=self.headers).json()
+ videos = []
+ for k in data['data']:
+ videos.append({
+ 'vod_id': k.get('book_id'),
+ 'vod_name': k.get('title'),
+ 'vod_pic': k.get('cover'),
+ 'vod_year': k.get('score'),
+ 'vod_remarks': f"{k.get('sub_title')}|{k.get('episode_cnt')}"
+ })
+ result = {}
+ result['list'] = videos
+ result['page'] = pg
+ result['pagecount'] = 9999
+ result['limit'] = 90
+ result['total'] = 999999
+ return result
+
+ def detailContent(self, ids):
+ # 更新请求路径为 /duanju/api.php
+ v = self.fetch(f'{self.ahost}/duanju/api.php', params={'book_id': ids[0]}, headers=self.headers).json()
+ vod = {
+ 'vod_id': ids[0],
+ 'vod_name': v.get('title'),
+ 'type_name': v.get('category'),
+ 'vod_year': v.get('time'),
+ 'vod_remarks': v.get('duration'),
+ 'vod_content': v.get('desc'),
+ 'vod_play_from': '爱看短剧',
+ 'vod_play_url': '#'.join([f"{i['title']}${i['video_id']}" for i in v['data']])
+ }
+ return {'list': [vod]}
+
+ def searchContent(self, key, quick, pg="1"):
+ return self.categoryContent(key, pg, True, {})
+
+ def playerContent(self, flag, id, vipFlags):
+ # 更新请求路径为 /duanju/api.php
+ data = self.fetch(f'{self.ahost}/duanju/api.php', params={'video_id': id}, headers=self.headers).json()
+ return {'parse': 0, 'url': data['data']['url'], 'header': self.headers}
+
+ def localProxy(self, param):
+ pass
\ No newline at end of file
diff --git a/jtxtv10/api/模板.js b/jtxtv10/api/模板.js
new file mode 100644
index 0000000..82b037b
--- /dev/null
+++ b/jtxtv10/api/模板.js
@@ -0,0 +1,304 @@
+if (typeof Object.assign != 'function') {
+ Object.assign = function () {
+ var target = arguments[0];
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+ return target;
+};
+}
+function getMubans() {
+ var mubanDict = { // 模板字典
+ mxpro: {
+ title: '',
+ host: '',
+ // homeUrl:'/',
+ url: '/vodshow/fyclass--------fypage---.html',
+ searchUrl: '/vodsearch/**----------fypage---.html',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ headers: {//网站的请求头,完整支持所有的,常带ua和cookies
+ 'User-Agent': 'MOBILE_UA',
+ // "Cookie": "searchneed=ok"
+ },
+ class_parse: '.navbar-items li:gt(2):lt(8);a&&Text;a&&href;/(\\d+).html',
+ play_parse: true,
+ lazy: '',
+ limit: 6,
+ 推荐: '.tab-list.active;a.module-poster-item.module-item;.module-poster-item-title&&Text;.lazyload&&data-original;.module-item-note&&Text;a&&href',
+ double: true, // 推荐内容是否双层定位
+ 一级: 'body a.module-poster-item.module-item;a&&title;.lazyload&&data-original;.module-item-note&&Text;a&&href',
+ 二级: {
+ "title": "h1&&Text;.module-info-tag&&Text",
+ "img": ".lazyload&&data-original",
+ "desc": ".module-info-item:eq(1)&&Text;.module-info-item:eq(2)&&Text;.module-info-item:eq(3)&&Text",
+ "content": ".module-info-introduction&&Text",
+ "tabs": ".module-tab-item",
+ "lists": ".module-play-list:eq(#id) a"
+ },
+ 搜索: 'body .module-item;.module-card-item-title&&Text;.lazyload&&data-original;.module-item-note&&Text;a&&href;.module-info-item-content&&Text',
+ },
+ mxone5: {
+ title: '',
+ host: '',
+ url: '/show/fyclass--------fypage---.html',
+ searchUrl: '/search/**----------fypage---.html',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ class_parse: '.nav-menu-items&&li;a&&Text;a&&href;.*/(.*?).html',
+ play_parse: true,
+ lazy: '',
+ limit: 6,
+ 推荐: '.module-list;.module-items&&.module-item;a&&title;img&&data-src;.module-item-text&&Text;a&&href',
+ double: true, // 推荐内容是否双层定位
+ 一级: '.module-items .module-item;a&&title;img&&data-src;.module-item-text&&Text;a&&href',
+ 二级: {
+ "title": "h1&&Text;.tag-link&&Text",
+ "img": ".module-item-pic&&img&&data-src",
+ "desc": ".video-info-items:eq(0)&&Text;.video-info-items:eq(1)&&Text;.video-info-items:eq(2)&&Text;.video-info-items:eq(3)&&Text",
+ "content": ".vod_content&&Text",
+ "tabs": ".module-tab-item",
+ "lists": ".module-player-list:eq(#id)&&.scroll-content&&a"
+ },
+ 搜索: '.module-items .module-search-item;a&&title;img&&data-src;.video-serial&&Text;a&&href',
+ },
+ 首图: {
+ title: '',
+ host: '',
+ url: '/vodshow/fyclass--------fypage---/',
+ searchUrl: '/vodsearch/**----------fypage---.html',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ headers: {//网站的请求头,完整支持所有的,常带ua和cookies
+ 'User-Agent': 'MOBILE_UA',
+ // "Cookie": "searchneed=ok"
+ },
+ class_parse: '.myui-header__menu li.hidden-sm:gt(0):lt(5);a&&Text;a&&href;/(\\d+).html',
+ play_parse: true,
+ lazy: '',
+ limit: 6,
+ 推荐: 'ul.myui-vodlist.clearfix;li;a&&title;a&&data-original;.pic-text&&Text;a&&href',
+ double: true, // 推荐内容是否双层定位
+ 一级: '.myui-vodlist li;a&&title;a&&data-original;.pic-text&&Text;a&&href',
+ 二级: {
+ "title": ".myui-content__detail .title&&Text;.myui-content__detail p:eq(-2)&&Text",
+ "img": ".myui-content__thumb .lazyload&&data-original",
+ "desc": ".myui-content__detail p:eq(0)&&Text;.myui-content__detail p:eq(1)&&Text;.myui-content__detail p:eq(2)&&Text",
+ "content": ".content&&Text",
+ "tabs": ".nav-tabs:eq(0) li",
+ "lists": ".myui-content__list:eq(#id) li"
+ },
+ 搜索: '#searchList li;a&&title;.lazyload&&data-original;.text-muted&&Text;a&&href;.text-muted:eq(-1)&&Text',
+ },
+ 首图2: {
+ title: '',
+ host: '',
+ url: '/list/fyclass-fypage.html',
+ searchUrl: '/vodsearch/**----------fypage---.html',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ headers: {
+ 'User-Agent': 'UC_UA',
+ // "Cookie": ""
+ },
+ // class_parse:'.stui-header__menu li:gt(0):lt(7);a&&Text;a&&href;/(\\d+).html',
+ class_parse: '.stui-header__menu li:gt(0):lt(7);a&&Text;a&&href;.*/(.*?).html',
+ play_parse: true,
+ lazy: '',
+ limit: 6,
+ 推荐: 'ul.stui-vodlist.clearfix;li;a&&title;.lazyload&&data-original;.pic-text&&Text;a&&href',
+ double: true, // 推荐内容是否双层定位
+ 一级: '.stui-vodlist li;a&&title;a&&data-original;.pic-text&&Text;a&&href',
+ 二级: {
+ "title": ".stui-content__detail .title&&Text;.stui-content__detail p:eq(-2)&&Text",
+ "img": ".stui-content__thumb .lazyload&&data-original",
+ "desc": ".stui-content__detail p:eq(0)&&Text;.stui-content__detail p:eq(1)&&Text;.stui-content__detail p:eq(2)&&Text",
+ "content": ".detail&&Text",
+ "tabs": ".stui-vodlist__head h3",
+ "lists": ".stui-content__playlist:eq(#id) li"
+ },
+ 搜索: 'ul.stui-vodlist__media:eq(0) li,ul.stui-vodlist:eq(0) li,#searchList li;a&&title;.lazyload&&data-original;.text-muted&&Text;a&&href;.text-muted:eq(-1)&&Text',
+ 搜索1: 'ul.stui-vodlist&&li;a&&title;.lazyload&&data-original;.text-muted&&Text;a&&href;.text-muted:eq(-1)&&Text',
+ 搜索2: 'ul.stui-vodlist__media&&li;a&&title;.lazyload&&data-original;.text-muted&&Text;a&&href;.text-muted:eq(-1)&&Text',
+ },
+ 默认: {
+ title: '',
+ host: '',
+ url: '/vodshow/fyclass--------fypage---.html',
+ searchUrl: '/vodsearch/-------------.html?wd=**',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ headers: {
+ 'User-Agent': 'MOBILE_UA',
+ },
+ play_parse: true,
+ lazy: '',
+ limit: 6,
+ double: true, // 推荐内容是否双层定位
+ },
+ vfed: {
+ title: '',
+ host: '',
+ url: '/index.php/vod/show/id/fyclass/page/fypage.html',
+ searchUrl: '/index.php/vod/search/page/fypage/wd/**.html',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ headers: {
+ 'User-Agent': 'UC_UA',
+ },
+ // class_parse:'.fed-pops-navbar&&ul.fed-part-rows&&a.fed-part-eone:gt(0):lt(5);a&&Text;a&&href;.*/(.*?).html',
+ class_parse: '.fed-pops-navbar&&ul.fed-part-rows&&a;a&&Text;a&&href;.*/(.*?).html',
+ play_parse: true,
+ lazy: '',
+ limit: 6,
+ 推荐: 'ul.fed-list-info.fed-part-rows;li;a.fed-list-title&&Text;a&&data-original;.fed-list-remarks&&Text;a&&href',
+ double: true, // 推荐内容是否双层定位
+ 一级: '.fed-list-info&&li;a.fed-list-title&&Text;a&&data-original;.fed-list-remarks&&Text;a&&href',
+ 二级: {
+ "title": "h1.fed-part-eone&&Text;.fed-deta-content&&.fed-part-rows&&li&&Text",
+ "img": ".fed-list-info&&a&&data-original",
+ "desc": ".fed-deta-content&&.fed-part-rows&&li:eq(1)&&Text;.fed-deta-content&&.fed-part-rows&&li:eq(2)&&Text;.fed-deta-content&&.fed-part-rows&&li:eq(3)&&Text",
+ "content": ".fed-part-esan&&Text",
+ "tabs": ".fed-drop-boxs&&.fed-part-rows&&li",
+ "lists": ".fed-play-item:eq(#id)&&ul:eq(1)&&li"
+ },
+ 搜索: '.fed-deta-info;h1&&Text;.lazyload&&data-original;.fed-list-remarks&&Text;a&&href;.fed-deta-content&&Text',
+ },
+ 海螺3: {
+ title: '',
+ host: '',
+ searchUrl: '/v_search/**----------fypage---.html',
+ url: '/vod_____show/fyclass--------fypage---.html',
+ headers: {
+ 'User-Agent': 'MOBILE_UA'
+ },
+ timeout: 5000,
+ class_parse: 'body&&.hl-nav li:gt(0);a&&Text;a&&href;.*/(.*?).html',
+ cate_exclude: '明星|专题|最新|排行',
+ limit: 40,
+ play_parse: true,
+ lazy: '',
+ 推荐: '.hl-vod-list;li;a&&title;a&&data-original;.remarks&&Text;a&&href',
+ double: true,
+ 一级: '.hl-vod-list&&.hl-list-item;a&&title;a&&data-original;.remarks&&Text;a&&href',
+ 二级: {
+ "title": ".hl-infos-title&&Text;.hl-text-conch&&Text",
+ "img": ".hl-lazy&&data-original",
+ "desc": ".hl-infos-content&&.hl-text-conch&&Text",
+ "content": ".hl-content-text&&Text",
+ "tabs": ".hl-tabs&&a",
+ "lists": ".hl-plays-list:eq(#id)&&li"
+ },
+ 搜索: '.hl-list-item;a&&title;a&&data-original;.remarks&&Text;a&&href',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ },
+ 海螺2: {
+ title: '',
+ host: '',
+ searchUrl: '/index.php/vod/search/page/fypage/wd/**/',
+ url: '/index.php/vod/show/id/fyclass/page/fypage/',
+ headers: {
+ 'User-Agent': 'MOBILE_UA'
+ },
+ timeout: 5000,
+ class_parse: '#nav-bar li;a&&Text;a&&href;id/(.*?)/',
+ limit: 40,
+ play_parse: true,
+ lazy: '',
+ 推荐: '.list-a.size;li;a&&title;.lazy&&data-original;.bt&&Text;a&&href',
+ double: true,
+ 一级: '.list-a&&li;a&&title;.lazy&&data-original;.list-remarks&&Text;a&&href',
+ 二级: {
+ "title": "h2&&Text;.deployment&&Text",
+ "img": ".lazy&&data-original",
+ "desc": ".deployment&&Text",
+ "content": ".ec-show&&Text",
+ "tabs": "#tag&&a",
+ "lists": ".play_list_box:eq(#id)&&li"
+ },
+ 搜索: '.search-list;a&&title;.lazy&&data-original;.deployment&&Text;a&&href',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ },
+ 短视: {
+ title: '',
+ host: '',
+ // homeUrl:'/',
+ url: '/channel/fyclass-fypage.html',
+ searchUrl: '/search.html?wd=**',
+ searchable: 2,//是否启用全局搜索,
+ quickSearch: 0,//是否启用快速搜索,
+ filterable: 0,//是否启用分类筛选,
+ headers: {//网站的请求头,完整支持所有的,常带ua和cookies
+ 'User-Agent': 'MOBILE_UA',
+ // "Cookie": "searchneed=ok"
+ },
+ class_parse: '.menu_bottom ul li;a&&Text;a&&href;.*/(.*?).html',
+ cate_exclude: '解析|动态',
+ play_parse: true,
+ lazy: '',
+ limit: 6,
+ 推荐: '.indexShowBox;ul&&li;a&&title;img&&data-src;.s1&&Text;a&&href',
+ double: true, // 推荐内容是否双层定位
+ 一级: '.pic-list&&li;a&&title;img&&data-src;.s1&&Text;a&&href',
+ 二级: {
+ "title": "h1&&Text;.content-rt&&p:eq(0)&&Text",
+ "img": ".img&&img&&data-src",
+ "desc": ".content-rt&&p:eq(1)&&Text;.content-rt&&p:eq(2)&&Text;.content-rt&&p:eq(3)&&Text;.content-rt&&p:eq(4)&&Text;.content-rt&&p:eq(5)&&Text",
+ "content": ".zkjj_a&&Text",
+ "tabs": ".py-tabs&&option",
+ "lists": ".player:eq(#id) li"
+ },
+ 搜索: '.sr_lists&&ul&&li;h3&&Text;img&&data-src;.int&&p:eq(0)&&Text;a&&href',
+ },
+ 短视2:{
+ title: '',
+ host: '',
+ class_name:'电影&电视剧&综艺&动漫',
+ class_url:'1&2&3&4',
+ searchUrl: '/index.php/ajax/suggest?mid=1&wd=**&limit=50',
+ searchable: 2,
+ quickSearch: 0,
+ headers:{'User-Agent':'MOBILE_UA'},
+ url: '/index.php/api/vod#type=fyclass&page=fypage',
+ filterable:0,//是否启用分类筛选,
+ filter_url:'',
+ filter: {},
+ filter_def:{},
+ detailUrl:'/index.php/vod/detail/id/fyid.html',
+ play_parse: true,
+ lazy: '',
+ limit: 6,
+ 推荐:'.list-vod.flex .public-list-box;a&&title;.lazy&&data-original;.public-list-prb&&Text;a&&href',
+ 一级:'js:let body=input.split("#")[1];let t=Math.round(new Date/1e3).toString();let key=md5("DS"+t+"DCC147D11943AF75");let url=input.split("#")[0];body=body+"&time="+t+"&key="+key;print(body);fetch_params.body=body;let html=post(url,fetch_params);let data=JSON.parse(html);VODS=data.list.map(function(it){it.vod_pic=urljoin2(input.split("/i")[0],it.vod_pic);return it});',
+ 二级:{
+ "title":".slide-info-title&&Text;.slide-info:eq(3)--strong&&Text",
+ "img":".detail-pic&&data-original",
+ "desc":".fraction&&Text;.slide-info-remarks:eq(1)&&Text;.slide-info-remarks:eq(2)&&Text;.slide-info:eq(2)--strong&&Text;.slide-info:eq(1)--strong&&Text",
+ "content":"#height_limit&&Text",
+ "tabs":".anthology.wow.fadeInUp.animated&&.swiper-wrapper&&a",
+ "tab_text":".swiper-slide&&Text",
+ "lists":".anthology-list-box:eq(#id) li"
+ },
+ 搜索:'json:list;name;pic;;id',
+ }
+ };
+ return JSON.parse(JSON.stringify(mubanDict));
+}
+var mubanDict = getMubans();
+var muban = getMubans();
+export default {muban,getMubans};
\ No newline at end of file
diff --git a/jtxtv10/api/河马短剧.py b/jtxtv10/api/河马短剧.py
new file mode 100644
index 0000000..eeee8ba
--- /dev/null
+++ b/jtxtv10/api/河马短剧.py
@@ -0,0 +1,581 @@
+# -*- coding: utf-8 -*-
+import requests
+import re
+import json
+import traceback
+import sys
+
+sys.path.append('../../')
+try:
+ from base.spider import Spider
+except ImportError:
+ # 定义一个基础接口类,用于本地测试
+ class Spider:
+ def init(self, extend=""):
+ pass
+
+class Spider(Spider):
+ def __init__(self):
+ self.siteUrl = "https://www.kuaikaw.cn"
+ self.nextData = None # 缓存NEXT_DATA数据
+ self.cateManual = {
+ "甜宠": "462",
+ "古装仙侠": "1102",
+ "现代言情": "1145",
+ "青春": "1170",
+ "豪门恩怨": "585",
+ "逆袭": "417-464",
+ "重生": "439-465",
+ "系统": "1159",
+ "总裁": "1147",
+ "职场商战": "943"
+ }
+
+ def getName(self):
+ # 返回爬虫名称
+ return "河马短剧"
+
+ def init(self, extend=""):
+ return
+
+ def fetch(self, url, headers=None):
+ """统一的网络请求接口"""
+ if headers is None:
+ headers = {
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
+ "Referer": self.siteUrl,
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
+ }
+
+ try:
+ response = requests.get(url, headers=headers, timeout=10, allow_redirects=True)
+ response.raise_for_status()
+ return response
+ except Exception as e:
+ print(f"请求异常: {url}, 错误: {str(e)}")
+ return None
+
+ def isVideoFormat(self, url):
+ # 检查是否为视频格式
+ video_formats = ['.mp4', '.mkv', '.avi', '.wmv', '.m3u8', '.flv', '.rmvb']
+ for format in video_formats:
+ if format in url.lower():
+ return True
+ return False
+
+ def manualVideoCheck(self):
+ # 不需要手动检查
+ return False
+
+ def homeContent(self, filter):
+ """获取首页分类及筛选"""
+ result = {}
+ # 分类列表,使用已初始化的cateManual
+ classes = []
+ for k in self.cateManual:
+ classes.append({
+ 'type_name': k,
+ 'type_id': self.cateManual[k]
+ })
+ result['class'] = classes
+ # 获取首页推荐视频
+ try:
+ result['list'] = self.homeVideoContent()['list']
+ except:
+ result['list'] = []
+
+ return result
+
+ def homeVideoContent(self):
+ """获取首页推荐视频内容"""
+ videos = []
+ try:
+ response = self.fetch(self.siteUrl)
+ html_content = response.text
+ # 提取NEXT_DATA JSON数据
+ next_data_pattern = r''
+ next_data_match = re.search(next_data_pattern, html_content, re.DOTALL)
+ if next_data_match:
+ next_data_json = json.loads(next_data_match.group(1))
+ page_props = next_data_json.get("props", {}).get("pageProps", {})
+ # 获取轮播图数据 - 这些通常是推荐内容
+ if "bannerList" in page_props and isinstance(page_props["bannerList"], list):
+ banner_list = page_props["bannerList"]
+ for banner in banner_list:
+ book_id = banner.get("bookId", "")
+ book_name = banner.get("bookName", "")
+ cover_url = banner.get("coverWap", banner.get("wapUrl", ""))
+ # 获取状态和章节数
+ status = banner.get("statusDesc", "")
+ total_chapters = banner.get("totalChapterNum", "")
+ if book_id and book_name:
+ videos.append({
+ "vod_id": f"/drama/{book_id}",
+ "vod_name": book_name,
+ "vod_pic": cover_url,
+ "vod_remarks": f"{status} {total_chapters}集" if total_chapters else status
+ })
+
+ # SEO分类下的推荐
+ if "seoColumnVos" in page_props and isinstance(page_props["seoColumnVos"], list):
+ for column in page_props["seoColumnVos"]:
+ book_infos = column.get("bookInfos", [])
+ for book in book_infos:
+ book_id = book.get("bookId", "")
+ book_name = book.get("bookName", "")
+ cover_url = book.get("coverWap", "")
+ status = book.get("statusDesc", "")
+ total_chapters = book.get("totalChapterNum", "")
+
+ if book_id and book_name:
+ videos.append({
+ "vod_id": f"/drama/{book_id}",
+ "vod_name": book_name,
+ "vod_pic": cover_url,
+ "vod_remarks": f"{status} {total_chapters}集" if total_chapters else status
+ })
+
+ # # 去重
+ # seen = set()
+ # unique_videos = []
+ # for video in videos:
+ # if video["vod_id"] not in seen:
+ # seen.add(video["vod_id"])
+ # unique_videos.append(video)
+ # videos = unique_videos
+
+ except Exception as e:
+ print(f"获取首页推荐内容出错: {e}")
+
+ result = {
+ "list": videos
+ }
+ return result
+
+ def categoryContent(self, tid, pg, filter, extend):
+ """获取分类内容"""
+ result = {}
+ videos = []
+ url = f"{self.siteUrl}/browse/{tid}/{pg}"
+ response = self.fetch(url)
+ html_content = response.text
+ # 提取NEXT_DATA JSON数据
+ next_data_pattern = r''
+ next_data_match = re.search(next_data_pattern, html_content, re.DOTALL)
+ if next_data_match:
+ next_data_json = json.loads(next_data_match.group(1))
+ page_props = next_data_json.get("props", {}).get("pageProps", {})
+ # 获取总页数和当前页
+ current_page = page_props.get("page", 1)
+ total_pages = page_props.get("pages", 1)
+ # 获取书籍列表
+ book_list = page_props.get("bookList", [])
+ # 转换为通用格式
+ for book in book_list:
+ book_id = book.get("bookId", "")
+ book_name = book.get("bookName", "")
+ cover_url = book.get("coverWap", "")
+ status_desc = book.get("statusDesc", "")
+ total_chapters = book.get("totalChapterNum", "")
+ if book_id and book_name:
+ videos.append({
+ "vod_id": f"/drama/{book_id}",
+ "vod_name": book_name,
+ "vod_pic": cover_url,
+ "vod_remarks": f"{status_desc} {total_chapters}集" if total_chapters else status_desc
+ })
+ # 构建返回结果
+ result = {
+ "list": videos,
+ "page": int(current_page),
+ "pagecount": total_pages,
+ "limit": len(videos),
+ "total": total_pages * len(videos) if videos else 0
+ }
+ return result
+
+ def switch(self, key, pg):
+ # 搜索功能
+ search_results = []
+ # 获取第一页结果,并检查总页数
+ url = f"{self.siteUrl}/search?searchValue={key}&page={pg}"
+ response = self.fetch(url)
+ html_content = response.text
+ # 提取NEXT_DATA JSON数据
+ next_data_pattern = r''
+ next_data_match = re.search(next_data_pattern, html_content, re.DOTALL)
+ if next_data_match:
+ next_data_json = json.loads(next_data_match.group(1))
+ page_props = next_data_json.get("props", {}).get("pageProps", {})
+ # 获取总页数
+ total_pages = page_props.get("pages", 1)
+ # 处理所有页的数据
+ all_book_list = []
+ # 添加第一页的书籍列表
+ book_list = page_props.get("bookList", [])
+ all_book_list.extend(book_list)
+ # 如果有多页,获取其他页的数据
+ if total_pages > 1 : # quick模式只获取第一页
+ for page in range(2, total_pages + 1):
+ next_page_url = f"{self.siteUrl}/search?searchValue={key}&page={page}"
+ next_page_response = self.fetch(next_page_url)
+ next_page_html = next_page_response.text
+ next_page_match = re.search(next_data_pattern, next_page_html, re.DOTALL)
+ if next_page_match:
+ next_page_json = json.loads(next_page_match.group(1))
+ next_page_props = next_page_json.get("props", {}).get("pageProps", {})
+ next_page_books = next_page_props.get("bookList", [])
+ all_book_list.extend(next_page_books)
+ # 转换为统一的搜索结果格式
+ for book in all_book_list:
+ book_id = book.get("bookId", "")
+ book_name = book.get("bookName", "")
+ cover_url = book.get("coverWap", "")
+ total_chapters = book.get("totalChapterNum", "0")
+ status_desc = book.get("statusDesc", "")
+ # 构建视频项
+ vod = {
+ "vod_id": f"/drama/{book_id}",
+ "vod_name": book_name,
+ "vod_pic": cover_url,
+ "vod_remarks": f"{status_desc} {total_chapters}集"
+ }
+ search_results.append(vod)
+ result = {
+ "list": search_results,
+ "page": pg
+ }
+ return result
+
+ def searchContent(self, key, quick, pg=1):
+ result = self.switch(key, pg=pg)
+ result['page'] = pg
+ return result
+
+ def searchContentPage(self, key, quick, pg=1):
+ return self.searchContent(key, quick, pg)
+
+ def detailContent(self, ids):
+ # 获取剧集信息
+ vod_id = ids[0]
+ episode_id = None
+ chapter_id = None
+
+ if not vod_id.startswith('/drama/'):
+ if vod_id.startswith('/episode/'):
+ episode_info = vod_id.replace('/episode/', '').split('/')
+ if len(episode_info) >= 2:
+ episode_id = episode_info[0]
+ chapter_id = episode_info[1]
+ vod_id = f'/drama/{episode_id}'
+ else:
+ vod_id = '/drama/' + vod_id
+
+ drama_url = self.siteUrl + vod_id
+ print(f"请求URL: {drama_url}")
+
+ headers = {
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
+ "Referer": self.siteUrl,
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
+ }
+
+ rsp = self.fetch(drama_url, headers=headers)
+ if not rsp or rsp.status_code != 200:
+ print(f"请求失败,状态码: {getattr(rsp, 'status_code', 'N/A')}")
+ return {}
+
+ html = rsp.text
+ next_data_match = re.search(r'', html, re.DOTALL)
+
+ if not next_data_match:
+ print("未找到NEXT_DATA内容")
+ return {}
+
+ try:
+ next_data = json.loads(next_data_match.group(1))
+ page_props = next_data.get("props", {}).get("pageProps", {})
+ print(f"找到页面属性,包含 {len(page_props.keys())} 个键")
+
+ book_info = page_props.get("bookInfoVo", {})
+ chapter_list = page_props.get("chapterList", [])
+
+ title = book_info.get("title", "")
+ sub_title = f"{book_info.get('totalChapterNum', '')}集"
+
+ categories = []
+ for category in book_info.get("categoryList", []):
+ categories.append(category.get("name", ""))
+
+ vod_content = book_info.get("introduction", "")
+
+ vod = {
+ "vod_id": vod_id,
+ "vod_name": title,
+ "vod_pic": book_info.get("coverWap", ""),
+ "type_name": ",".join(categories),
+ "vod_year": "",
+ "vod_area": book_info.get("countryName", ""),
+ "vod_remarks": sub_title,
+ "vod_actor": ", ".join([p.get("name", "") for p in book_info.get("performerList", [])]),
+ "vod_director": "",
+ "vod_content": vod_content
+ }
+
+ # 处理播放列表
+ play_url_list = []
+ episodes = []
+
+ if chapter_list:
+ print(f"找到 {len(chapter_list)} 个章节")
+
+ # 先检查是否有可以直接使用的MP4链接作为模板
+ mp4_template = None
+ first_mp4_chapter_id = None
+
+ # 先搜索第一个章节的MP4链接
+ # 为提高成功率,尝试直接请求第一个章节的播放页
+ if chapter_list and len(chapter_list) > 0:
+ first_chapter = chapter_list[0]
+ first_chapter_id = first_chapter.get("chapterId", "")
+ drama_id_clean = vod_id.replace('/drama/', '')
+
+ if first_chapter_id and drama_id_clean:
+ first_episode_url = f"{self.siteUrl}/episode/{drama_id_clean}/{first_chapter_id}"
+ print(f"请求第一集播放页: {first_episode_url}")
+
+ first_rsp = self.fetch(first_episode_url, headers=headers)
+ if first_rsp and first_rsp.status_code == 200:
+ first_html = first_rsp.text
+ # 直接从HTML提取MP4链接
+ mp4_pattern = r'(https?://[^"\']+\.mp4)'
+ mp4_matches = re.findall(mp4_pattern, first_html)
+ if mp4_matches:
+ mp4_template = mp4_matches[0]
+ first_mp4_chapter_id = first_chapter_id
+ print(f"找到MP4链接模板: {mp4_template}")
+ print(f"模板对应的章节ID: {first_mp4_chapter_id}")
+
+ # 如果未找到模板,再检查章节对象中是否有MP4链接
+ if not mp4_template:
+ for chapter in chapter_list[:5]: # 只检查前5个章节以提高效率
+ if "chapterVideoVo" in chapter and chapter["chapterVideoVo"]:
+ chapter_video = chapter["chapterVideoVo"]
+ mp4_url = chapter_video.get("mp4", "") or chapter_video.get("mp4720p", "") or chapter_video.get("vodMp4Url", "")
+ if mp4_url and ".mp4" in mp4_url:
+ mp4_template = mp4_url
+ first_mp4_chapter_id = chapter.get("chapterId", "")
+ print(f"从chapterVideoVo找到MP4链接模板: {mp4_template}")
+ print(f"模板对应的章节ID: {first_mp4_chapter_id}")
+ break
+
+ # 遍历所有章节处理播放信息
+ for chapter in chapter_list:
+ chapter_id = chapter.get("chapterId", "")
+ chapter_name = chapter.get("chapterName", "")
+
+ # 1. 如果章节自身有MP4链接,直接使用
+ if "chapterVideoVo" in chapter and chapter["chapterVideoVo"]:
+ chapter_video = chapter["chapterVideoVo"]
+ mp4_url = chapter_video.get("mp4", "") or chapter_video.get("mp4720p", "") or chapter_video.get("vodMp4Url", "")
+ if mp4_url and ".mp4" in mp4_url:
+ episodes.append(f"{chapter_name}${mp4_url}")
+ continue
+
+ # 2. 如果有MP4模板,尝试替换章节ID构建MP4链接
+ if mp4_template and first_mp4_chapter_id and chapter_id:
+ # 替换模板中的章节ID部分
+ if first_mp4_chapter_id in mp4_template:
+ new_mp4_url = mp4_template.replace(first_mp4_chapter_id, chapter_id)
+ episodes.append(f"{chapter_name}${new_mp4_url}")
+ continue
+
+ # 3. 如果上述方法都不可行,回退到使用chapter_id构建中间URL
+ if chapter_id and chapter_name:
+ url = f"{vod_id}${chapter_id}${chapter_name}"
+ episodes.append(f"{chapter_name}${url}")
+
+ if not episodes and vod_id:
+ # 尝试构造默认的集数
+ total_chapters = int(book_info.get("totalChapterNum", "0"))
+ if total_chapters > 0:
+ print(f"尝试构造 {total_chapters} 个默认集数")
+
+ # 如果知道章节ID的模式,可以构造
+ if chapter_id and episode_id:
+ for i in range(1, total_chapters + 1):
+ chapter_name = f"第{i}集"
+ url = f"{vod_id}${chapter_id}${chapter_name}"
+ episodes.append(f"{chapter_name}${url}")
+ else:
+ # 使用普通的构造方式
+ for i in range(1, total_chapters + 1):
+ chapter_name = f"第{i}集"
+ url = f"{vod_id}${chapter_name}"
+ episodes.append(f"{chapter_name}${url}")
+
+ if episodes:
+ play_url_list.append("#".join(episodes))
+ vod['vod_play_from'] = '河马剧场'
+ vod['vod_play_url'] = '$$$'.join(play_url_list)
+
+ result = {
+ 'list': [vod]
+ }
+ return result
+ except Exception as e:
+ print(f"解析详情页失败: {str(e)}")
+ print(traceback.format_exc())
+ return {}
+
+ def playerContent(self, flag, id, vipFlags):
+ result = {}
+ print(f"调用playerContent: flag={flag}, id={id}")
+
+ headers = {
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
+ "Referer": self.siteUrl,
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
+ }
+
+ # 解析id参数
+ parts = id.split('$')
+ drama_id = None
+ chapter_id = None
+
+ if len(parts) >= 2:
+ drama_id = parts[0]
+ chapter_id = parts[1]
+ chapter_name = parts[2] if len(parts) > 2 else "第一集"
+ print(f"解析参数: drama_id={drama_id}, chapter_id={chapter_id}")
+ else:
+ # 处理旧数据格式
+ print(f"使用原始URL格式: {id}")
+ result["parse"] = 0
+ result["url"] = id
+ result["header"] = json.dumps(headers)
+ return result
+
+ # 直接检查chapter_id是否包含http(可能已经是视频链接)
+ if 'http' in chapter_id and '.mp4' in chapter_id:
+ print(f"已经是MP4链接: {chapter_id}")
+ result["parse"] = 0
+ result["url"] = chapter_id
+ result["header"] = json.dumps(headers)
+ return result
+
+ # 构建episode页面URL
+ drama_id_clean = drama_id.replace('/drama/', '')
+ episode_url = f"{self.siteUrl}/episode/{drama_id_clean}/{chapter_id}"
+ print(f"请求episode页面: {episode_url}")
+
+ try:
+ rsp = self.fetch(episode_url, headers=headers)
+ if not rsp or rsp.status_code != 200:
+ print(f"请求失败,状态码: {getattr(rsp, 'status_code', 'N/A')}")
+ result["parse"] = 0
+ result["url"] = id
+ result["header"] = json.dumps(headers)
+ return result
+
+ html = rsp.text
+ print(f"获取页面大小: {len(html)} 字节")
+
+ # 尝试从NEXT_DATA提取视频链接
+ mp4_url = None
+
+ # 方法1: 从NEXT_DATA提取
+ next_data_match = re.search(r'', html, re.DOTALL)
+ if next_data_match:
+ try:
+ print("找到NEXT_DATA")
+ next_data = json.loads(next_data_match.group(1))
+ page_props = next_data.get("props", {}).get("pageProps", {})
+
+ # 从chapterList中查找当前章节
+ chapter_list = page_props.get("chapterList", [])
+ print(f"找到章节列表,长度: {len(chapter_list)}")
+
+ for chapter in chapter_list:
+ if chapter.get("chapterId") == chapter_id:
+ print(f"找到匹配的章节: {chapter.get('chapterName')}")
+ chapter_video = chapter.get("chapterVideoVo", {})
+ mp4_url = chapter_video.get("mp4", "") or chapter_video.get("mp4720p", "") or chapter_video.get("vodMp4Url", "")
+ if mp4_url:
+ print(f"从chapterList找到MP4链接: {mp4_url}")
+ break
+
+ # 如果未找到,尝试从当前章节获取
+ if not mp4_url:
+ current_chapter = page_props.get("chapterInfo", {})
+ if current_chapter:
+ print("找到当前章节信息")
+ chapter_video = current_chapter.get("chapterVideoVo", {})
+ mp4_url = chapter_video.get("mp4", "") or chapter_video.get("mp4720p", "") or chapter_video.get("vodMp4Url", "")
+ if mp4_url:
+ print(f"从chapterInfo找到MP4链接: {mp4_url}")
+ except Exception as e:
+ print(f"解析NEXT_DATA失败: {str(e)}")
+ print(traceback.format_exc())
+
+ # 方法2: 直接从HTML中提取MP4链接
+ if not mp4_url:
+ mp4_pattern = r'(https?://[^"\']+\.mp4)'
+ mp4_matches = re.findall(mp4_pattern, html)
+ if mp4_matches:
+ # 查找含有chapter_id的链接
+ matched_mp4 = False
+ for url in mp4_matches:
+ if chapter_id in url:
+ mp4_url = url
+ matched_mp4 = True
+ print(f"从HTML直接提取章节MP4链接: {mp4_url}")
+ break
+
+ # 如果没找到包含chapter_id的链接,使用第一个
+ if not matched_mp4 and mp4_matches:
+ mp4_url = mp4_matches[0]
+ print(f"从HTML直接提取MP4链接: {mp4_url}")
+
+ if mp4_url and ".mp4" in mp4_url:
+ print(f"最终找到的MP4链接: {mp4_url}")
+ result["parse"] = 0
+ result["url"] = mp4_url
+ result["header"] = json.dumps(headers)
+ return result
+ else:
+ print(f"未找到有效的MP4链接,尝试再次解析页面内容")
+ # 再尝试一次从HTML中广泛搜索所有可能的MP4链接
+ all_mp4_pattern = r'(https?://[^"\']+\.mp4)'
+ all_mp4_matches = re.findall(all_mp4_pattern, html)
+ if all_mp4_matches:
+ mp4_url = all_mp4_matches[0]
+ print(f"从HTML广泛搜索找到MP4链接: {mp4_url}")
+ result["parse"] = 0
+ result["url"] = mp4_url
+ result["header"] = json.dumps(headers)
+ return result
+
+ print(f"未找到视频链接,返回原episode URL: {episode_url}")
+ result["parse"] = 0
+ result["url"] = episode_url
+ result["header"] = json.dumps(headers)
+ return result
+ except Exception as e:
+ print(f"请求或解析失败: {str(e)}")
+ print(traceback.format_exc())
+ result["parse"] = 0
+ result["url"] = id
+ result["header"] = json.dumps(headers)
+ return result
+
+ def localProxy(self, param):
+ # 本地代理处理,此处简单返回传入的参数
+ return [200, "video/MP2T", {}, param]
+
+ def destroy(self):
+ # 资源回收
+ pass
\ No newline at end of file
diff --git a/jtxtv10/api/金牌影视.py b/jtxtv10/api/金牌影视.py
new file mode 100644
index 0000000..815951a
--- /dev/null
+++ b/jtxtv10/api/金牌影视.py
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 -*-
+# by @嗷呜
+import json
+import sys
+import threading
+import uuid
+import requests
+sys.path.append('..')
+from base.spider import Spider
+import time
+from Crypto.Hash import MD5, SHA1
+
+class Spider(Spider):
+ '''
+ 配置示例:
+ {
+ "key": "xxxx",
+ "name": "xxxx",
+ "type": 3,
+ "api": ".所在路径/金牌.py",
+ "searchable": 1,
+ "quickSearch": 1,
+ "filterable": 1,
+ "changeable": 1,
+ "ext": {
+ "site": "https://www.jiabaide.cn,域名2,域名3"
+ }
+ },
+ '''
+ def init(self, extend=""):
+ if extend:
+ hosts=json.loads(extend)['site']
+ self.host = self.host_late(hosts)
+ pass
+
+ def getName(self):
+ pass
+
+ def isVideoFormat(self, url):
+ pass
+
+ def manualVideoCheck(self):
+ pass
+
+ def destroy(self):
+ pass
+
+ def homeContent(self, filter):
+ cdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/get/filer/type", headers=self.getheaders()).json()
+ fdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/get/filer/list", headers=self.getheaders()).json()
+ result = {}
+ classes = []
+ filters={}
+ for k in cdata['data']:
+ classes.append({
+ 'type_name': k['typeName'],
+ 'type_id': str(k['typeId']),
+ })
+ sort_values = [{"n": "最近更新", "v": "2"},{"n": "人气高低", "v": "3"}, {"n": "评分高低", "v": "4"}]
+ for tid, d in fdata['data'].items():
+ current_sort_values = sort_values.copy()
+ if tid == '1':
+ del current_sort_values[0]
+ filters[tid] = [
+ {"key": "type", "name": "类型",
+ "value": [{"n": i["itemText"], "v": i["itemValue"]} for i in d["typeList"]]},
+
+ *([] if not d["plotList"] else [{"key": "v_class", "name": "剧情",
+ "value": [{"n": i["itemText"], "v": i["itemText"]}
+ for i in d["plotList"]]}]),
+
+ {"key": "area", "name": "地区",
+ "value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["districtList"]]},
+
+ {"key": "year", "name": "年份",
+ "value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["yearList"]]},
+
+ {"key": "lang", "name": "语言",
+ "value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["languageList"]]},
+
+ {"key": "sort", "name": "排序", "value": current_sort_values}
+ ]
+ result['class'] = classes
+ result['filters'] = filters
+ return result
+
+ def homeVideoContent(self):
+ data1 = self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/home/all/list", headers=self.getheaders()).json()
+ data2=self.fetch(f"{self.host}/api/mw-movie/anonymous/home/hotSearch",headers=self.getheaders()).json()
+ data=[]
+ for i in data1['data'].values():
+ data.extend(i['list'])
+ data.extend(data2['data'])
+ vods=self.getvod(data)
+ return {'list':vods}
+
+ def categoryContent(self, tid, pg, filter, extend):
+
+ params = {
+ "area": extend.get('area', ''),
+ "filterStatus": "1",
+ "lang": extend.get('lang', ''),
+ "pageNum": pg,
+ "pageSize": "30",
+ "sort": extend.get('sort', '1'),
+ "sortBy": "1",
+ "type": extend.get('type', ''),
+ "type1": tid,
+ "v_class": extend.get('v_class', ''),
+ "year": extend.get('year', '')
+ }
+ data = self.fetch(f"{self.host}/api/mw-movie/anonymous/video/list?{self.js(params)}", headers=self.getheaders(params)).json()
+ result = {}
+ result['list'] = self.getvod(data['data']['list'])
+ result['page'] = pg
+ result['pagecount'] = 9999
+ result['limit'] = 90
+ result['total'] = 999999
+ return result
+
+ def detailContent(self, ids):
+ data=self.fetch(f"{self.host}/api/mw-movie/anonymous/video/detail?id={ids[0]}",headers=self.getheaders({'id':ids[0]})).json()
+ vod=self.getvod([data['data']])[0]
+ vod['vod_play_from']='金牌'
+ vod['vod_play_url'] = '#'.join(
+ f"{i['name'] if len(vod['episodelist']) > 1 else vod['vod_name']}${ids[0]}@@{i['nid']}" for i in
+ vod['episodelist'])
+ vod.pop('episodelist', None)
+ return {'list':[vod]}
+
+ def searchContent(self, key, quick, pg="1"):
+ params = {
+ "keyword": key,
+ "pageNum": pg,
+ "pageSize": "8",
+ "sourceCode": "1"
+ }
+ data=self.fetch(f"{self.host}/api/mw-movie/anonymous/video/searchByWord?{self.js(params)}",headers=self.getheaders(params)).json()
+ vods=self.getvod(data['data']['result']['list'])
+ return {'list':vods,'page':pg}
+
+ def playerContent(self, flag, id, vipFlags):
+ self.header = {
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36',
+ 'sec-ch-ua-platform': '"Windows"',
+ 'DNT': '1',
+ 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"',
+ 'sec-ch-ua-mobile': '?0',
+ 'Origin': self.host,
+ 'Referer': f'{self.host}/'
+ }
+ ids=id.split('@@')
+ pdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/v2/video/episode/url?clientType=1&id={ids[0]}&nid={ids[1]}",headers=self.getheaders({'clientType':'1','id': ids[0], 'nid': ids[1]})).json()
+ vlist=[]
+ for i in pdata['data']['list']:vlist.extend([i['resolutionName'],i['url']])
+ return {'parse':0,'url':vlist,'header':self.header}
+
+ def localProxy(self, param):
+ pass
+
+ def host_late(self, url_list):
+ if isinstance(url_list, str):
+ urls = [u.strip() for u in url_list.split(',')]
+ else:
+ urls = url_list
+ if len(urls) <= 1:
+ return urls[0] if urls else ''
+
+ results = {}
+ threads = []
+
+ def test_host(url):
+ try:
+ start_time = time.time()
+ response = requests.head(url, timeout=1.0, allow_redirects=False)
+ delay = (time.time() - start_time) * 1000
+ results[url] = delay
+ except Exception as e:
+ results[url] = float('inf')
+ for url in urls:
+ t = threading.Thread(target=test_host, args=(url,))
+ threads.append(t)
+ t.start()
+ for t in threads:
+ t.join()
+ return min(results.items(), key=lambda x: x[1])[0]
+
+ def md5(self, sign_key):
+ md5_hash = MD5.new()
+ md5_hash.update(sign_key.encode('utf-8'))
+ md5_result = md5_hash.hexdigest()
+ return md5_result
+
+ def js(self, param):
+ return '&'.join(f"{k}={v}" for k, v in param.items())
+
+ def getheaders(self, param=None):
+ if param is None:param = {}
+ t=str(int(time.time()*1000))
+ param['key']='cb808529bae6b6be45ecfab29a4889bc'
+ param['t']=t
+ sha1_hash = SHA1.new()
+ sha1_hash.update(self.md5(self.js(param)).encode('utf-8'))
+ sign = sha1_hash.hexdigest()
+ deviceid = str(uuid.uuid4())
+ headers = {
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36',
+ 'Accept': 'application/json, text/plain, */*',
+ 'sign': sign,
+ 't': t,
+ 'deviceid':deviceid
+ }
+ return headers
+
+ def convert_field_name(self, field):
+ field = field.lower()
+ if field.startswith('vod') and len(field) > 3:
+ field = field.replace('vod', 'vod_')
+ if field.startswith('type') and len(field) > 4:
+ field = field.replace('type', 'type_')
+ return field
+
+ def getvod(self, array):
+ return [{self.convert_field_name(k): v for k, v in item.items()} for item in array]
+