更新源 202607150606

This commit is contained in:
github-actions[bot]
2026-07-15 06:06:24 +00:00
parent afc62d6a5b
commit 8112426ca0
11 changed files with 2365 additions and 27 deletions
+9 -9
View File
@@ -1,5 +1,5 @@
{
"spider": "./jar/spider.jar;md5;dc774ec05ddb2be146f3fee027d5b51a",
"spider": "./jar/spider.jar;md5;20b61ea1e8772fd2f8e013c5c2fab86f",
"lives": [
{"name": "live","boot": false,"type": 0,"url": "./tv.txt","playerType": 2,"ua": "okhttp/3.8.1","timeout": 20,"epg": "https://epg.cdn.loc.cc/?ch={name}&date={date}","logo": "https://logo.wyfc.qzz.io/{name}.png"}
],
@@ -17,6 +17,7 @@
{"key": "qiletv","name": "奇乐影视","type": 3,"api": "csp_XBPQ","searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"ext": "./json/奇乐.json","header": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key": "热播影视","name": "热播 • APP","type": 3,"api": "csp_AppRJ","searchable": 1,"quickSearch": 1,"filterable": 0,"ext": {"url": "http://v.rbotv.cn"}},
{"key": "三秋影视","name": "三秋 • APP","type": 3,"api": "csp_App3Q"},
{"key": "马猴影视","name": "马猴 • APP","type": 3,"api": "csp_AppMH"},
{"key": "韩圈","name": "韩圈 • APP","type": 3,"api": "csp_Hxq","ext": "http://www.小不点.com/api/2026/fishhxq.php"},
{"key": "无极99","name": "无极 • APP","type": 3,"api": "csp_App99","ext": {"host": "http://111.170.140.70:19987/app/bn","LoginPath": "/app/log","version": "","appkey": "c403317c2a2946659b0bcbabdd5de43a","versionName": "1.0.1","name": "无极影视","package": "com.wujiys.app","buildNumber": "2001","buildSignature": "A0859BDC35AD3E81D4051E2A4678C77C62773EDB3423DDF1DEFF94CB331D398E"}},
{"key": "橙子99","name": "橙子 • APP","type": 3,"api": "csp_App99","ext": {"host": "http://113.31.180.218:19987/app/bn","LoginPath": "/app/log","appkey": "6e8f3764a0644f3686ab5cdd57bcdf48","versionName": "4.3.1","name": "小橙子","package": "com.xchengzix.app","buildNumber": "2001","buildSignature": "99DA983CF878CEC016616175464A91A94775E20A489F07F76FCDEA1A0F2B6B45"}},
@@ -28,8 +29,6 @@
{"key": "华谊","name": "华谊 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "华谊","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp9Ek4wIlQAtwFnuBRlsFiow2tr+4UOciGeNKbY7nL74etUqUb6fvpOSOHhFEfaWlfwUpOB17x3JEL3No19nfjCeVYrYPjlJcgoqUWH/tfIfFAQWvtxBIBlKazkhw8d3ChysWmeWRikKqkBsVRY4oqNPuj4sjm6Zult0U4I4prRQIDAQAB","dataKey": "NDBYSZR1DMRRZ05NSUCWEJNIYWLBPT0=","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.muyue.tool","host": "","site": "https://vip.123pan.cn/1851089669/oss/az5.txt","version": "1.0.0.4","decrypt": "1"}},
{"key": "苹果","name": "苹果 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "小苹果","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCduNEnfxGaLuQRk5ABzXHhPV43zi00sCHjLo8BYc+Wi6xXm2b4v0i28Sq4WlNCKhseft9fz8kO/qLr6/022o1RcuOU7e4GFL3U9WnNODwRBYSYWd+K8nqpI/tAUDmZEBGRWqjrc7x6aMl3A+xpnWkLbPCLsuhbuuUE3tv09oeOpwIDAQAB","dataKey": "S0VNDTJHOFHHCNMRAW5IV2TOS2PBPQ==","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.juechufsh.android.xpg1","host": "","site": "https://xpgandroid03-1372779881.cos.ap-beijing.myqcloud.com/app_xpg_android.txt","version": "1.0.0.3","decrypt": "0"}},
{"key": "薯条","name": "薯条 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "薯条影视","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCduNEnfxGaLuQRk5ABzXHhPV43zi00sCHjLo8BYc+Wi6xXm2b4v0i28Sq4WlNCKhseft9fz8kO/qLr6/022o1RcuOU7e4GFL3U9WnNODwRBYSYWd+K8nqpI/tAUDmZEBGRWqjrc7x6aMl3A+xpnWkLbPCLsuhbuuUE3tv09oeOpwIDAQAB","dataKey": "A1VACZJWDKRZY1P3MFV0DDRAZ3F3PT0=","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.st.standroid","host": "","site": "https://dyttandroid-1372779881.cos.ap-guangzhou.myqcloud.com/app_dyttandroid.txt","version": "5.0.0.1","decrypt": "0"}},
{"key": "久久","name": "久久 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "http://99cms.jj99.icu:8199/api.php/app/"},
{"key": "闪影","name": "闪影 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "http://38.47.213.61:41271/mogai_api.php/v1.vod"},
{"key": "飞飞","name": "飞飞 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "https://qpcj.69mini.com/api.php/app/"},
{"key": "金牌","name": "金牌 • APP","type": 3,"api": "csp_AppQi","searchable": 1,"changeable": 1,"ext": {"url": "https://qj4.catbb.xyz","dataKey": "eecbio48dsq13kkk","dataIv": "eecbio48dsq13kkk","search": "searchList4","init": "initV122","ua": "okhttp/3.10.0"}},
{"key": "蓝鹰","name": "蓝鹰 • APP","type": 3,"api": "csp_AppQi","searchable": 1,"changeable": 1,"ext": {"site": "https://xinlanyinghb.oss-cn-wuhan-lr.aliyuncs.com/xly.txt","dataKey": "ca94b06ca359d80e","dataIv": "ca94b06ca359d80e","init": "initV120","ua": "okhttp/3.10.0"}},
@@ -57,19 +56,20 @@
{"key": "笒鬼短剧","name": "笒鬼 • 短剧","type": 3,"api": "csp_CenguiDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "好看短剧","name": "好看 • 短剧","type": 3,"api": "csp_HaokanDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "围观短剧","name": "围观 • 短剧","type": 3,"api": "csp_WeiguanDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "星芽短剧","name": "星芽 • 短剧","type": 3,"api": "./py/星芽短剧.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "星芽短剧","name": "星芽 • 短剧","type": 3,"api": "./py/XYDJ.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "山楂影视","name": "山楂 • 影视","type": 3,"api": "./py/SZYS.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1},
{"key": "瓜子影视","name": "瓜子 • 影视","type": 3,"api": "./py/GZYS.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1},
{"key": "独播影视","name": "独播 • 影视","type": 3,"api": "csp_Duboku"},
{"key": "厂长影视","name": "厂长 • 影视","type": 3,"api": "csp_Czsapp","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "https://cz01.vip"},
{"key": "金牌影视","name": "金牌 • 影视","type": 3,"api": "csp_Jpys","ext": "https://y2s52n7.com,https://m.hkybqufgh.com,https://m.sizhengxt.com,https://m.9zhoukj.com,https://m.sizhengxt.com,https://m.jiabaide.cn"},
{"key": "修罗影视","name": "修罗 • 影视","api": "csp_Xlys","type": 3,"ext": "https://www.xlysw.ggff.net"},
{"key": "瓜子影视","name": "瓜子 • 影视","type": 3,"api": "csp_Gz360"},
{"key": "云播影视","name": "云播 • 影视","type": 3,"api": "csp_Tvyb"},
{"key": "骚火影视","name": "骚火 • 影视","type": 3,"api": "csp_SaoHuo","ext": "https://shdy5.us"},
{"key": "农民影视","name": "农民 • 影视","type": 3,"api": "csp_Wwys","ext": "https://vip.wwgz.cn:5200"},
{"key": "爱看机器人","name": "爱看 • 影视","type": 3,"api": "csp_Ikanbot","searchable": 1,"quickSearch": 1,"filterable": 1},
{"key": "小镇影视","name": "小镇 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/XZYS.json"},
{"key": "面包影视","name": "面包 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/MBYS.json"},
{"key": "永乐影视","name": "永乐 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/YLYS.json"},
{"key": "枫叶影视","name": "枫叶 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "手机","简介": "text cor3\">[*],&&</div>","图片": "data-src=\"&&\"[替换:amp;>>空]","主页url": "https://www.cd-zj.com/label/qq.html","副标题": "ft2\">&&</i>","线路数组": "<i class=\"fa ds-dianying\">&&</span>","播放数组": "anthology-list-play size\">&&</ul>","分类url": "https://www.cd-zj.com/cupfox-list/{cateId}-{area}--{class}-----{catePg}---{year}.html","分类": "电影$1#电视剧$2#综艺$3#动漫$4#短剧$5"}},
{"key": "小镇影视","name": "小镇 • 影视","type": 3,"api": "csp_XBPQ","ext": {"线路数组": "<a data-toggle=\"tab\"&&/a>","线路标题": ">&&<","分类url": "http://dyxz.tv/list/{cateId}_{catePg}.html","分类": "电影$1#电视剧$2#动漫$3#综艺$4"}},
{"key": "面包影视","name": "面包 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "User-Agent$MOBILE_UA","编码": "UTF-8","分类url": "https://v.aiwule.com/vodshow/{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html","分类": "电影$20#电视剧$21#动漫$23#综艺$22#短剧$47","简介": "简介:&&"}},
{"key": "永乐影视","name": "永乐 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "User-Agent$MOBILE_UA","编码": "UTF-8","分类": "电影$1#电视剧$2#综艺$3#动漫$4","类型": "动作片$6#喜剧片$7#爱情片$8#科幻片$9#奇幻片$10#恐怖片$11#剧情片$12#战争片$20#动画片$26#悬疑片$22#冒险片$23#犯罪片$24#惊悚片$45#歌舞片$46#灾难片$47#网络片$48||国产剧$13#港台剧$14#日剧$15#韩剧$33#欧美剧$16#泰剧$34#新马剧$35#其他剧$25||内地综艺$27#港台综艺$28#日本综艺$29#韩国综艺$36#欧美综艺$30#新马泰综艺$37#其他综艺$38||国产动漫$31#日本动漫$32#韩国动漫$39#港台动漫$40#新马泰动漫$41#欧美动漫$42#其他动漫$43","分类url": "https://www.ylys.tv/vodshow/{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html"}},
{"key": "剧圈影视","name": "剧圈 • 影视","type": 3,"api": "csp_XYQHiker","ext": "./json/JQYS.json"},
{"key": "1905","name": "1905 • 影视","type": 3,"api": "csp_Web1905","searchable": 1,"quickSearch": 0,"filterable": 0},
{"key": "哆啦新番社","name": "哆啦 • 新番社","type": 3,"api": "csp_XBPQ","style": {"type": "list"},"ext": "./json/DLXFS.json"},
BIN
View File
Binary file not shown.
+9 -9
View File
@@ -1,5 +1,5 @@
{
"spider": "./jar/spider.jar;md5;dc774ec05ddb2be146f3fee027d5b51a",
"spider": "./jar/spider.jar;md5;20b61ea1e8772fd2f8e013c5c2fab86f",
"lives": [
{"name": "migu","type": 0,"url": "https://develop202.github.io/migu_video/interface.txt","playerType": 1,"ua": "okhttp/3.8.1","timeout": 20,"epg": "https://epg.cdn.loc.cc/?ch={name}&date={date}","logo": "https://logo.wyfc.qzz.io/{name}.png"},
{"name": "live","type": 0,"url": "https://epg.pw/test_channels.m3u","playerType": 1,"ua": "okhttp/3.8.1","timeout": 20,"epg": "https://epg.cdn.loc.cc/?ch={name}&date={date}","logo": "https://logo.wyfc.qzz.io/{name}.png"},
@@ -18,6 +18,7 @@
{"key": "qiletv","name": "奇乐影视","type": 3,"api": "csp_XBPQ","searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"ext": "./json/奇乐.json","header": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}},
{"key": "热播影视","name": "热播 • APP","type": 3,"api": "csp_AppRJ","searchable": 1,"quickSearch": 1,"filterable": 0,"ext": {"url": "http://v.rbotv.cn"}},
{"key": "三秋影视","name": "三秋 • APP","type": 3,"api": "csp_App3Q"},
{"key": "马猴影视","name": "马猴 • APP","type": 3,"api": "csp_AppMH"},
{"key": "韩圈","name": "韩圈 • APP","type": 3,"api": "csp_Hxq","ext": "http://www.小不点.com/api/2026/fishhxq.php"},
{"key": "无极99","name": "无极 • APP","type": 3,"api": "csp_App99","ext": {"host": "http://111.170.140.70:19987/app/bn","LoginPath": "/app/log","version": "","appkey": "c403317c2a2946659b0bcbabdd5de43a","versionName": "1.0.1","name": "无极影视","package": "com.wujiys.app","buildNumber": "2001","buildSignature": "A0859BDC35AD3E81D4051E2A4678C77C62773EDB3423DDF1DEFF94CB331D398E"}},
{"key": "橙子99","name": "橙子 • APP","type": 3,"api": "csp_App99","ext": {"host": "http://113.31.180.218:19987/app/bn","LoginPath": "/app/log","appkey": "6e8f3764a0644f3686ab5cdd57bcdf48","versionName": "4.3.1","name": "小橙子","package": "com.xchengzix.app","buildNumber": "2001","buildSignature": "99DA983CF878CEC016616175464A91A94775E20A489F07F76FCDEA1A0F2B6B45"}},
@@ -29,8 +30,6 @@
{"key": "华谊","name": "华谊 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "华谊","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp9Ek4wIlQAtwFnuBRlsFiow2tr+4UOciGeNKbY7nL74etUqUb6fvpOSOHhFEfaWlfwUpOB17x3JEL3No19nfjCeVYrYPjlJcgoqUWH/tfIfFAQWvtxBIBlKazkhw8d3ChysWmeWRikKqkBsVRY4oqNPuj4sjm6Zult0U4I4prRQIDAQAB","dataKey": "NDBYSZR1DMRRZ05NSUCWEJNIYWLBPT0=","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.muyue.tool","host": "","site": "https://vip.123pan.cn/1851089669/oss/az5.txt","version": "1.0.0.4","decrypt": "1"}},
{"key": "苹果","name": "苹果 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "小苹果","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCduNEnfxGaLuQRk5ABzXHhPV43zi00sCHjLo8BYc+Wi6xXm2b4v0i28Sq4WlNCKhseft9fz8kO/qLr6/022o1RcuOU7e4GFL3U9WnNODwRBYSYWd+K8nqpI/tAUDmZEBGRWqjrc7x6aMl3A+xpnWkLbPCLsuhbuuUE3tv09oeOpwIDAQAB","dataKey": "S0VNDTJHOFHHCNMRAW5IV2TOS2PBPQ==","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.juechufsh.android.xpg1","host": "","site": "https://xpgandroid03-1372779881.cos.ap-beijing.myqcloud.com/app_xpg_android.txt","version": "1.0.0.3","decrypt": "0"}},
{"key": "薯条","name": "薯条 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "薯条影视","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCduNEnfxGaLuQRk5ABzXHhPV43zi00sCHjLo8BYc+Wi6xXm2b4v0i28Sq4WlNCKhseft9fz8kO/qLr6/022o1RcuOU7e4GFL3U9WnNODwRBYSYWd+K8nqpI/tAUDmZEBGRWqjrc7x6aMl3A+xpnWkLbPCLsuhbuuUE3tv09oeOpwIDAQAB","dataKey": "A1VACZJWDKRZY1P3MFV0DDRAZ3F3PT0=","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.st.standroid","host": "","site": "https://dyttandroid-1372779881.cos.ap-guangzhou.myqcloud.com/app_dyttandroid.txt","version": "5.0.0.1","decrypt": "0"}},
{"key": "久久","name": "久久 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "http://99cms.jj99.icu:8199/api.php/app/"},
{"key": "闪影","name": "闪影 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "http://38.47.213.61:41271/mogai_api.php/v1.vod"},
{"key": "飞飞","name": "飞飞 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "https://qpcj.69mini.com/api.php/app/"},
{"key": "金牌","name": "金牌 • APP","type": 3,"api": "csp_AppQi","searchable": 1,"changeable": 1,"ext": {"url": "https://qj4.catbb.xyz","dataKey": "eecbio48dsq13kkk","dataIv": "eecbio48dsq13kkk","search": "searchList4","init": "initV122","ua": "okhttp/3.10.0"}},
{"key": "蓝鹰","name": "蓝鹰 • APP","type": 3,"api": "csp_AppQi","searchable": 1,"changeable": 1,"ext": {"site": "https://xinlanyinghb.oss-cn-wuhan-lr.aliyuncs.com/xly.txt","dataKey": "ca94b06ca359d80e","dataIv": "ca94b06ca359d80e","init": "initV120","ua": "okhttp/3.10.0"}},
@@ -58,19 +57,20 @@
{"key": "笒鬼短剧","name": "笒鬼 • 短剧","type": 3,"api": "csp_CenguiDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "好看短剧","name": "好看 • 短剧","type": 3,"api": "csp_HaokanDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "围观短剧","name": "围观 • 短剧","type": 3,"api": "csp_WeiguanDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "星芽短剧","name": "星芽 • 短剧","type": 3,"api": "./py/星芽短剧.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "星芽短剧","name": "星芽 • 短剧","type": 3,"api": "./py/XYDJ.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "山楂影视","name": "山楂 • 影视","type": 3,"api": "./py/SZYS.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1},
{"key": "瓜子影视","name": "瓜子 • 影视","type": 3,"api": "./py/GZYS.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1},
{"key": "独播影视","name": "独播 • 影视","type": 3,"api": "csp_Duboku"},
{"key": "厂长影视","name": "厂长 • 影视","type": 3,"api": "csp_Czsapp","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "https://cz01.vip"},
{"key": "金牌影视","name": "金牌 • 影视","type": 3,"api": "csp_Jpys","ext": "https://y2s52n7.com,https://m.hkybqufgh.com,https://m.sizhengxt.com,https://m.9zhoukj.com,https://m.sizhengxt.com,https://m.jiabaide.cn"},
{"key": "修罗影视","name": "修罗 • 影视","api": "csp_Xlys","type": 3,"ext": "https://www.xlysw.ggff.net"},
{"key": "瓜子影视","name": "瓜子 • 影视","type": 3,"api": "csp_Gz360"},
{"key": "云播影视","name": "云播 • 影视","type": 3,"api": "csp_Tvyb"},
{"key": "骚火影视","name": "骚火 • 影视","type": 3,"api": "csp_SaoHuo","ext": "https://shdy5.us"},
{"key": "农民影视","name": "农民 • 影视","type": 3,"api": "csp_Wwys","ext": "https://vip.wwgz.cn:5200"},
{"key": "爱看机器人","name": "爱看 • 影视","type": 3,"api": "csp_Ikanbot","searchable": 1,"quickSearch": 1,"filterable": 1},
{"key": "小镇影视","name": "小镇 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/XZYS.json"},
{"key": "面包影视","name": "面包 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/MBYS.json"},
{"key": "永乐影视","name": "永乐 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/YLYS.json"},
{"key": "枫叶影视","name": "枫叶 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "手机","简介": "text cor3\">[*],&&</div>","图片": "data-src=\"&&\"[替换:amp;>>空]","主页url": "https://www.cd-zj.com/label/qq.html","副标题": "ft2\">&&</i>","线路数组": "<i class=\"fa ds-dianying\">&&</span>","播放数组": "anthology-list-play size\">&&</ul>","分类url": "https://www.cd-zj.com/cupfox-list/{cateId}-{area}--{class}-----{catePg}---{year}.html","分类": "电影$1#电视剧$2#综艺$3#动漫$4#短剧$5"}},
{"key": "小镇影视","name": "小镇 • 影视","type": 3,"api": "csp_XBPQ","ext": {"线路数组": "<a data-toggle=\"tab\"&&/a>","线路标题": ">&&<","分类url": "http://dyxz.tv/list/{cateId}_{catePg}.html","分类": "电影$1#电视剧$2#动漫$3#综艺$4"}},
{"key": "面包影视","name": "面包 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "User-Agent$MOBILE_UA","编码": "UTF-8","分类url": "https://v.aiwule.com/vodshow/{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html","分类": "电影$20#电视剧$21#动漫$23#综艺$22#短剧$47","简介": "简介:&&"}},
{"key": "永乐影视","name": "永乐 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "User-Agent$MOBILE_UA","编码": "UTF-8","分类": "电影$1#电视剧$2#综艺$3#动漫$4","类型": "动作片$6#喜剧片$7#爱情片$8#科幻片$9#奇幻片$10#恐怖片$11#剧情片$12#战争片$20#动画片$26#悬疑片$22#冒险片$23#犯罪片$24#惊悚片$45#歌舞片$46#灾难片$47#网络片$48||国产剧$13#港台剧$14#日剧$15#韩剧$33#欧美剧$16#泰剧$34#新马剧$35#其他剧$25||内地综艺$27#港台综艺$28#日本综艺$29#韩国综艺$36#欧美综艺$30#新马泰综艺$37#其他综艺$38||国产动漫$31#日本动漫$32#韩国动漫$39#港台动漫$40#新马泰动漫$41#欧美动漫$42#其他动漫$43","分类url": "https://www.ylys.tv/vodshow/{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html"}},
{"key": "剧圈影视","name": "剧圈 • 影视","type": 3,"api": "csp_XYQHiker","ext": "./json/JQYS.json"},
{"key": "1905","name": "1905 • 影视","type": 3,"api": "csp_Web1905","searchable": 1,"quickSearch": 0,"filterable": 0},
{"key": "哆啦新番社","name": "哆啦 • 新番社","type": 3,"api": "csp_XBPQ","style": {"type": "list"},"ext": "./json/DLXFS.json"},
+496
View File
@@ -0,0 +1,496 @@
# coding = utf-8
#!/usr/bin/python
import re
import sys
import json
import time
import base64
import hashlib
import random
import string
import urllib.parse
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base.spider import Spider
sys.path.append('..')
class Spider(Spider):
def __init__(self):
self.name = "瓜子"
self.hosts = [
'https://apinew.uozvr.com',
'https://api.w32z7vtd.com',
'https://api.6a7nnf7.com',
'https://api.umygrx3.com',
'https://api.rmedphk.com'
]
self.host_index = 0
self.host = self.hosts[self.host_index]
# AES 固定密钥(与Java版一致)
self.AES_KEY = 'OITxa5OqAYjhswxx'
self.AES_IV = 'rCMNwZASNBKZ8mXV'
# RSA 公钥/私钥
self.RSA_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUM5+/y8sPsWkd1/RQS64X259EUwxFXFE5HlA65MqrxnPs0JqoSRojSDy5QhwvROlaD6TwRQHKMY2OAZ6SnQeUJsChTEFIR9qUkwrs3/MVUMxjsv6JS6Oe/juclyJGTgVmDhB55EafXsD0SQYVj/QXXsxR6ewR5E2kL52yAAD4yQIDAQAB"
self.RSA_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGAe6hKrWLi1zQmjTT1
ozbE4QdFeJGNxubxld6GrFGximxfMsMB6BpJhpcTouAqywAFppiKetUBBbXwYsYU
1wNr648XVmPmCMCy4rY8vdliFnbMUj086DU6Z+/oXBdWU3/b1G0DN3E9wULRSwcK
ZT3wj/cCI1vsCm3gj2R5SqkA9Y0CAwEAAQKBgAJH+4CxV0/zBVcLiBCHvSANm0l7
HetybTh/j2p0Y1sTXro4ALwAaCTUeqdBjWiLSo9lNwDHFyq8zX90+gNxa7c5EqcW
V9FmlVXr8VhfBzcZo1nXeNdXFT7tQ2yah/odtdcx+vRMSGJd1t/5k5bDd9wAvYdI
DblMAg+wiKKZ5KcdAkEA1cCakEN4NexkF5tHPRrR6XOY/XHfkqXxEhMqmNbB9U34
saTJnLWIHC8IXys6Qmzz30TtzCjuOqKRRy+FMM4TdwJBAJQZFPjsGC+RqcG5UvVM
iMPhnwe/bXEehShK86yJK/g/UiKrO87h3aEu5gcJqBygTq3BBBoH2md3pr/W+hUM
WBsCQQChfhTIrdDinKi6lRxrdBnn0Ohjg2cwuqK5zzU9p/N+S9x7Ck8wUI53DKm8
jUJE8WAG7WLj/oCOWEh+ic6NIwTdAkEAj0X8nhx6AXsgCYRql1klbqtVmL8+95KZ
K7PnLWG/IfjQUy3pPGoSaZ7fdquG8bq8oyf5+dzjE/oTXcByS+6XRQJAP/5ciy1b
L3NhUhsaOVy55MHXnPjdcTX0FaLi+ybXZIfIQ2P4rb19mVq1feMbCXhz+L1rG8oa
t5lYKfpe8k83ZA==
-----END RSA PRIVATE KEY-----"""
self.DEVICE_OLD_KEY = "aLFBMWpxBrIDAD1Si/KVvm41"
# 设备信息(随机生成)
self.deviceId = str(864150060000000 + random.randint(0, 9999))
self.deviceKey = ''.join(random.choices('0123456789ABCDEF', k=40)) # 20字节hex大写
self.token = ""
self.token_id = ""
self.registered = False
self.header = {
'User-Agent': 'Lavf/57.83.100',
'code': 'GZ0369',
'deviceId': self.deviceId,
'lang': 'zh_cn',
'Cache-Control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded',
'Version': '2604028',
'PackageName': 'com.ae06aebdbb.y286327f5a.ofe849883320260517',
'Ver': '3.0.3.2',
'api-ver': '3.0.3.2',
'Referer': self.host
}
self.cache = {}
self.cache_timeout = 300
# 初始化token
self.init_token()
def getName(self):
return self.name
def init(self, extend=''):
pass
# ---------- 设备注册与认证 ----------
def init_token(self):
"""初始化token:注册设备 -> 刷新"""
print("===== 初始化设备认证 =====")
try:
if not self.registered:
self.sign_up()
# 刷新获取最终token
self.refresh_token()
except Exception as e:
print(f"初始化token失败: {e}")
# 兜底使用原有硬编码(几乎没用)
self.token = '024212ef0975c5306a1434e113a46463.bc77313e11a248558a6ca244ca980944ec3421fa480c50e0229ad91f1cb15aea582603202cd71796885c9e5163e500f1b72f737059aff1ddb8beea47c5a331d6760540345b7f88b2302a0e6e09589f9dcf3ff9175d8c905f990203f5fc04748008ea7a366571cbf5b09509a873dcfba3cf1d5590385f5f7ef6e01d1850974aa220eb5178c89e61c24411af9b9a19435e.06fde789ece48d9b33c5dc857e04e9b5838f08264d928b87237d3476c4484b46'
def sign_up(self):
"""注册设备"""
print("注册新设备...")
params = {
"new_key": self.deviceKey,
"old_key": self.DEVICE_OLD_KEY,
"phone_type": 1,
"code": ""
}
result = self._auth_request('/App/Authentication/Device/signUp', params)
self._apply_auth(result)
self.registered = True
def sign_in(self):
"""登录设备"""
print("设备登录...")
params = {
"new_key": self.deviceKey,
"old_key": self.DEVICE_OLD_KEY
}
result = self._auth_request('/App/Authentication/Device/signIn', params)
self._apply_auth(result)
def _apply_auth(self, result):
"""从认证响应中提取token"""
new_token = result.get('token', '')
if not new_token:
raise Exception("认证失败,无token返回: {}".format(result))
self.token = new_token
new_token_id = result.get('app_user_id', '')
if new_token_id:
self.token_id = new_token_id
print(f"获取token成功, token前缀: {self.token[:30]}...")
def refresh_token(self):
"""刷新token"""
print("刷新token...")
result = self._auth_request('/App/Authentication/Authenticator/refresh', {})
self._apply_auth(result)
def _auth_request(self, path, params):
"""认证类请求(不需要ensure_token"""
return self._send_encrypted_request(params, path, is_auth=True)
# ---------- 业务请求核心(修复加密与签名) ----------
def ensure_token(self):
"""确保token有效,如未就绪则重新获取"""
if not self.token or not self.token_id:
if self.registered:
self.sign_in()
else:
self.sign_up()
self.refresh_token()
def _send_encrypted_request(self, data, path, is_auth=False):
"""
发送加密请求,返回解密后的字典
:param data: 业务参数字典
:param path: 请求路径
:param is_auth: 是否为认证类请求(signUp/signIn/refresh),此时不使用ensure_token
"""
try:
if not is_auth:
self.ensure_token()
# 1. 将参数转为JSON并AES加密
json_params = json.dumps(data)
encrypted = self.aes_encrypt(json_params, self.AES_KEY, self.AES_IV)
request_key = encrypted.upper() # Java中是bytesToHex(encrypted).toUpperCase()
# 2. 生成keys (RSA加密 iv/key JSON)
key_json = json.dumps({"iv": self.AES_IV, "key": self.AES_KEY})
keys = self.rsa_encrypt(key_json, self.RSA_PUBLIC_KEY)
# 3. 生成签名
t = str(int(time.time()))
sign_str = f"token_id=,token={self.token},phone_type=1,request_key={request_key},app_id=1,time={t},keys={keys}*&zvdvdvddbfikkkumtmdwqppp?|4Y!s!2br"
signature = self.get_md5(sign_str) # 已改为大写
# 4. 构建请求体
body = {
'token': self.token,
'token_id': '',
'phone_type': '1',
'time': t,
'phone_model': 'xiaomi-25031', # 与Java版保持一致
'keys': keys,
'request_key': request_key,
'signature': signature,
'app_id': '1',
'ad_version': '1'
}
# 5. 发送请求
url = f"{self.host}{path}"
response = self.post(url, headers=self.header, data=body, timeout=10)
if response.status_code != 200:
raise Exception(f"HTTP {response.status_code}")
resp_json = response.json()
# 检查业务code(若不为200可能token过期)
if 'code' in resp_json and resp_json['code'] != 200:
print(f"业务错误码: {resp_json['code']}, 信息: {resp_json}")
# 如果不是认证请求,尝试重新获取token后重试一次(这里简单处理,外层get_data已有重试)
raise Exception("业务错误")
data_section = resp_json.get('data')
if not data_section:
raise Exception("响应缺少data字段")
encrypted_response = data_section.get('response_key', '')
encrypted_keys = data_section.get('keys', '')
# 6. 解密响应
decrypted_keys_json = self.rsa_decrypt(encrypted_keys, self.RSA_PRIVATE_KEY)
key_info = json.loads(decrypted_keys_json)
resp_key = key_info['key']
resp_iv = key_info['iv']
decrypted_data = self.aes_decrypt(encrypted_response, resp_key, resp_iv)
return json.loads(decrypted_data)
except Exception as e:
print(f"请求失败 [{path}]: {e}")
return None
def get_data(self, data, path, use_cache=True):
"""带重试和域名轮询的数据获取(保持原框架)"""
try:
cache_key = f"{path}_{hash(str(data))}" if use_cache else None
if use_cache and cache_key in self.cache:
cached_data, timestamp = self.cache[cache_key]
if time.time() - timestamp < self.cache_timeout:
return cached_data
for attempt in range(3):
tried = 0
while tried < len(self.hosts):
self.host = self.hosts[self.host_index]
self.header['Referer'] = self.host
result = self._send_encrypted_request(data, path)
if result is not None:
print(f"请求成功: {path}, 域名: {self.host}")
if use_cache and cache_key:
self.cache[cache_key] = (result, time.time())
return result
# 切换到下一个域名
self.host_index = (self.host_index + 1) % len(self.hosts)
tried += 1
# 所有域名失败,尝试重新认证并重试
if attempt < 2:
print("所有域名失败,尝试重新认证...")
try:
self.ensure_token()
except:
pass
self.host_index = 0
else:
break
return None
except Exception as e:
print(f"get_data异常: {e}")
return None
# ---------- 加解密工具 ----------
def aes_encrypt(self, text, key, iv):
try:
key_bytes = key.encode('utf-8')
iv_bytes = iv.encode('utf-8')
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
encrypted = cipher.encrypt(pad(text.encode('utf-8'), AES.block_size))
return encrypted.hex().upper()
except Exception as e:
print(f"AES加密失败: {e}")
return ""
def aes_decrypt(self, text, key, iv):
try:
key_bytes = key.encode('utf-8')
iv_bytes = iv.encode('utf-8')
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
encrypted_bytes = bytes.fromhex(text)
decrypted = unpad(cipher.decrypt(encrypted_bytes), AES.block_size)
return decrypted.decode('utf-8')
except Exception as e:
print(f"AES解密失败: {e}")
return ""
def rsa_encrypt(self, text, public_key_str):
"""RSA公钥加密(PKCS1v1.5"""
try:
key = RSA.import_key("-----BEGIN PUBLIC KEY-----\n" + public_key_str + "\n-----END PUBLIC KEY-----")
cipher = PKCS1_v1_5.new(key)
encrypted = cipher.encrypt(text.encode('utf-8'))
return base64.b64encode(encrypted).decode('utf-8')
except Exception as e:
print(f"RSA加密失败: {e}")
return ""
def rsa_decrypt(self, encrypted_data, private_key_str):
"""RSA私钥解密"""
try:
encrypted_bytes = base64.b64decode(encrypted_data)
rsa_key = RSA.import_key(private_key_str)
cipher = PKCS1_v1_5.new(rsa_key)
decrypted = cipher.decrypt(encrypted_bytes, None)
return decrypted.decode('utf-8') if decrypted else ""
except Exception as e:
print(f"RSA解密失败: {e}")
return ""
def get_md5(self, text):
return hashlib.md5(text.encode()).hexdigest().upper() # 与Java一致大写
# ---------- 业务方法(不变) ----------
def homeContent(self, filter):
result = {}
classes = [
{"type_name": "电影", "type_id": "1"},
{"type_name": "电视剧", "type_id": "2"},
{"type_name": "动漫", "type_id": "4"},
{"type_name": "综艺", "type_id": "3"},
{"type_name": "短剧", "type_id": "64"}
]
result['class'] = classes
filters = {}
for cate in classes:
tid = cate['type_id']
filters[tid] = [
{"key": "area", "name": "地区", "value": [
{"n": "全部", "v": "0"}, {"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": "0"}, {"n": "2025", "v": "2025"}, {"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": "更早", "v": "2004"}
]},
{"key": "sort", "name": "排序", "value": [
{"n": "最新", "v": "d_id"}, {"n": "最热", "v": "d_hits"}, {"n": "推荐", "v": "d_score"}
]}
]
result['filters'] = filters
return result
def homeVideoContent(self):
return {'list': []}
def categoryContent(self, tid, pg, filter, extend):
videos = []
try:
body = {
"area": extend.get('area', '0'),
"year": extend.get('year', '0'),
"pageSize": "30",
"sort": extend.get('sort', 'd_id'),
"page": str(pg),
"tid": tid
}
cache_key = f"category_{tid}_{pg}_{hash(str(body))}"
data = self.get_cached_data(cache_key, body, '/App/IndexList/indexList')
if data and 'list' in data:
for item in data['list']:
vod_continu = item.get('vod_continu', 0)
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}'
video = {
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
"vod_name": item.get('vod_name', ''),
"vod_pic": item.get('vod_pic', ''),
"vod_remarks": remarks
}
videos.append(video)
except Exception as e:
print(f"获取分类内容失败: {e}")
return {'list': videos, 'page': int(pg), 'pagecount': 9999, 'limit': 30, 'total': 999999}
def detailContent(self, ids):
try:
vod_id = ids[0].split('/')[0]
t = str(int(time.time()))
body1 = {"token_id": self.token_id, "vod_id": vod_id, "mobile_time": t, "token": self.token}
qdata = self.get_data(body1, '/App/IndexPlay/playInfo')
body2 = {"vurl_cloud_id": "2", "vod_d_id": vod_id}
jdata = self.get_data(body2, '/App/Resource/Vurl/show')
if not qdata or 'vodInfo' not in qdata:
return {'list': []}
vod = qdata['vodInfo']
video_detail = {
"vod_id": vod_id,
"vod_name": vod.get('vod_name', ''),
"vod_pic": vod.get('vod_pic', ''),
"vod_year": vod.get('vod_year', ''),
"vod_area": vod.get('vod_area', ''),
"vod_actor": vod.get('vod_actor', ''),
"vod_director": vod.get('vod_director', ''),
"vod_content": vod.get('vod_use_content', '').strip(),
"vod_play_from": "瓜子影视"
}
play_list = []
if jdata and 'list' in jdata:
for index, item in enumerate(jdata['list']):
if 'play' in item:
n, p = [], []
for key, value in item['play'].items():
if 'param' in value and value['param']:
n.append(key)
p.append(value['param'])
if p:
play_name = str(index + 1) if len(jdata['list']) != 1 else vod.get('vod_name', '')
play_url = f"{p[-1]}||{'@'.join(n)}"
play_list.append(f"{play_name}${play_url}")
video_detail["vod_play_url"] = "#".join(play_list)
return {'list': [video_detail]}
except Exception as e:
print(f"获取详情失败: {e}")
return {'list': []}
def searchContent(self, key, quick, pg=1):
videos = []
try:
body = {"keywords": key, "order_val": "1", "page": str(pg)}
data = self.get_data(body, '/App/Index/findMoreVod', use_cache=False)
if data and 'list' in data:
for item in data['list']:
vod_continu = item.get('vod_continu', 0)
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}'
videos.append({
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
"vod_name": item.get('vod_name', ''),
"vod_pic": item.get('vod_pic', ''),
"vod_remarks": remarks
})
except Exception as e:
print(f"搜索失败: {e}")
return {'list': videos, 'page': int(pg), 'pagecount': 9999, 'limit': 30, 'total': 999999}
def playerContent(self, flag, id, vipFlags):
try:
parts = id.split('||')
if len(parts) < 2:
return {"parse": 0, "playUrl": "", "url": ""}
param_str = parts[0]
resolutions = parts[1].split('@') if len(parts) > 1 else []
params = {}
for pair in param_str.split('&'):
if '=' in pair:
key, value = pair.split('=', 1)
params[key] = value
if resolutions:
resolutions.sort(key=lambda x: int(x) if x.isdigit() else 0, reverse=True)
params['resolution'] = resolutions[0]
data = self.get_data(params, '/App/Resource/VurlDetail/showOne', use_cache=False)
if data and 'url' in data:
return {"parse": 0, "playUrl": "", "url": data['url'],
"header": json.dumps({"User-Agent": "Lavf/57.83.100", "Referer": "http://WJiZxLXA2.com/"}), 'danmaku': 'http://127.0.0.1:9978/proxy?do=diydanmu'}
return {"parse": 0, "playUrl": "", "url": ""}
except Exception as e:
print(f"播放解析失败: {e}")
return {"parse": 0, "playUrl": "", "url": ""}
def isVideoFormat(self, url):
video_formats = ['.m3u8', '.mp4', '.avi', '.mkv', '.flv', '.ts']
return any(url.lower().endswith(fmt) for fmt in video_formats)
def manualVideoCheck(self):
pass
def localProxy(self, params):
return None
def get_cached_data(self, cache_key, data, path):
current_time = time.time()
if cache_key in self.cache:
cached_data, timestamp = self.cache[cache_key]
if current_time - timestamp < self.cache_timeout:
return cached_data
result = self.get_data(data, path)
if result:
self.cache[cache_key] = (result, current_time)
return result
if __name__ == '__main__':
pass
+330
View File
@@ -0,0 +1,330 @@
# -*- coding: utf-8 -*-
# by @嗷呜
import json
import random
import sys
from base64 import b64encode, b64decode
from concurrent.futures import ThreadPoolExecutor
# 引入 RSA 加解密所需模块
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend=""):
did = self.getdid()
self.headers.update({'deviceId': did})
token = self.gettk()
self.headers.update({'token': token})
def getName(self):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def destroy(self):
pass
# 1. 修改为主机域名
host = 'http://qkys.qukanwh.com'
# 2. 同步原脚本的配置请求头
headers = {
'HOST': 'qkys.qukanwh.com',
'User-Agent': 'okhttp/4.12.0',
'client': 'app',
'deviceType': 'Android',
'Referer': ''
}
# 3. 导入原脚本中的 RSA 密钥对与配置
publicKey_str = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoYt0BP77U+DM08BiI/QbSRIfxijXo85BTPqIM1Ow8BNwhLETzRIZ+dEwdWDbydG/PspgBAfRpGaYVdJYtvaC2JnoO8+Ik6qMWojfEJxSFLa0Pb0A892tun4gsxoEMjcreZ+YGyaBxAfqX0BSMfdrOgIYaZQjYrw9TRLlUT31QoQIDAQAB\n-----END PUBLIC KEY-----"
privateKey_str = "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCquQQ5r6+yJI8CDFkXRp8vUsdD45ov8EP12ooLs56ca2DQXaSNGS9910bAPVA9chkp0mKIvKqjAsHz5Tl9EeNPblarGEeJUIxpxZtiSqNTpvtiD/TjhpzuHYic7RAfQ/h7p/ypE8ymU42pYjsB5t26Mv6XgkLV+jzrSf73HlCuS0iMyLmt6zz3Mw9izM13EpB8iFLtfbbYymycKTx4RAmPQLwhNGex/AlUIYxXP4R2yyaa4W6mEtc6aME2QuzJFxPgP3HJ9NBx/LWVn4skxWjZ7zg+VRQRHnjyVaSLu3Z5gN5ITWCyE32qaHJa6WBahZj5jWhRyAG1bQ+xKJa8lBL5AgMBAAECggEAUwv9SjJ0PSwbhNuM2w23kcWquROWhYtTA91zGY4esehqB/IFgb2mpIh8Gje5OKqwIu/8jpd4SiOlRYdUF8sD0DfUYRZGdj2AkFNX6tBz8tVfo6wvbB6naA1lzzBij1L5JO3qsjS3cJFkb+kg2yP66AC2Z+0tpfk8eRhdtshAZwfcd1DEGt1uAvYL1eaUK9HRvpt9lPeGcHERDl2hBd4uyaF0K1O+zF9y59nYbTySWPxRZq3sFEE85xRMlstD7YZi7W2gKvMFRD4/FKmrZ3m7aKJRITtyKOyyPcYmepNv3Qv7kk59Pg38n2WWQ0Ra/bCH3E48YNCnQvZMpitkTfJhoQKBgQDbnROOYTP8OTJ6f/qhoGjxeO3x1VOaOp8l0x7b0SCfoqNGS0Cyiqj72BmJtPMPqSTjn6MmNzqbg1KOdhXyzNozs+i5ccW1M56j96mr5I/Z0FpE3oyIHNfDDBlf9M8YQqEF9oYxniYYft9oapO7cRQkHER6qpvnHTavwlv4m78CXwKBgQDHAjs2YlpKDdI1lcbZJCc7TwtH+Pd2bUki8YXafWNcPhITQHbOZjr310eK1QJC6GJncjkOqbX7yv3ivvTO35FZTQhuA1xEG1P00FG8bE0tHYPIwQHi9y0eA5cieMdo8E6XYria1mw/3fqSQEsfZyJlR32JQIoGAipM8iO1X2nZpwKBgDkMFIhnt5lNQk+P7wsNIDWZtDWdtJnboHuy29E+Abt2A/O+mI/IdRz2hau/1WO8DFkUnszOi+rZshhPlGP90rCbi1igtTrcrdjp/KkqNjPea5R4OwkgdOu1uOG0NheXNzzVTQaWjk7Opjn5dWa7eP/oV+GFb/oZHJuLYVizHGsBAoGADA7rjZEKDYCm4w5PPSr+oY5ZjaPdQrS+gLqHtMRyN82fBMGcMUdqfUfzEstzVqCEDeaS5HuOBlK3bXzKkppjUTjksN3NQmcxgBz7RuJ9DqXCLXDcb2cwuafYCYOt+YLOEEgwDVm+t2P44dG5e46hO+fICH/7nP+WlpD5buz4GfMCgYB57r3g/6hi9WUDnfc7ZAzWMqR0EhJVYKYy+KFEtdIPzhkkIHq5RASe88E9kzoGoZFdb3tIjvGZWcHerirrqWkMsuQtP/Qi0zjieid5tAPj+r4kbiCVTw0E0jnmPBzGInQi7lpeTTKnG1fbyS5lBS+WmHfIuzpECgCkxhaT+LJJkg==\n-----END PRIVATE KEY-----"
# RSA 公钥加密实现
def rsa_encrypt(self, text):
try:
key = RSA.import_key(self.publicKey_str)
cipher = PKCS1_v1_5.new(key)
cipher_text = cipher.encrypt(text.encode('utf-8'))
return b64encode(cipher_text).decode('utf-8')
except Exception as e:
print(f"RSA加密失败: {e}")
return ""
# RSA 私钥解密实现
def rsa_decrypt(self, text):
try:
key = RSA.import_key(self.privateKey_str)
cipher = PKCS1_v1_5.new(key)
raw_bytes = b64decode(text.encode('utf-8'))
decrypted = b""
offset = 0
while offset < len(raw_bytes):
chunk = raw_bytes[offset:offset + 256]
decrypted += cipher.decrypt(chunk, None)
offset += 256
return decrypted.decode('utf-8')
except Exception as e:
print(f"RSA解密失败: {e}")
return ""
def homeContent(self, filter):
data = self.post(f"{self.host}/api/v1/app/screen/screenType", headers=self.headers).json()
result = {}
cate = {
"类型": "type",
"地区": "area",
"年份": "year"
}
sort = {
'key': 'sort',
'name': '排序',
'value': [{'n': '最新', 'v': 'NEWEST'}, {'n': '热门', 'v': 'HOT'}, {'n': '收藏', 'v': 'COLLECT'}]
}
classes = []
filters = {}
for k in data.get('data', []):
classes.append({
'type_name': k['name'],
'type_id': str(k['id'])
})
filters[str(k['id'])] = []
for v in k.get('children', []):
if v['name'] in cate:
filters[str(k['id'])].append({
'name': v['name'],
'key': cate[v['name']],
'value': [{'n': i['name'], 'v': i['name']} for i in v.get('children', [])]
})
filters[str(k['id'])].append(sort)
result['class'] = classes
result['filters'] = filters
return result
def homeVideoContent(self):
jdata = {
"condition": {
"sreecnTypeEnum": "NEWEST"
},
"pageNum": 1,
"pageSize": 40
}
data = self.post(f"{self.host}/api/v1/app/screen/screenMovie", headers=self.headers, json=jdata).json()
return {'list': self.getlist(data.get('data', {}).get('records', []))}
def categoryContent(self, tid, pg, filter, extend):
# 保持最纯粹的条件字段,移除任何空字符串占位
condition = {
'sreecnTypeEnum': 'NEWEST',
'typeId': int(tid) if str(tid).isdigit() else tid
}
if extend:
if 'sort' in extend:
condition['sreecnTypeEnum'] = extend.pop('sort')
condition.update(extend)
jdata = {
'condition': condition,
'pageNum': int(pg),
'pageSize': 40,
}
try:
data = self.post(f"{self.host}/api/v1/app/screen/screenMovie", headers=self.headers, json=jdata).json()
result = {}
if data and data.get('data') and 'records' in data['data']:
result['list'] = self.getlist(data['data']['records'])
else:
result['list'] = []
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 40
result['total'] = 999999
return result
except Exception as e:
print(f"分类获取错误: {e}")
return {'list': [], 'page': pg}
def detailContent(self, ids):
ids = ids[0].split('@@')
jdata = {"id": int(ids[0]), "typeId": ids[-1]}
v = self.post(f"{self.host}/api/v1/app/play/movieDesc", headers=self.headers, json=jdata).json()
v = v.get('data', {})
vod = {
'type_name': v.get('typeId', ''),
'vod_year': v.get('year', ''),
'vod_area': v.get('area', ''),
'vod_actor': v.get('star', ''),
'vod_director': v.get('director', ''),
'vod_content': v.get('introduce', ''),
'vod_play_from': '',
'vod_play_url': ''
}
play_params = {
"id": int(ids[0]),
"source": 0,
"typeId": ids[-1]
}
encrypt_payload = {"key": self.rsa_encrypt(json.dumps(play_params))}
c_res = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=encrypt_payload).json()
decrypted_play_str = self.rsa_decrypt(c_res.get('data', ''))
if not decrypted_play_str:
return {'list': [vod]}
decrypted_play_data = json.loads(decrypted_play_str)
l = decrypted_play_data.get('moviePlayerList', [])
if not l:
return {'list': [vod]}
n = {str(i['id']): i['moviePlayerName'] for i in l}
m = play_params.copy()
m.update({'playerId': l[0]['id']})
first_source_payload = {"key": self.rsa_encrypt(json.dumps(m))}
first_res = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=first_source_payload).json()
decrypted_first_str = self.rsa_decrypt(first_res.get('data', ''))
if decrypted_first_str:
decrypted_first_episode = json.loads(decrypted_first_str)
pd = self.getv(m, decrypted_first_episode.get('episodeList', []))
else:
pd = {}
if len(l) > 1:
with ThreadPoolExecutor(max_workers=len(l)-1) as executor:
future_to_player = {executor.submit(self.getd, play_params, player): player for player in l[1:]}
for future in future_to_player:
try:
o, p = future.result()
if p:
pd.update(self.getv(o, p))
except Exception as e:
print(f"多线路请求失败: {e}")
w, e = [], []
for i, x in pd.items():
if x:
w.append(n.get(i, '未知线路'))
e.append(x)
vod['vod_play_from'] = '$$$'.join(w)
vod['vod_play_url'] = '$$$'.join(e)
return {'list': [vod]}
def searchContent(self, key, quick, pg="1"):
jdata = {
"condition": {
"value": str(key)
},
"pageNum": int(pg),
"pageSize": 40
}
try:
data = self.post(f"{self.host}/api/v1/app/search/searchMovie", headers=self.headers, json=jdata).json()
return {'list': self.getlist(data.get('data', {}).get('records', [])), 'page': pg}
except Exception as e:
print(f"搜索请求失败: {e}")
return {'list': [], 'page': pg}
def playerContent(self, flag, id, vipFlags):
raw_id_str = self.d64(id)
if not raw_id_str:
return {'parse': 0, 'url': ''}
jdata = json.loads(raw_id_str)
encrypt_payload = {"key": self.rsa_encrypt(json.dumps(jdata))}
data = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=encrypt_payload).json()
try:
decrypted_url_data = json.loads(self.rsa_decrypt(data.get('data', '')))
playerUrl = decrypted_url_data.get('url', '')
if not playerUrl:
return {'parse': 0, 'url': ''}
params = {'playerUrl': playerUrl, 'playerId': jdata['playerId']}
pd = self.fetch(f"{self.host}/api/v1/app/play/analysisMovieUrl", headers=self.headers, params=params).json()
url, p = pd.get('data', ''), 0
except Exception as e:
print(f"解析流媒体直链失败: {e}")
url, p = "", 0
return {'parse': p, 'url': url, 'header': {'User-Agent': 'okhttp/4.12.0'}, 'danmaku': 'http://127.0.0.1:9978/proxy?do=diydanmu'}
def localProxy(self, param):
pass
def liveContent(self, url):
pass
def gettk(self):
self.headers.update({'deviceId': self.getdid()})
try:
data = self.fetch(f"{self.host}/api/v1/app/user/visitorInfo", headers=self.headers).json()
return data.get('data', {}).get('token', '')
except:
return ""
def getdid(self):
did = self.getCache('ldid')
if not did:
hex_chars = '0123456789abcdef'
did = ''.join(random.choice(hex_chars) for _ in range(16))
self.setCache('ldid', did)
return did
def getd(self, jdata, player):
x = jdata.copy()
x.update({'playerId': player['id']})
encrypt_payload = {"key": self.rsa_encrypt(json.dumps(x))}
response = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=encrypt_payload).json()
decrypted_str = self.rsa_decrypt(response.get('data', ''))
if decrypted_str:
decrypted_episode = json.loads(decrypted_str)
return x, decrypted_episode.get('episodeList', [])
return x, []
def getv(self, d, c):
f = {str(d['playerId']): ''}
g = []
for i in c:
j = d.copy()
j.update({'episodeId': i['id']})
g.append(f"{i['episode']}${self.e64(json.dumps(j))}")
f[str(d['playerId'])] = '#'.join(g)
return f
def getlist(self, data):
videos = []
for i in data:
if not i.get('id'):
continue
videos.append({
'vod_id': f"{i['id']}@@{i.get('typeId', '')}",
'vod_name': i.get('name', ''),
'vod_pic': i.get('cover', ''),
'vod_year': i.get('year', ''),
'vod_remarks': i.get('totalEpisode', '')
})
return videos
def e64(self, text):
try:
return b64encode(text.encode('utf-8')).decode('utf-8')
except:
return ""
def d64(self, encoded_text):
try:
return b64decode(encoded_text.encode('utf-8')).decode('utf-8')
except:
return ""
+343
View File
@@ -0,0 +1,343 @@
# coding = utf-8
# !/usr/bin/python
"""
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from bs4 import BeautifulSoup
from base64 import b64decode
import urllib.request
import urllib.parse
import binascii
import requests
import base64
import json
import time
import sys
import re
import os
sys.path.append('..')
xurl = "https://app.whjzjx.cn"
headers = {
'User-Agent': 'Linux; Android 12; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Mobile Safari/537.36'
}
headerf = {
"platform": "1",
"user_agent": "Mozilla/5.0 (Linux; Android 9; V1938T Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Safari/537.36",
"content-type": "application/json; charset=utf-8"
}
times = int(time.time() * 1000)
data = {
"device": "2a50580e69d38388c94c93605241fb306",
"package_name": "com.jz.xydj",
"android_id": "ec1280db12795506",
"install_first_open": True,
"first_install_time": 1752505243345,
"last_update_time": 1752505243345,
"report_link_url": "",
"authorization": "",
"timestamp": times
}
plain_text = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
key = "B@ecf920Od8A4df7"
key_bytes = key.encode('utf-8')
plain_bytes = plain_text.encode('utf-8')
cipher = AES.new(key_bytes, AES.MODE_ECB)
padded_data = pad(plain_bytes, AES.block_size)
ciphertext = cipher.encrypt(padded_data)
encrypted = base64.b64encode(ciphertext).decode('utf-8')
response = requests.post("https://u.shytkjgs.com/user/v3/account/login", headers=headerf, data=encrypted)
response_data = response.json()
Authorization = response_data['data']['token']
headerx = {
'authorization': Authorization,
'platform': '1',
'version_name': '3.8.3.1'
}
class Spider(Spider):
global xurl
global headerx
global headers
def getName(self):
return "首页"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
if pl == 3:
plx = []
while True:
start_index = text.find(start_str)
if start_index == -1:
break
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
break
middle_text = text[start_index + len(start_str):end_index]
plx.append(middle_text)
text = text.replace(start_str + middle_text + end_str, '')
if len(plx) > 0:
purl = ''
for i in range(len(plx)):
matches = re.findall(start_index1, plx[i])
output = ""
for match in matches:
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
if match3:
number = match3.group(1)
else:
number = 0
if 'http' not in match[0]:
output += f"#{match[1]}${number}{xurl}{match[0]}"
else:
output += f"#{match[1]}${number}{match[0]}"
output = output[1:]
purl = purl + output + "$$$"
purl = purl[:-3]
return purl
else:
return ""
else:
start_index = text.find(start_str)
if start_index == -1:
return ""
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
return ""
if pl == 0:
middle_text = text[start_index + len(start_str):end_index]
return middle_text.replace("\\", "")
if pl == 1:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
jg = ' '.join(matches)
return jg
if pl == 2:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
new_list = [f'{item}' for item in matches]
jg = '$$$'.join(new_list)
return jg
def homeContent(self, filter):
result = {}
result = {"class": [{"type_id": "1", "type_name": "剧场"},
{"type_id": "3", "type_name": "新剧"},
{"type_id": "2", "type_name": "热播"},
{"type_id": "7", "type_name": "星选"},
{"type_id": "5", "type_name": "阳光"}],
}
return result
def homeVideoContent(self):
videos = []
url= f'{xurl}/v1/theater/home_page?theater_class_id=1&class2_id=4&page_num=1&page_size=24'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
if detail.status_code == 200:
data = detail.json()
for vod in data['data']['list']:
name = vod['theater']['title']
id = vod['theater']['id']
pic = vod['theater']['cover_url']
remark = vod['theater']['play_amount_str']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
result = {'list': videos}
return result
def categoryContent(self, cid, pg, filter, ext):
result = {}
videos = []
url = f'{xurl}/v1/theater/home_page?theater_class_id={cid}&page_num={pg}&page_size=24'
detail = requests.get(url=url,headers=headerx)
detail.encoding = "utf-8"
if detail.status_code == 200:
data = detail.json()
for vod in data['data']['list']:
name = vod['theater']['title']
id = vod['theater']['id']
pic = vod['theater']['cover_url']
remark = vod['theater']['theme']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
did = ids[0]
result = {}
videos = []
xianlu = ''
bofang = ''
url = f'{xurl}/v2/theater_parent/detail?theater_parent_id={did}'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
if detail.status_code == 200:
data = detail.json()
url = 'https://fs-im-kefu.7moor-fs1.com/ly/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1732707176882/jiduo.txt'
response = requests.get(url)
response.encoding = 'utf-8'
code = response.text
name = self.extract_middle_text(code, "s1='", "'", 0)
Jumps = self.extract_middle_text(code, "s2='", "'", 0)
content = '剧情:' + data['data']['introduction']
area = data['data']['desc_tags'][0]
remarks = data['data']['filing']
# 修复剧集只有一集的问题 - 检查theaters数据是否存在且不为空
if 'theaters' in data['data'] and data['data']['theaters']:
for sou in data['data']['theaters']:
id = sou['son_video_url']
name = sou['num']
bofang = bofang + str(name) + '$' + id + '#'
bofang = bofang[:-1] if bofang.endswith('#') else bofang
xianlu = '星芽'
else:
# 如果没有theaters数据,检查是否有单个视频URL
if 'video_url' in data['data'] and data['data']['video_url']:
bofang = '1$' + data['data']['video_url']
xianlu = '星芽'
else:
bofang = Jumps
xianlu = '1'
videos.append({
"vod_id": did,
"vod_content": content,
"vod_remarks": remarks,
"vod_area": area,
"vod_play_from": xianlu,
"vod_play_url": bofang
})
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = id
result["header"] = headers
return result
def searchContentPage(self, key, quick, page):
result = {}
videos = []
payload = {
"text": key
}
url = f"{xurl}/v3/search"
detail = requests.post(url=url, headers=headerx, json=payload)
if detail.status_code == 200:
detail.encoding = "utf-8"
data = detail.json()
for vod in data['data']['theater']['search_data']:
name = vod['title']
id = vod['id']
pic = vod['cover_url']
remark = vod['score_str']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
result['list'] = videos
result['page'] = page
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+9 -9
View File
@@ -1,5 +1,5 @@
{
"spider": "./spider.jar;md5;dc774ec05ddb2be146f3fee027d5b51a",
"spider": "./spider.jar;md5;20b61ea1e8772fd2f8e013c5c2fab86f",
"wallpaper": "http://127.0.0.1:9978/proxy?do=wallpaper",
"danmaku": "http://127.0.0.1:9978/proxy?do=danmaku&name={name}&episode={episode}",
"sites": [
@@ -9,6 +9,7 @@
{"key": "预告","name": "新片 • 预告","type": 3,"api": "csp_YGP","searchable": 0},
{"key": "热播影视","name": "热播 • APP","type": 3,"api": "csp_AppRJ","searchable": 1,"quickSearch": 1,"filterable": 0,"ext": {"url": "http://v.rbotv.cn"}},
{"key": "三秋影视","name": "三秋 • APP","type": 3,"api": "csp_App3Q"},
{"key": "马猴影视","name": "马猴 • APP","type": 3,"api": "csp_AppMH"},
{"key": "韩圈","name": "韩圈 • APP","type": 3,"api": "csp_Hxq","ext": "http://www.小不点.com/api/2026/fishhxq.php"},
{"key": "无极99","name": "无极 • APP","type": 3,"api": "csp_App99","ext": {"host": "http://111.170.140.70:19987/app/bn","LoginPath": "/app/log","version": "","appkey": "c403317c2a2946659b0bcbabdd5de43a","versionName": "1.0.1","name": "无极影视","package": "com.wujiys.app","buildNumber": "2001","buildSignature": "A0859BDC35AD3E81D4051E2A4678C77C62773EDB3423DDF1DEFF94CB331D398E"}},
{"key": "橙子99","name": "橙子 • APP","type": 3,"api": "csp_App99","ext": {"host": "http://113.31.180.218:19987/app/bn","LoginPath": "/app/log","appkey": "6e8f3764a0644f3686ab5cdd57bcdf48","versionName": "4.3.1","name": "小橙子","package": "com.xchengzix.app","buildNumber": "2001","buildSignature": "99DA983CF878CEC016616175464A91A94775E20A489F07F76FCDEA1A0F2B6B45"}},
@@ -20,8 +21,6 @@
{"key": "华谊","name": "华谊 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "华谊","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp9Ek4wIlQAtwFnuBRlsFiow2tr+4UOciGeNKbY7nL74etUqUb6fvpOSOHhFEfaWlfwUpOB17x3JEL3No19nfjCeVYrYPjlJcgoqUWH/tfIfFAQWvtxBIBlKazkhw8d3ChysWmeWRikKqkBsVRY4oqNPuj4sjm6Zult0U4I4prRQIDAQAB","dataKey": "NDBYSZR1DMRRZ05NSUCWEJNIYWLBPT0=","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.muyue.tool","host": "","site": "https://vip.123pan.cn/1851089669/oss/az5.txt","version": "1.0.0.4","decrypt": "1"}},
{"key": "苹果","name": "苹果 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "小苹果","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCduNEnfxGaLuQRk5ABzXHhPV43zi00sCHjLo8BYc+Wi6xXm2b4v0i28Sq4WlNCKhseft9fz8kO/qLr6/022o1RcuOU7e4GFL3U9WnNODwRBYSYWd+K8nqpI/tAUDmZEBGRWqjrc7x6aMl3A+xpnWkLbPCLsuhbuuUE3tv09oeOpwIDAQAB","dataKey": "S0VNDTJHOFHHCNMRAW5IV2TOS2PBPQ==","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.juechufsh.android.xpg1","host": "","site": "https://xpgandroid03-1372779881.cos.ap-beijing.myqcloud.com/app_xpg_android.txt","version": "1.0.0.3","decrypt": "0"}},
{"key": "薯条","name": "薯条 • APP","type": 3,"api": "csp_AppDrama","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": {"appName": "薯条影视","publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCduNEnfxGaLuQRk5ABzXHhPV43zi00sCHjLo8BYc+Wi6xXm2b4v0i28Sq4WlNCKhseft9fz8kO/qLr6/022o1RcuOU7e4GFL3U9WnNODwRBYSYWd+K8nqpI/tAUDmZEBGRWqjrc7x6aMl3A+xpnWkLbPCLsuhbuuUE3tv09oeOpwIDAQAB","dataKey": "A1VACZJWDKRZY1P3MFV0DDRAZ3F3PT0=","dataIv": "OC1A06E197EF10CF3F6058CA7A803B5E","pkg": "com.st.standroid","host": "","site": "https://dyttandroid-1372779881.cos.ap-guangzhou.myqcloud.com/app_dyttandroid.txt","version": "5.0.0.1","decrypt": "0"}},
{"key": "久久","name": "久久 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "http://99cms.jj99.icu:8199/api.php/app/"},
{"key": "闪影","name": "闪影 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "http://38.47.213.61:41271/mogai_api.php/v1.vod"},
{"key": "飞飞","name": "飞飞 • APP","type": 3,"api": "csp_AppYsV2","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "https://qpcj.69mini.com/api.php/app/"},
{"key": "金牌","name": "金牌 • APP","type": 3,"api": "csp_AppQi","searchable": 1,"changeable": 1,"ext": {"url": "https://qj4.catbb.xyz","dataKey": "eecbio48dsq13kkk","dataIv": "eecbio48dsq13kkk","search": "searchList4","init": "initV122","ua": "okhttp/3.10.0"}},
{"key": "蓝鹰","name": "蓝鹰 • APP","type": 3,"api": "csp_AppQi","searchable": 1,"changeable": 1,"ext": {"site": "https://xinlanyinghb.oss-cn-wuhan-lr.aliyuncs.com/xly.txt","dataKey": "ca94b06ca359d80e","dataIv": "ca94b06ca359d80e","init": "initV120","ua": "okhttp/3.10.0"}},
@@ -50,19 +49,20 @@
{"key": "笒鬼短剧","name": "笒鬼 • 短剧","type": 3,"api": "csp_CenguiDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "好看短剧","name": "好看 • 短剧","type": 3,"api": "csp_HaokanDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "围观短剧","name": "围观 • 短剧","type": 3,"api": "csp_WeiguanDJ","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "星芽短剧","name": "星芽 • 短剧","type": 3,"api": "./py/星芽短剧.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "星芽短剧","name": "星芽 • 短剧","type": 3,"api": "./py/XYDJ.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1,"genre": "shortdrama"},
{"key": "山楂影视","name": "山楂 • 影视","type": 3,"api": "./py/SZYS.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1},
{"key": "瓜子影视","name": "瓜子 • 影视","type": 3,"api": "./py/GZYS.py","searchable": 1,"changeable": 1,"quickSearch": 1,"filterable": 1},
{"key": "独播影视","name": "独播 • 影视","type": 3,"api": "csp_Duboku"},
{"key": "厂长影视","name": "厂长 • 影视","type": 3,"api": "csp_Czsapp","searchable": 1,"quickSearch": 1,"filterable": 1,"ext": "https://cz01.vip"},
{"key": "金牌影视","name": "金牌 • 影视","type": 3,"api": "csp_Jpys","ext": "https://y2s52n7.com,https://m.hkybqufgh.com,https://m.sizhengxt.com,https://m.9zhoukj.com,https://m.sizhengxt.com,https://m.jiabaide.cn"},
{"key": "修罗影视","name": "修罗 • 影视","api": "csp_Xlys","type": 3,"ext": "https://www.xlysw.ggff.net"},
{"key": "瓜子影视","name": "瓜子 • 影视","type": 3,"api": "csp_Gz360"},
{"key": "云播影视","name": "云播 • 影视","type": 3,"api": "csp_Tvyb"},
{"key": "骚火影视","name": "骚火 • 影视","type": 3,"api": "csp_SaoHuo","ext": "https://shdy5.us"},
{"key": "农民影视","name": "农民 • 影视","type": 3,"api": "csp_Wwys","ext": "https://vip.wwgz.cn:5200"},
{"key": "爱看机器人","name": "爱看 • 影视","type": 3,"api": "csp_Ikanbot","searchable": 1,"quickSearch": 1,"filterable": 1},
{"key": "小镇影视","name": "小镇 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/XZYS.json"},
{"key": "面包影视","name": "面包 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/MBYS.json"},
{"key": "永乐影视","name": "永乐 • 影视","type": 3,"api": "csp_XBPQ","ext": "./json/YLYS.json"},
{"key": "枫叶影视","name": "枫叶 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "手机","简介": "text cor3\">[*],&&</div>","图片": "data-src=\"&&\"[替换:amp;>>空]","主页url": "https://www.cd-zj.com/label/qq.html","副标题": "ft2\">&&</i>","线路数组": "<i class=\"fa ds-dianying\">&&</span>","播放数组": "anthology-list-play size\">&&</ul>","分类url": "https://www.cd-zj.com/cupfox-list/{cateId}-{area}--{class}-----{catePg}---{year}.html","分类": "电影$1#电视剧$2#综艺$3#动漫$4#短剧$5"}},
{"key": "小镇影视","name": "小镇 • 影视","type": 3,"api": "csp_XBPQ","ext": {"线路数组": "<a data-toggle=\"tab\"&&/a>","线路标题": ">&&<","分类url": "http://dyxz.tv/list/{cateId}_{catePg}.html","分类": "电影$1#电视剧$2#动漫$3#综艺$4"}},
{"key": "面包影视","name": "面包 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "User-Agent$MOBILE_UA","编码": "UTF-8","分类url": "https://v.aiwule.com/vodshow/{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html","分类": "电影$20#电视剧$21#动漫$23#综艺$22#短剧$47","简介": "简介:&&"}},
{"key": "永乐影视","name": "永乐 • 影视","type": 3,"api": "csp_XBPQ","ext": {"请求头": "User-Agent$MOBILE_UA","编码": "UTF-8","分类": "电影$1#电视剧$2#综艺$3#动漫$4","类型": "动作片$6#喜剧片$7#爱情片$8#科幻片$9#奇幻片$10#恐怖片$11#剧情片$12#战争片$20#动画片$26#悬疑片$22#冒险片$23#犯罪片$24#惊悚片$45#歌舞片$46#灾难片$47#网络片$48||国产剧$13#港台剧$14#日剧$15#韩剧$33#欧美剧$16#泰剧$34#新马剧$35#其他剧$25||内地综艺$27#港台综艺$28#日本综艺$29#韩国综艺$36#欧美综艺$30#新马泰综艺$37#其他综艺$38||国产动漫$31#日本动漫$32#韩国动漫$39#港台动漫$40#新马泰动漫$41#欧美动漫$42#其他动漫$43","分类url": "https://www.ylys.tv/vodshow/{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html"}},
{"key": "剧圈影视","name": "剧圈 • 影视","type": 3,"api": "csp_XYQHiker","ext": "./json/JQYS.json"},
{"key": "1905","name": "1905 • 影视","type": 3,"api": "csp_Web1905","searchable": 1,"quickSearch": 0,"filterable": 0},
{"key": "哆啦新番社","name": "哆啦 • 新番社","type": 3,"api": "csp_XBPQ","style": {"type": "list"},"ext": "./json/DLXFS.json"},
+496
View File
@@ -0,0 +1,496 @@
# coding = utf-8
#!/usr/bin/python
import re
import sys
import json
import time
import base64
import hashlib
import random
import string
import urllib.parse
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base.spider import Spider
sys.path.append('..')
class Spider(Spider):
def __init__(self):
self.name = "瓜子"
self.hosts = [
'https://apinew.uozvr.com',
'https://api.w32z7vtd.com',
'https://api.6a7nnf7.com',
'https://api.umygrx3.com',
'https://api.rmedphk.com'
]
self.host_index = 0
self.host = self.hosts[self.host_index]
# AES 固定密钥(与Java版一致)
self.AES_KEY = 'OITxa5OqAYjhswxx'
self.AES_IV = 'rCMNwZASNBKZ8mXV'
# RSA 公钥/私钥
self.RSA_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUM5+/y8sPsWkd1/RQS64X259EUwxFXFE5HlA65MqrxnPs0JqoSRojSDy5QhwvROlaD6TwRQHKMY2OAZ6SnQeUJsChTEFIR9qUkwrs3/MVUMxjsv6JS6Oe/juclyJGTgVmDhB55EafXsD0SQYVj/QXXsxR6ewR5E2kL52yAAD4yQIDAQAB"
self.RSA_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGAe6hKrWLi1zQmjTT1
ozbE4QdFeJGNxubxld6GrFGximxfMsMB6BpJhpcTouAqywAFppiKetUBBbXwYsYU
1wNr648XVmPmCMCy4rY8vdliFnbMUj086DU6Z+/oXBdWU3/b1G0DN3E9wULRSwcK
ZT3wj/cCI1vsCm3gj2R5SqkA9Y0CAwEAAQKBgAJH+4CxV0/zBVcLiBCHvSANm0l7
HetybTh/j2p0Y1sTXro4ALwAaCTUeqdBjWiLSo9lNwDHFyq8zX90+gNxa7c5EqcW
V9FmlVXr8VhfBzcZo1nXeNdXFT7tQ2yah/odtdcx+vRMSGJd1t/5k5bDd9wAvYdI
DblMAg+wiKKZ5KcdAkEA1cCakEN4NexkF5tHPRrR6XOY/XHfkqXxEhMqmNbB9U34
saTJnLWIHC8IXys6Qmzz30TtzCjuOqKRRy+FMM4TdwJBAJQZFPjsGC+RqcG5UvVM
iMPhnwe/bXEehShK86yJK/g/UiKrO87h3aEu5gcJqBygTq3BBBoH2md3pr/W+hUM
WBsCQQChfhTIrdDinKi6lRxrdBnn0Ohjg2cwuqK5zzU9p/N+S9x7Ck8wUI53DKm8
jUJE8WAG7WLj/oCOWEh+ic6NIwTdAkEAj0X8nhx6AXsgCYRql1klbqtVmL8+95KZ
K7PnLWG/IfjQUy3pPGoSaZ7fdquG8bq8oyf5+dzjE/oTXcByS+6XRQJAP/5ciy1b
L3NhUhsaOVy55MHXnPjdcTX0FaLi+ybXZIfIQ2P4rb19mVq1feMbCXhz+L1rG8oa
t5lYKfpe8k83ZA==
-----END RSA PRIVATE KEY-----"""
self.DEVICE_OLD_KEY = "aLFBMWpxBrIDAD1Si/KVvm41"
# 设备信息(随机生成)
self.deviceId = str(864150060000000 + random.randint(0, 9999))
self.deviceKey = ''.join(random.choices('0123456789ABCDEF', k=40)) # 20字节hex大写
self.token = ""
self.token_id = ""
self.registered = False
self.header = {
'User-Agent': 'Lavf/57.83.100',
'code': 'GZ0369',
'deviceId': self.deviceId,
'lang': 'zh_cn',
'Cache-Control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded',
'Version': '2604028',
'PackageName': 'com.ae06aebdbb.y286327f5a.ofe849883320260517',
'Ver': '3.0.3.2',
'api-ver': '3.0.3.2',
'Referer': self.host
}
self.cache = {}
self.cache_timeout = 300
# 初始化token
self.init_token()
def getName(self):
return self.name
def init(self, extend=''):
pass
# ---------- 设备注册与认证 ----------
def init_token(self):
"""初始化token:注册设备 -> 刷新"""
print("===== 初始化设备认证 =====")
try:
if not self.registered:
self.sign_up()
# 刷新获取最终token
self.refresh_token()
except Exception as e:
print(f"初始化token失败: {e}")
# 兜底使用原有硬编码(几乎没用)
self.token = '024212ef0975c5306a1434e113a46463.bc77313e11a248558a6ca244ca980944ec3421fa480c50e0229ad91f1cb15aea582603202cd71796885c9e5163e500f1b72f737059aff1ddb8beea47c5a331d6760540345b7f88b2302a0e6e09589f9dcf3ff9175d8c905f990203f5fc04748008ea7a366571cbf5b09509a873dcfba3cf1d5590385f5f7ef6e01d1850974aa220eb5178c89e61c24411af9b9a19435e.06fde789ece48d9b33c5dc857e04e9b5838f08264d928b87237d3476c4484b46'
def sign_up(self):
"""注册设备"""
print("注册新设备...")
params = {
"new_key": self.deviceKey,
"old_key": self.DEVICE_OLD_KEY,
"phone_type": 1,
"code": ""
}
result = self._auth_request('/App/Authentication/Device/signUp', params)
self._apply_auth(result)
self.registered = True
def sign_in(self):
"""登录设备"""
print("设备登录...")
params = {
"new_key": self.deviceKey,
"old_key": self.DEVICE_OLD_KEY
}
result = self._auth_request('/App/Authentication/Device/signIn', params)
self._apply_auth(result)
def _apply_auth(self, result):
"""从认证响应中提取token"""
new_token = result.get('token', '')
if not new_token:
raise Exception("认证失败,无token返回: {}".format(result))
self.token = new_token
new_token_id = result.get('app_user_id', '')
if new_token_id:
self.token_id = new_token_id
print(f"获取token成功, token前缀: {self.token[:30]}...")
def refresh_token(self):
"""刷新token"""
print("刷新token...")
result = self._auth_request('/App/Authentication/Authenticator/refresh', {})
self._apply_auth(result)
def _auth_request(self, path, params):
"""认证类请求(不需要ensure_token"""
return self._send_encrypted_request(params, path, is_auth=True)
# ---------- 业务请求核心(修复加密与签名) ----------
def ensure_token(self):
"""确保token有效,如未就绪则重新获取"""
if not self.token or not self.token_id:
if self.registered:
self.sign_in()
else:
self.sign_up()
self.refresh_token()
def _send_encrypted_request(self, data, path, is_auth=False):
"""
发送加密请求,返回解密后的字典
:param data: 业务参数字典
:param path: 请求路径
:param is_auth: 是否为认证类请求(signUp/signIn/refresh),此时不使用ensure_token
"""
try:
if not is_auth:
self.ensure_token()
# 1. 将参数转为JSON并AES加密
json_params = json.dumps(data)
encrypted = self.aes_encrypt(json_params, self.AES_KEY, self.AES_IV)
request_key = encrypted.upper() # Java中是bytesToHex(encrypted).toUpperCase()
# 2. 生成keys (RSA加密 iv/key JSON)
key_json = json.dumps({"iv": self.AES_IV, "key": self.AES_KEY})
keys = self.rsa_encrypt(key_json, self.RSA_PUBLIC_KEY)
# 3. 生成签名
t = str(int(time.time()))
sign_str = f"token_id=,token={self.token},phone_type=1,request_key={request_key},app_id=1,time={t},keys={keys}*&zvdvdvddbfikkkumtmdwqppp?|4Y!s!2br"
signature = self.get_md5(sign_str) # 已改为大写
# 4. 构建请求体
body = {
'token': self.token,
'token_id': '',
'phone_type': '1',
'time': t,
'phone_model': 'xiaomi-25031', # 与Java版保持一致
'keys': keys,
'request_key': request_key,
'signature': signature,
'app_id': '1',
'ad_version': '1'
}
# 5. 发送请求
url = f"{self.host}{path}"
response = self.post(url, headers=self.header, data=body, timeout=10)
if response.status_code != 200:
raise Exception(f"HTTP {response.status_code}")
resp_json = response.json()
# 检查业务code(若不为200可能token过期)
if 'code' in resp_json and resp_json['code'] != 200:
print(f"业务错误码: {resp_json['code']}, 信息: {resp_json}")
# 如果不是认证请求,尝试重新获取token后重试一次(这里简单处理,外层get_data已有重试)
raise Exception("业务错误")
data_section = resp_json.get('data')
if not data_section:
raise Exception("响应缺少data字段")
encrypted_response = data_section.get('response_key', '')
encrypted_keys = data_section.get('keys', '')
# 6. 解密响应
decrypted_keys_json = self.rsa_decrypt(encrypted_keys, self.RSA_PRIVATE_KEY)
key_info = json.loads(decrypted_keys_json)
resp_key = key_info['key']
resp_iv = key_info['iv']
decrypted_data = self.aes_decrypt(encrypted_response, resp_key, resp_iv)
return json.loads(decrypted_data)
except Exception as e:
print(f"请求失败 [{path}]: {e}")
return None
def get_data(self, data, path, use_cache=True):
"""带重试和域名轮询的数据获取(保持原框架)"""
try:
cache_key = f"{path}_{hash(str(data))}" if use_cache else None
if use_cache and cache_key in self.cache:
cached_data, timestamp = self.cache[cache_key]
if time.time() - timestamp < self.cache_timeout:
return cached_data
for attempt in range(3):
tried = 0
while tried < len(self.hosts):
self.host = self.hosts[self.host_index]
self.header['Referer'] = self.host
result = self._send_encrypted_request(data, path)
if result is not None:
print(f"请求成功: {path}, 域名: {self.host}")
if use_cache and cache_key:
self.cache[cache_key] = (result, time.time())
return result
# 切换到下一个域名
self.host_index = (self.host_index + 1) % len(self.hosts)
tried += 1
# 所有域名失败,尝试重新认证并重试
if attempt < 2:
print("所有域名失败,尝试重新认证...")
try:
self.ensure_token()
except:
pass
self.host_index = 0
else:
break
return None
except Exception as e:
print(f"get_data异常: {e}")
return None
# ---------- 加解密工具 ----------
def aes_encrypt(self, text, key, iv):
try:
key_bytes = key.encode('utf-8')
iv_bytes = iv.encode('utf-8')
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
encrypted = cipher.encrypt(pad(text.encode('utf-8'), AES.block_size))
return encrypted.hex().upper()
except Exception as e:
print(f"AES加密失败: {e}")
return ""
def aes_decrypt(self, text, key, iv):
try:
key_bytes = key.encode('utf-8')
iv_bytes = iv.encode('utf-8')
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
encrypted_bytes = bytes.fromhex(text)
decrypted = unpad(cipher.decrypt(encrypted_bytes), AES.block_size)
return decrypted.decode('utf-8')
except Exception as e:
print(f"AES解密失败: {e}")
return ""
def rsa_encrypt(self, text, public_key_str):
"""RSA公钥加密(PKCS1v1.5"""
try:
key = RSA.import_key("-----BEGIN PUBLIC KEY-----\n" + public_key_str + "\n-----END PUBLIC KEY-----")
cipher = PKCS1_v1_5.new(key)
encrypted = cipher.encrypt(text.encode('utf-8'))
return base64.b64encode(encrypted).decode('utf-8')
except Exception as e:
print(f"RSA加密失败: {e}")
return ""
def rsa_decrypt(self, encrypted_data, private_key_str):
"""RSA私钥解密"""
try:
encrypted_bytes = base64.b64decode(encrypted_data)
rsa_key = RSA.import_key(private_key_str)
cipher = PKCS1_v1_5.new(rsa_key)
decrypted = cipher.decrypt(encrypted_bytes, None)
return decrypted.decode('utf-8') if decrypted else ""
except Exception as e:
print(f"RSA解密失败: {e}")
return ""
def get_md5(self, text):
return hashlib.md5(text.encode()).hexdigest().upper() # 与Java一致大写
# ---------- 业务方法(不变) ----------
def homeContent(self, filter):
result = {}
classes = [
{"type_name": "电影", "type_id": "1"},
{"type_name": "电视剧", "type_id": "2"},
{"type_name": "动漫", "type_id": "4"},
{"type_name": "综艺", "type_id": "3"},
{"type_name": "短剧", "type_id": "64"}
]
result['class'] = classes
filters = {}
for cate in classes:
tid = cate['type_id']
filters[tid] = [
{"key": "area", "name": "地区", "value": [
{"n": "全部", "v": "0"}, {"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": "0"}, {"n": "2025", "v": "2025"}, {"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": "更早", "v": "2004"}
]},
{"key": "sort", "name": "排序", "value": [
{"n": "最新", "v": "d_id"}, {"n": "最热", "v": "d_hits"}, {"n": "推荐", "v": "d_score"}
]}
]
result['filters'] = filters
return result
def homeVideoContent(self):
return {'list': []}
def categoryContent(self, tid, pg, filter, extend):
videos = []
try:
body = {
"area": extend.get('area', '0'),
"year": extend.get('year', '0'),
"pageSize": "30",
"sort": extend.get('sort', 'd_id'),
"page": str(pg),
"tid": tid
}
cache_key = f"category_{tid}_{pg}_{hash(str(body))}"
data = self.get_cached_data(cache_key, body, '/App/IndexList/indexList')
if data and 'list' in data:
for item in data['list']:
vod_continu = item.get('vod_continu', 0)
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}'
video = {
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
"vod_name": item.get('vod_name', ''),
"vod_pic": item.get('vod_pic', ''),
"vod_remarks": remarks
}
videos.append(video)
except Exception as e:
print(f"获取分类内容失败: {e}")
return {'list': videos, 'page': int(pg), 'pagecount': 9999, 'limit': 30, 'total': 999999}
def detailContent(self, ids):
try:
vod_id = ids[0].split('/')[0]
t = str(int(time.time()))
body1 = {"token_id": self.token_id, "vod_id": vod_id, "mobile_time": t, "token": self.token}
qdata = self.get_data(body1, '/App/IndexPlay/playInfo')
body2 = {"vurl_cloud_id": "2", "vod_d_id": vod_id}
jdata = self.get_data(body2, '/App/Resource/Vurl/show')
if not qdata or 'vodInfo' not in qdata:
return {'list': []}
vod = qdata['vodInfo']
video_detail = {
"vod_id": vod_id,
"vod_name": vod.get('vod_name', ''),
"vod_pic": vod.get('vod_pic', ''),
"vod_year": vod.get('vod_year', ''),
"vod_area": vod.get('vod_area', ''),
"vod_actor": vod.get('vod_actor', ''),
"vod_director": vod.get('vod_director', ''),
"vod_content": vod.get('vod_use_content', '').strip(),
"vod_play_from": "瓜子影视"
}
play_list = []
if jdata and 'list' in jdata:
for index, item in enumerate(jdata['list']):
if 'play' in item:
n, p = [], []
for key, value in item['play'].items():
if 'param' in value and value['param']:
n.append(key)
p.append(value['param'])
if p:
play_name = str(index + 1) if len(jdata['list']) != 1 else vod.get('vod_name', '')
play_url = f"{p[-1]}||{'@'.join(n)}"
play_list.append(f"{play_name}${play_url}")
video_detail["vod_play_url"] = "#".join(play_list)
return {'list': [video_detail]}
except Exception as e:
print(f"获取详情失败: {e}")
return {'list': []}
def searchContent(self, key, quick, pg=1):
videos = []
try:
body = {"keywords": key, "order_val": "1", "page": str(pg)}
data = self.get_data(body, '/App/Index/findMoreVod', use_cache=False)
if data and 'list' in data:
for item in data['list']:
vod_continu = item.get('vod_continu', 0)
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}'
videos.append({
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
"vod_name": item.get('vod_name', ''),
"vod_pic": item.get('vod_pic', ''),
"vod_remarks": remarks
})
except Exception as e:
print(f"搜索失败: {e}")
return {'list': videos, 'page': int(pg), 'pagecount': 9999, 'limit': 30, 'total': 999999}
def playerContent(self, flag, id, vipFlags):
try:
parts = id.split('||')
if len(parts) < 2:
return {"parse": 0, "playUrl": "", "url": ""}
param_str = parts[0]
resolutions = parts[1].split('@') if len(parts) > 1 else []
params = {}
for pair in param_str.split('&'):
if '=' in pair:
key, value = pair.split('=', 1)
params[key] = value
if resolutions:
resolutions.sort(key=lambda x: int(x) if x.isdigit() else 0, reverse=True)
params['resolution'] = resolutions[0]
data = self.get_data(params, '/App/Resource/VurlDetail/showOne', use_cache=False)
if data and 'url' in data:
return {"parse": 0, "playUrl": "", "url": data['url'],
"header": json.dumps({"User-Agent": "Lavf/57.83.100", "Referer": "http://WJiZxLXA2.com/"}), 'danmaku': 'http://127.0.0.1:9978/proxy?do=diydanmu'}
return {"parse": 0, "playUrl": "", "url": ""}
except Exception as e:
print(f"播放解析失败: {e}")
return {"parse": 0, "playUrl": "", "url": ""}
def isVideoFormat(self, url):
video_formats = ['.m3u8', '.mp4', '.avi', '.mkv', '.flv', '.ts']
return any(url.lower().endswith(fmt) for fmt in video_formats)
def manualVideoCheck(self):
pass
def localProxy(self, params):
return None
def get_cached_data(self, cache_key, data, path):
current_time = time.time()
if cache_key in self.cache:
cached_data, timestamp = self.cache[cache_key]
if current_time - timestamp < self.cache_timeout:
return cached_data
result = self.get_data(data, path)
if result:
self.cache[cache_key] = (result, current_time)
return result
if __name__ == '__main__':
pass
+330
View File
@@ -0,0 +1,330 @@
# -*- coding: utf-8 -*-
# by @嗷呜
import json
import random
import sys
from base64 import b64encode, b64decode
from concurrent.futures import ThreadPoolExecutor
# 引入 RSA 加解密所需模块
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend=""):
did = self.getdid()
self.headers.update({'deviceId': did})
token = self.gettk()
self.headers.update({'token': token})
def getName(self):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def destroy(self):
pass
# 1. 修改为主机域名
host = 'http://qkys.qukanwh.com'
# 2. 同步原脚本的配置请求头
headers = {
'HOST': 'qkys.qukanwh.com',
'User-Agent': 'okhttp/4.12.0',
'client': 'app',
'deviceType': 'Android',
'Referer': ''
}
# 3. 导入原脚本中的 RSA 密钥对与配置
publicKey_str = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoYt0BP77U+DM08BiI/QbSRIfxijXo85BTPqIM1Ow8BNwhLETzRIZ+dEwdWDbydG/PspgBAfRpGaYVdJYtvaC2JnoO8+Ik6qMWojfEJxSFLa0Pb0A892tun4gsxoEMjcreZ+YGyaBxAfqX0BSMfdrOgIYaZQjYrw9TRLlUT31QoQIDAQAB\n-----END PUBLIC KEY-----"
privateKey_str = "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCquQQ5r6+yJI8CDFkXRp8vUsdD45ov8EP12ooLs56ca2DQXaSNGS9910bAPVA9chkp0mKIvKqjAsHz5Tl9EeNPblarGEeJUIxpxZtiSqNTpvtiD/TjhpzuHYic7RAfQ/h7p/ypE8ymU42pYjsB5t26Mv6XgkLV+jzrSf73HlCuS0iMyLmt6zz3Mw9izM13EpB8iFLtfbbYymycKTx4RAmPQLwhNGex/AlUIYxXP4R2yyaa4W6mEtc6aME2QuzJFxPgP3HJ9NBx/LWVn4skxWjZ7zg+VRQRHnjyVaSLu3Z5gN5ITWCyE32qaHJa6WBahZj5jWhRyAG1bQ+xKJa8lBL5AgMBAAECggEAUwv9SjJ0PSwbhNuM2w23kcWquROWhYtTA91zGY4esehqB/IFgb2mpIh8Gje5OKqwIu/8jpd4SiOlRYdUF8sD0DfUYRZGdj2AkFNX6tBz8tVfo6wvbB6naA1lzzBij1L5JO3qsjS3cJFkb+kg2yP66AC2Z+0tpfk8eRhdtshAZwfcd1DEGt1uAvYL1eaUK9HRvpt9lPeGcHERDl2hBd4uyaF0K1O+zF9y59nYbTySWPxRZq3sFEE85xRMlstD7YZi7W2gKvMFRD4/FKmrZ3m7aKJRITtyKOyyPcYmepNv3Qv7kk59Pg38n2WWQ0Ra/bCH3E48YNCnQvZMpitkTfJhoQKBgQDbnROOYTP8OTJ6f/qhoGjxeO3x1VOaOp8l0x7b0SCfoqNGS0Cyiqj72BmJtPMPqSTjn6MmNzqbg1KOdhXyzNozs+i5ccW1M56j96mr5I/Z0FpE3oyIHNfDDBlf9M8YQqEF9oYxniYYft9oapO7cRQkHER6qpvnHTavwlv4m78CXwKBgQDHAjs2YlpKDdI1lcbZJCc7TwtH+Pd2bUki8YXafWNcPhITQHbOZjr310eK1QJC6GJncjkOqbX7yv3ivvTO35FZTQhuA1xEG1P00FG8bE0tHYPIwQHi9y0eA5cieMdo8E6XYria1mw/3fqSQEsfZyJlR32JQIoGAipM8iO1X2nZpwKBgDkMFIhnt5lNQk+P7wsNIDWZtDWdtJnboHuy29E+Abt2A/O+mI/IdRz2hau/1WO8DFkUnszOi+rZshhPlGP90rCbi1igtTrcrdjp/KkqNjPea5R4OwkgdOu1uOG0NheXNzzVTQaWjk7Opjn5dWa7eP/oV+GFb/oZHJuLYVizHGsBAoGADA7rjZEKDYCm4w5PPSr+oY5ZjaPdQrS+gLqHtMRyN82fBMGcMUdqfUfzEstzVqCEDeaS5HuOBlK3bXzKkppjUTjksN3NQmcxgBz7RuJ9DqXCLXDcb2cwuafYCYOt+YLOEEgwDVm+t2P44dG5e46hO+fICH/7nP+WlpD5buz4GfMCgYB57r3g/6hi9WUDnfc7ZAzWMqR0EhJVYKYy+KFEtdIPzhkkIHq5RASe88E9kzoGoZFdb3tIjvGZWcHerirrqWkMsuQtP/Qi0zjieid5tAPj+r4kbiCVTw0E0jnmPBzGInQi7lpeTTKnG1fbyS5lBS+WmHfIuzpECgCkxhaT+LJJkg==\n-----END PRIVATE KEY-----"
# RSA 公钥加密实现
def rsa_encrypt(self, text):
try:
key = RSA.import_key(self.publicKey_str)
cipher = PKCS1_v1_5.new(key)
cipher_text = cipher.encrypt(text.encode('utf-8'))
return b64encode(cipher_text).decode('utf-8')
except Exception as e:
print(f"RSA加密失败: {e}")
return ""
# RSA 私钥解密实现
def rsa_decrypt(self, text):
try:
key = RSA.import_key(self.privateKey_str)
cipher = PKCS1_v1_5.new(key)
raw_bytes = b64decode(text.encode('utf-8'))
decrypted = b""
offset = 0
while offset < len(raw_bytes):
chunk = raw_bytes[offset:offset + 256]
decrypted += cipher.decrypt(chunk, None)
offset += 256
return decrypted.decode('utf-8')
except Exception as e:
print(f"RSA解密失败: {e}")
return ""
def homeContent(self, filter):
data = self.post(f"{self.host}/api/v1/app/screen/screenType", headers=self.headers).json()
result = {}
cate = {
"类型": "type",
"地区": "area",
"年份": "year"
}
sort = {
'key': 'sort',
'name': '排序',
'value': [{'n': '最新', 'v': 'NEWEST'}, {'n': '热门', 'v': 'HOT'}, {'n': '收藏', 'v': 'COLLECT'}]
}
classes = []
filters = {}
for k in data.get('data', []):
classes.append({
'type_name': k['name'],
'type_id': str(k['id'])
})
filters[str(k['id'])] = []
for v in k.get('children', []):
if v['name'] in cate:
filters[str(k['id'])].append({
'name': v['name'],
'key': cate[v['name']],
'value': [{'n': i['name'], 'v': i['name']} for i in v.get('children', [])]
})
filters[str(k['id'])].append(sort)
result['class'] = classes
result['filters'] = filters
return result
def homeVideoContent(self):
jdata = {
"condition": {
"sreecnTypeEnum": "NEWEST"
},
"pageNum": 1,
"pageSize": 40
}
data = self.post(f"{self.host}/api/v1/app/screen/screenMovie", headers=self.headers, json=jdata).json()
return {'list': self.getlist(data.get('data', {}).get('records', []))}
def categoryContent(self, tid, pg, filter, extend):
# 保持最纯粹的条件字段,移除任何空字符串占位
condition = {
'sreecnTypeEnum': 'NEWEST',
'typeId': int(tid) if str(tid).isdigit() else tid
}
if extend:
if 'sort' in extend:
condition['sreecnTypeEnum'] = extend.pop('sort')
condition.update(extend)
jdata = {
'condition': condition,
'pageNum': int(pg),
'pageSize': 40,
}
try:
data = self.post(f"{self.host}/api/v1/app/screen/screenMovie", headers=self.headers, json=jdata).json()
result = {}
if data and data.get('data') and 'records' in data['data']:
result['list'] = self.getlist(data['data']['records'])
else:
result['list'] = []
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 40
result['total'] = 999999
return result
except Exception as e:
print(f"分类获取错误: {e}")
return {'list': [], 'page': pg}
def detailContent(self, ids):
ids = ids[0].split('@@')
jdata = {"id": int(ids[0]), "typeId": ids[-1]}
v = self.post(f"{self.host}/api/v1/app/play/movieDesc", headers=self.headers, json=jdata).json()
v = v.get('data', {})
vod = {
'type_name': v.get('typeId', ''),
'vod_year': v.get('year', ''),
'vod_area': v.get('area', ''),
'vod_actor': v.get('star', ''),
'vod_director': v.get('director', ''),
'vod_content': v.get('introduce', ''),
'vod_play_from': '',
'vod_play_url': ''
}
play_params = {
"id": int(ids[0]),
"source": 0,
"typeId": ids[-1]
}
encrypt_payload = {"key": self.rsa_encrypt(json.dumps(play_params))}
c_res = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=encrypt_payload).json()
decrypted_play_str = self.rsa_decrypt(c_res.get('data', ''))
if not decrypted_play_str:
return {'list': [vod]}
decrypted_play_data = json.loads(decrypted_play_str)
l = decrypted_play_data.get('moviePlayerList', [])
if not l:
return {'list': [vod]}
n = {str(i['id']): i['moviePlayerName'] for i in l}
m = play_params.copy()
m.update({'playerId': l[0]['id']})
first_source_payload = {"key": self.rsa_encrypt(json.dumps(m))}
first_res = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=first_source_payload).json()
decrypted_first_str = self.rsa_decrypt(first_res.get('data', ''))
if decrypted_first_str:
decrypted_first_episode = json.loads(decrypted_first_str)
pd = self.getv(m, decrypted_first_episode.get('episodeList', []))
else:
pd = {}
if len(l) > 1:
with ThreadPoolExecutor(max_workers=len(l)-1) as executor:
future_to_player = {executor.submit(self.getd, play_params, player): player for player in l[1:]}
for future in future_to_player:
try:
o, p = future.result()
if p:
pd.update(self.getv(o, p))
except Exception as e:
print(f"多线路请求失败: {e}")
w, e = [], []
for i, x in pd.items():
if x:
w.append(n.get(i, '未知线路'))
e.append(x)
vod['vod_play_from'] = '$$$'.join(w)
vod['vod_play_url'] = '$$$'.join(e)
return {'list': [vod]}
def searchContent(self, key, quick, pg="1"):
jdata = {
"condition": {
"value": str(key)
},
"pageNum": int(pg),
"pageSize": 40
}
try:
data = self.post(f"{self.host}/api/v1/app/search/searchMovie", headers=self.headers, json=jdata).json()
return {'list': self.getlist(data.get('data', {}).get('records', [])), 'page': pg}
except Exception as e:
print(f"搜索请求失败: {e}")
return {'list': [], 'page': pg}
def playerContent(self, flag, id, vipFlags):
raw_id_str = self.d64(id)
if not raw_id_str:
return {'parse': 0, 'url': ''}
jdata = json.loads(raw_id_str)
encrypt_payload = {"key": self.rsa_encrypt(json.dumps(jdata))}
data = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=encrypt_payload).json()
try:
decrypted_url_data = json.loads(self.rsa_decrypt(data.get('data', '')))
playerUrl = decrypted_url_data.get('url', '')
if not playerUrl:
return {'parse': 0, 'url': ''}
params = {'playerUrl': playerUrl, 'playerId': jdata['playerId']}
pd = self.fetch(f"{self.host}/api/v1/app/play/analysisMovieUrl", headers=self.headers, params=params).json()
url, p = pd.get('data', ''), 0
except Exception as e:
print(f"解析流媒体直链失败: {e}")
url, p = "", 0
return {'parse': p, 'url': url, 'header': {'User-Agent': 'okhttp/4.12.0'}, 'danmaku': 'http://127.0.0.1:9978/proxy?do=diydanmu'}
def localProxy(self, param):
pass
def liveContent(self, url):
pass
def gettk(self):
self.headers.update({'deviceId': self.getdid()})
try:
data = self.fetch(f"{self.host}/api/v1/app/user/visitorInfo", headers=self.headers).json()
return data.get('data', {}).get('token', '')
except:
return ""
def getdid(self):
did = self.getCache('ldid')
if not did:
hex_chars = '0123456789abcdef'
did = ''.join(random.choice(hex_chars) for _ in range(16))
self.setCache('ldid', did)
return did
def getd(self, jdata, player):
x = jdata.copy()
x.update({'playerId': player['id']})
encrypt_payload = {"key": self.rsa_encrypt(json.dumps(x))}
response = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=encrypt_payload).json()
decrypted_str = self.rsa_decrypt(response.get('data', ''))
if decrypted_str:
decrypted_episode = json.loads(decrypted_str)
return x, decrypted_episode.get('episodeList', [])
return x, []
def getv(self, d, c):
f = {str(d['playerId']): ''}
g = []
for i in c:
j = d.copy()
j.update({'episodeId': i['id']})
g.append(f"{i['episode']}${self.e64(json.dumps(j))}")
f[str(d['playerId'])] = '#'.join(g)
return f
def getlist(self, data):
videos = []
for i in data:
if not i.get('id'):
continue
videos.append({
'vod_id': f"{i['id']}@@{i.get('typeId', '')}",
'vod_name': i.get('name', ''),
'vod_pic': i.get('cover', ''),
'vod_year': i.get('year', ''),
'vod_remarks': i.get('totalEpisode', '')
})
return videos
def e64(self, text):
try:
return b64encode(text.encode('utf-8')).decode('utf-8')
except:
return ""
def d64(self, encoded_text):
try:
return b64decode(encoded_text.encode('utf-8')).decode('utf-8')
except:
return ""
+343
View File
@@ -0,0 +1,343 @@
# coding = utf-8
# !/usr/bin/python
"""
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from bs4 import BeautifulSoup
from base64 import b64decode
import urllib.request
import urllib.parse
import binascii
import requests
import base64
import json
import time
import sys
import re
import os
sys.path.append('..')
xurl = "https://app.whjzjx.cn"
headers = {
'User-Agent': 'Linux; Android 12; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Mobile Safari/537.36'
}
headerf = {
"platform": "1",
"user_agent": "Mozilla/5.0 (Linux; Android 9; V1938T Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Safari/537.36",
"content-type": "application/json; charset=utf-8"
}
times = int(time.time() * 1000)
data = {
"device": "2a50580e69d38388c94c93605241fb306",
"package_name": "com.jz.xydj",
"android_id": "ec1280db12795506",
"install_first_open": True,
"first_install_time": 1752505243345,
"last_update_time": 1752505243345,
"report_link_url": "",
"authorization": "",
"timestamp": times
}
plain_text = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
key = "B@ecf920Od8A4df7"
key_bytes = key.encode('utf-8')
plain_bytes = plain_text.encode('utf-8')
cipher = AES.new(key_bytes, AES.MODE_ECB)
padded_data = pad(plain_bytes, AES.block_size)
ciphertext = cipher.encrypt(padded_data)
encrypted = base64.b64encode(ciphertext).decode('utf-8')
response = requests.post("https://u.shytkjgs.com/user/v3/account/login", headers=headerf, data=encrypted)
response_data = response.json()
Authorization = response_data['data']['token']
headerx = {
'authorization': Authorization,
'platform': '1',
'version_name': '3.8.3.1'
}
class Spider(Spider):
global xurl
global headerx
global headers
def getName(self):
return "首页"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
if pl == 3:
plx = []
while True:
start_index = text.find(start_str)
if start_index == -1:
break
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
break
middle_text = text[start_index + len(start_str):end_index]
plx.append(middle_text)
text = text.replace(start_str + middle_text + end_str, '')
if len(plx) > 0:
purl = ''
for i in range(len(plx)):
matches = re.findall(start_index1, plx[i])
output = ""
for match in matches:
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
if match3:
number = match3.group(1)
else:
number = 0
if 'http' not in match[0]:
output += f"#{match[1]}${number}{xurl}{match[0]}"
else:
output += f"#{match[1]}${number}{match[0]}"
output = output[1:]
purl = purl + output + "$$$"
purl = purl[:-3]
return purl
else:
return ""
else:
start_index = text.find(start_str)
if start_index == -1:
return ""
end_index = text.find(end_str, start_index + len(start_str))
if end_index == -1:
return ""
if pl == 0:
middle_text = text[start_index + len(start_str):end_index]
return middle_text.replace("\\", "")
if pl == 1:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
jg = ' '.join(matches)
return jg
if pl == 2:
middle_text = text[start_index + len(start_str):end_index]
matches = re.findall(start_index1, middle_text)
if matches:
new_list = [f'{item}' for item in matches]
jg = '$$$'.join(new_list)
return jg
def homeContent(self, filter):
result = {}
result = {"class": [{"type_id": "1", "type_name": "剧场"},
{"type_id": "3", "type_name": "新剧"},
{"type_id": "2", "type_name": "热播"},
{"type_id": "7", "type_name": "星选"},
{"type_id": "5", "type_name": "阳光"}],
}
return result
def homeVideoContent(self):
videos = []
url= f'{xurl}/v1/theater/home_page?theater_class_id=1&class2_id=4&page_num=1&page_size=24'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
if detail.status_code == 200:
data = detail.json()
for vod in data['data']['list']:
name = vod['theater']['title']
id = vod['theater']['id']
pic = vod['theater']['cover_url']
remark = vod['theater']['play_amount_str']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
result = {'list': videos}
return result
def categoryContent(self, cid, pg, filter, ext):
result = {}
videos = []
url = f'{xurl}/v1/theater/home_page?theater_class_id={cid}&page_num={pg}&page_size=24'
detail = requests.get(url=url,headers=headerx)
detail.encoding = "utf-8"
if detail.status_code == 200:
data = detail.json()
for vod in data['data']['list']:
name = vod['theater']['title']
id = vod['theater']['id']
pic = vod['theater']['cover_url']
remark = vod['theater']['theme']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
did = ids[0]
result = {}
videos = []
xianlu = ''
bofang = ''
url = f'{xurl}/v2/theater_parent/detail?theater_parent_id={did}'
detail = requests.get(url=url, headers=headerx)
detail.encoding = "utf-8"
if detail.status_code == 200:
data = detail.json()
url = 'https://fs-im-kefu.7moor-fs1.com/ly/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1732707176882/jiduo.txt'
response = requests.get(url)
response.encoding = 'utf-8'
code = response.text
name = self.extract_middle_text(code, "s1='", "'", 0)
Jumps = self.extract_middle_text(code, "s2='", "'", 0)
content = '剧情:' + data['data']['introduction']
area = data['data']['desc_tags'][0]
remarks = data['data']['filing']
# 修复剧集只有一集的问题 - 检查theaters数据是否存在且不为空
if 'theaters' in data['data'] and data['data']['theaters']:
for sou in data['data']['theaters']:
id = sou['son_video_url']
name = sou['num']
bofang = bofang + str(name) + '$' + id + '#'
bofang = bofang[:-1] if bofang.endswith('#') else bofang
xianlu = '星芽'
else:
# 如果没有theaters数据,检查是否有单个视频URL
if 'video_url' in data['data'] and data['data']['video_url']:
bofang = '1$' + data['data']['video_url']
xianlu = '星芽'
else:
bofang = Jumps
xianlu = '1'
videos.append({
"vod_id": did,
"vod_content": content,
"vod_remarks": remarks,
"vod_area": area,
"vod_play_from": xianlu,
"vod_play_url": bofang
})
result['list'] = videos
return result
def playerContent(self, flag, id, vipFlags):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = id
result["header"] = headers
return result
def searchContentPage(self, key, quick, page):
result = {}
videos = []
payload = {
"text": key
}
url = f"{xurl}/v3/search"
detail = requests.post(url=url, headers=headerx, json=payload)
if detail.status_code == 200:
detail.encoding = "utf-8"
data = detail.json()
for vod in data['data']['theater']['search_data']:
name = vod['title']
id = vod['id']
pic = vod['cover_url']
remark = vod['score_str']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
}
videos.append(video)
result['list'] = videos
result['page'] = page
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
BIN
View File
Binary file not shown.