diff --git a/CatVodTVSpider/README.md b/CatVodTVSpider/README.md new file mode 100644 index 0000000..91c89c9 --- /dev/null +++ b/CatVodTVSpider/README.md @@ -0,0 +1,261 @@ +# 猫影视TV客户端爬虫自定义接口工程 + +``` +思维没有边界 一切皆有可能 +``` +:rocket:[**TG交流群**](https://t.me/catvodtv_offical) + +本工程最终食用需配合 猫影视TV新版(一下简称为软件) **v2.0.0**及以上版本。 + +![logo](app/src/main/res/drawable-xhdpi/app_icon.png) + +### 欢迎各路大佬踊跃提PR,分享爬虫代码。 + +### 这里是用户分享的爬虫代码打包的共享包,可以配合自定义配置,直接食用 [custom_spider.jar](/jar/) + +## 快速开始 +---- +本工程是一个完整的AndroidStudio工程,请你用AS打开编辑。 + +工程调试完毕后要需要导出生成jar文件配合软件使用,执行根目录下的 `buildAndGenJar.bat` 会在`jar`目录生成一个名为`custom_spider.jar`的jar文件,这个文件就是我们最终要是用的代码包。 + +### 代码包食用方式 +---- +本地加载:将`custom_spider.jar`放入设备sd卡根目录即可。 **注意,如需本地加载,请手动赋予App存储空间读写权限,App默认不申请存储空间读写权限** + +远程加载:将`custom_spider.jar`上传到你的网络空间,获取对应的文件下载地址,在软件自定义配置的json文件中加入下面格式的键值对。 +```json +"spider": "http://xxx.xxx.xxx/custom_spider.jar" +``` +支持jar文件本地缓存(需v2.0.5及以上版本) +```json +"spider": "http://xxx.xxx.xxx/custom_spider.jar;md5;jar文件md5" +// 例如 +"spider": "https://github.com/catvod/CatVodTVSpider/blob/master/jar/custom_spider.jar?raw=true;md5;c6ed6bc8285f0aca90e7cb3abf7f9caa", +``` + +### 如何在自定义配置中调用我们代码包中的Spider +---- +同样在自定义json中加入相应的播放源即可,**type=3, api对应你代码工程中自定义的爬虫类名(api必须是`csp_`开头),例如实例工程中的`Aidi`** +```json +{ + "key": "csp_Aidi", + "name": "爱迪", + "type": 3, + "api": "csp_Aidi", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 +} +``` +--- +**Json解析扩展(需v2.0.2及以上版本)** + +通过jar包可以实现json解析并发、轮询等相关功能,**参与并发和轮询的json解析地址,默认为解析地址列表中的所有json解析(即type=1)**。 + +在自定义json中的`parse`里加入相应的解析配置(type=2)即可启用。**调用扩展类的名称配置在`parse`的`url`字段里,例如扩展类`JsonParallel`的json配置`url`字段值为`Parallel`**。如下: + +```json +{ + "name": "Json并发", + "type": 2, + "url": "Parallel" +}, +{ + "name": "Json轮询", + "type": 2, + "url": "Sequence" +} +``` + +**XPath规则套娃(需v2.0.4及以上版本)** +套娃规则祥见[XPath.md](/XPath.md) + +--- + +**M浏览器中APP影视规则支持**(2022.01.17 by 小黄瓜)支持筛选。 + +因App影视接口收集地址停止维护,代码里不再读取相关json地址。 + +如果你有对应影视Api地址请在ext字段中直接配置Api地址。 + +
+配置代码示例 点我展开 + +```json +{"key":"csp_appysv2_追剧达人", "name":"追剧推荐(M2)", "type":3,"api":"csp_AppYsV2", "searchable":1, "quickSearch":0, "filterable":1, "ext":"https://vipmv.co/xgapp.php/v1/"}, +{"key":"csp_appysv2_天空影视", "name":"天空推荐(M2)", "type":3,"api":"csp_AppYsV2", "searchable":1, "quickSearch":0, "filterable":1, "ext":"https://tkys.tv/xgapp.php/v1/"}, +{"key":"csp_appys_HG影视", "name":"HG影视(M)", "type":3, "api":"csp_AppYs", "searchable":1, "quickSearch":0, "filterable":1, "ext":"http://hgyx.vip/api.php/v1.vod"}, +{"key":"csp_appys_瑞丰资源", "name":"瑞丰资源(M)", "type":3, "api":"csp_AppYs", "searchable":1, "quickSearch":0, "filterable":1, "ext":"https://ts.yjhan.com:4433/ruifenglb_api.php/v1.vod"}, +``` +
+ +--- + +**部分内置代理接口使用(需v2.0.9及以上版本)** + +入口 [Proxy](/app/src/main/java/com/github/catvod/spider/Proxy.java) + +图片代理,示例参考[Nekk](/app/src/main/java/com/github/catvod/spider/Nekk.java#L449) + +直播配置重定向代理,示例参考[TxtSubscribe](/app/src/main/java/com/github/catvod/live/TxtSubscribe.java) + +--- + +**推送功能扩展(需v2.1.0.Beta6及以上版本)** + +入口 [PushAgent](/app/src/main/java/com/github/catvod/spider/PushAgent.java) + +需要在配置文件中,加入key为`push_agent`的站点,数据返回格式和普通爬虫一致 + +```json +{ + "key": "push_agent", + "name": "推送", + "type": 3, + "api": "csp_PushAgent", + "searchable": 0, + "quickSearch": 0, + "filterable": 0 +} +``` +--- + +## 基础类 +---- + +> com.github.catvod.crawler.Spider 爬虫基类 + +> com.github.catvod.crawler.SpiderReq 用于发起网络请求 获取网络数据,请使用[OkHttpUtil](/app/src/main/java/com/github/catvod/utils/okhttp/OkHttpUtil.java)类,SpiderReq后续将删除 + +> com.github.catvod.crawler.SpiderReqResult 网络请求结果 + +> com.github.catvod.crawler.SpiderUrl 用于定义一个网络请求 + + +## 示例 +---- + +请查看 仓库中的爱迪影视 相关实现 ,调试可参考 `com.github.catvod.demo.MainActivity` ,直接调用对应爬虫相关接口 + +> com.github.catvod.spider.Aidi + +## 注意事项!! +---- +1. 除了`com.github.catvod.spider`包以外的代码,最终都会被软件本身内置的代码代替掉,所以,建议你不要修改除`com.github.catvod.spider`包以外的代码。 + +2. 不要在`Spider`中使用`Gson` + +3. 待补充 + +## 爬虫类返回的相关Json字符串格式说明 +---- +### homeContent +```json +{ + "class": [{ // 分类 + "type_id": "dianying", // 分类id + "type_name": "电影" // 分类名 + }, { + "type_id": "lianxuju", + "type_name": "连续剧" + }], + "filters": { // 筛选 + "dianying": [{ // 分类id 就是上面class中的分类id + "key": "0", // 筛选key + "name": "分类", // 筛选名称 + "value": [{ // 筛选选项 + "n": "全部", // 选项展示的名称 + "v": "dianying" // 选项最终在url中的展现 + }, { + "n": "动作片", + "v": "dongzuopian" + }] + }], + "lianxuju": [{ + "key": 0, + "name": "分类", + "value": [{ + "n": "全部", + "v": "lianxuju" + }, { + "n": "国产剧", + "v": "guochanju" + }, { + "n": "港台剧", + "v": "gangtaiju" + }] + }] + }, + "list": [{ // 首页最近更新视频列表 + "vod_id": "1901", // 视频id + "vod_name": "判决", // 视频名 + "vod_pic": "https:\/\/pic.imgdb.cn\/item\/614631e62ab3f51d918e9201.jpg", // 展示图片 + "vod_remarks": "6.8" // 视频信息 展示在 视频名上方 + }, { + "vod_id": "1908", + "vod_name": "移山的父亲", + "vod_pic": "https:\/\/pic.imgdb.cn\/item\/6146fab82ab3f51d91c01af1.jpg", + "vod_remarks": "6.7" + }] +} +``` + +### categoryContent +```json +{ + "page": 1, // 当前页 + "pagecount": 2, // 总共几页 + "limit": 60, // 每页几条数据 + "total": 120, // 总共多少调数据 + "list": [{ // 视频列表 下面的视频结构 同上面homeContent中的 + "vod_id": "1897", + "vod_name": "北区侦缉队", + "vod_pic": "https:\/\/pic.imgdb.cn\/item\/6145d4b22ab3f51d91bd98b6.jpg", + "vod_remarks": "7.3" + }, { + "vod_id": "1879", + "vod_name": "浪客剑心 最终章 人诛篇", + "vod_pic": "https:\/\/pic.imgdb.cn\/item\/60e3f37e5132923bf82ef95e.jpg", + "vod_remarks": "8.0" + }] +} +``` + +### detailContent +```json +{ + "list": [{ + "vod_id": "1902", + "vod_name": "海岸村恰恰恰", + "vod_pic": "https:\/\/pic.imgdb.cn\/item\/61463fd12ab3f51d91a0f44d.jpg", + "type_name": "剧情", + "vod_year": "2021", + "vod_area": "韩国", + "vod_remarks": "更新至第8集", + "vod_actor": "申敏儿,金宣虎,李相二,孔敏晶,徐尚沅,禹美华,朴艺荣,李世亨,边胜泰,金贤佑,金英玉", + "vod_director": "柳济元", + "vod_content": "海岸村恰恰恰剧情:  韩剧海岸村恰恰恰 갯마을 차차차改编自2004年的电影《我的百事通男友洪班长》,海岸村恰恰恰 갯마을 차차차讲述来自大都市的牙医(申敏儿 饰)到充满人情味的海岸村开设牙医诊所,那里住着一位各方面都", + // 播放源 多个用$$$分隔 + "vod_play_from": "qiepian$$$yun3edu", + // 播放列表 注意分隔符 分别是 多个源$$$分隔,源中的剧集用#分隔,剧集的名称和地址用$分隔 + "vod_play_url": "第1集$1902-1-1#第2集$1902-1-2#第3集$1902-1-3#第4集$1902-1-4#第5集$1902-1-5#第6集$1902-1-6#第7集$1902-1-7#第8集$1902-1-8$$$第1集$1902-2-1#第2集$1902-2-2#第3集$1902-2-3#第4集$1902-2-4#第5集$1902-2-5#第6集$1902-2-6#第7集$1902-2-7#第8集$1902-2-8" + }] +} +``` + +### searchContent +```json +{ + "list": [{ // 视频列表 下面的视频结构 同上面homeContent中的 + "vod_id": "1606", + "vod_name": "陪你一起长大", + "vod_pic": "https:\/\/img.aidi.tv\/img\/upload\/vod\/20210417-1\/e27d4eb86f7cde375171dd324b2c19ae.jpg", + "vod_remarks": "更新至第37集" + }] +} +``` + + + + diff --git a/CatVodTVSpider/XPath.md b/CatVodTVSpider/XPath.md new file mode 100644 index 0000000..a5a8b80 --- /dev/null +++ b/CatVodTVSpider/XPath.md @@ -0,0 +1,340 @@ +### XPath规则套娃(需v2.0.4及以上版本) +---- +套娃依赖自定义爬虫jar,同样需要在自定义json中加入相应的配置,**type=3, api为csp_XPath**,套娃相关规则配置在`ext`字段中,注意:ext字段值**只能是字符串** + +为控制配置文件容量,同时支持在ext字段中直接配置规则和拉取规则的网址。 2021.10.21 by 小黄瓜 + +```json + { + "key": "csp_xpath_94sm", + "name": "94神马(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/94sm.json" + }, + { + "key": "csp_xpath_jpys", + "name": "极品影视(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/jpys.json" + }, + { + "key": "csp_xpath_age", + "name": "AGE动漫(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/agefans.json" + }, + { + "key": "csp_xpath_jpyszl", + "name": "极品直链(XPath)", + "type": 3, + "api": "csp_XPathMac", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/jpys.json" + }, + { + "key": "csp_xpath_cjt", + "name": "CJT影视(XPath)", + "type": 3, + "api": "csp_XPathMac", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/cjtys.json" + }, + { + "key": "csp_xpath_tvci", + "name": "大师兄(XPath)", + "type": 3, + "api": "csp_XPathFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/tvci.json" + }, + { + "key": "csp_xpath_saohuotv", + "name": "骚火电影(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/saohuotv2.json" + }, + { + "key": "csp_xpath_dm84", + "name": "动漫巴士(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/dm84.json" + }, + { + "key": "csp_xpath_egg", + "name": "蛋蛋影院(XPath)", + "type": 3, + "api": "csp_XPathEgg", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/egg.json" + }, + { + "key": "csp_xpath_555", + "name": "555电影(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/555.json" + }, + { + "key": "csp_xpath_4kyu", + "name": "一只鱼4K(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/4kyu.json" + }, + { + "key": "csp_xpath_miniku", + "name": "Miniku(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/miniku.json" + }, + { + "key": "csp_xpath_pianba", + "name": "Pianba(XPath)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/pianba.json" + }, + { + "key": "csp_xpath_Ole", + "name": "Ole(EXO)", + "type": 3, + "api": "csp_XPath", + "searchable": 1, + "quickSearch": 1, + "filterable": 0, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/olevod.json" + }, + { + "key": "唐人街", + "name": "唐人街(P)", + "type": 3, + "api": "csp_XPathMacFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/Renjie.json" + }, + { + "key": "Gimy", + "name": "Gimy(P)", + "type": 3, + "api": "csp_XPathMacFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/Gimy.json" + }, + { + "key": "Jumi", + "name": "Jumi(P)", + "type": 3, + "api": "csp_XPathMacFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/Jumi.json" + }, + { + "key": "Djx", + "name": "瓜皮TV(P)", + "type": 3, + "api": "csp_XPathFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/Djx.json" + }, + { + "key": "独播库", + "name": "独播库(P)", + "type": 3, + "api": "csp_XPathMacFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/Duboku.json" + }, + { + "key": "Sky4k", + "name": "Sky4k(P)[en]", + "type": 3, + "api": "csp_XPathMacFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/Sky4k.json" + }, + { + "key": "Nfuxs", + "name": "南府(P)", + "type": 3, + "api": "csp_XPathFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/Nfuxs.json" + }, + { + "key": "Aixixi", + "name": "爱西西(P)", + "type": 3, + "api": "csp_XPathFilter", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": "https://cdn.jsdelivr.net/gh/catvod/CatVodTVSpider@master/xpath/Aixixi.json" + } + +``` + +### 套娃规则 + +demo配置写的比较细,不一定所有字段都要有,具体还是自己多试一试 + +```json +{ + "ua": "", + // 首页地址 用于获取 分类和首页推荐 + "homeUrl": "http://www.9rmb.com", + // 分类节点 + "cateNode": "//ul[contains(@class,'navbar-nav')]/li/a[contains(@href, '.html') and not(contains(@href, '6'))]", + // 分类名 + "cateName": "/text()", + // 分类id + "cateId": "/@href", + // 分类id二次处理正则 + "cateIdR": "/type/(\\d+).html", + // 手动设置分类,如果手动设置了分类则不使用上面的分类xpath获取分类 例如 "cateManual": {"电影": "1", "电视剧": "2"}, + "cateManual": {}, + // 首页推荐视频的节点 + "homeVodNode": "//div[@class='col-md-12 movie-item-out']//a[not(contains(@href, '6'))]/parent::*/parent::*/parent::*/div[contains(@class, 'movie-item-out') and position()<10]/div[@class='movie-item']/a", + // 首页推荐视频的名称 + "homeVodName": "/@title", + // 二次处理正则 + "homeVodNameR": "", + // 首页推荐视频的id + "homeVodId": "/@href", + // 二次处理正则 + "homeVodIdR": "/show/(\\w+).html", + // 首页推荐视频的图片 + "homeVodImg": "/img/@src", + // 二次处理正则 + "homeVodImgR": "", + // 首页推荐视频的简介 + "homeVodMark": "/button/text()", + // 二次处理正则 + "homeVodMarkR": "", + // 分类页地址 {cateId} 分类id {catePg} 当前页 + "cateUrl": "http://www.9rmb.com/type/{cateId}/{catePg}.html", + // 同上面的homeVod字段 分类列表中的视频信息 + "cateVodNode": "//div[@class='movie-item']/a", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/show/(\\w+).html", + "cateVodImg": "/img/@src", + "cateVodMark": "/button/text()", + // 详情页地址 用于获取详情页信息 及 播放列表和地址 + "dtUrl": "http://www.9rmb.com/show/{vid}.html", + // 详情节点 + "dtNode": "//div[@class='container-fluid']", + // 视频名 + "dtName": "//div[@class='col-md-9']//div[@class='col-md-4']//img/@alt", + "dtNameR": "", + // 视频图片 + "dtImg": "//div[@class='col-md-9']//div[@class='col-md-4']//img/@src", + "dtImgR": "", + // 视频分类 + "dtCate": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '类型')]/parent::*/following-sibling::*/text()", + "dtCateR": "", + // 视频年份 + "dtYear": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '日期')]/parent::*/following-sibling::*/text()", + "dtYearR": "", + // 视频地区 + "dtArea": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '国家')]/parent::*/following-sibling::*/text()", + "dtAreaR": "", + // 视频状态 + "dtMark": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '状态')]/parent::*/following-sibling::*/text()", + "dtMarkR": "", + // 主演 + "dtActor": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '主演')]/parent::*/following-sibling::*/text()", + "dtActorR": "", + // 导演 + "dtDirector": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '导演')]/parent::*/following-sibling::*/text()", + "dtDirectorR": "", + // 视频简介 + "dtDesc": "//p[@class='summary']/text()", + "dtDescR": "", + // 播放源节点 + "dtFromNode": "//div[contains(@class,'resource-list')]/div[@class='panel-heading']/strong", + // 播放源名称 + "dtFromName": "/text()", + // 二次处理正则 + "dtFromNameR": "\\S+\\.(\\S+) \\(", + // 播放列表节点 + "dtUrlNode": "//div[contains(@class,'resource-list')]/ul[@class='dslist-group']", + // 播放地址节点 + "dtUrlSubNode": "/li/a", + // 播放地址 + "dtUrlId": "@href", + // 二次处理正则 + "dtUrlIdR": "/play/(\\S+).html", + // 剧集名称 + "dtUrlName": "/text()", + // 二次处理正则 + "dtUrlNameR": "", + // 播放页面的地址 {playUrl} 对应上面 dtUrlId 获取到的地址 + "playUrl": "http://www.9rmb.com/play/{playUrl}.html", + // 解析webview的user-agent + "playUa": "", + // 搜索地址 + "searchUrl": "http://www.9rmb.com/search?wd={wd}", + // 同上面的homeVod字段 搜索结果中的视频信息, 这里有对苹果cms suggest搜索接口的特殊支持,参考示例中的极品影视 + "scVodNode": "//div[@class='movie-item']/a", + "scVodName": "/@title", + "scVodId": "/@href", + "scVodIdR": "/show/(\\w+).html", + "scVodImg": "/img/@src", + "scVodMark": "/button/text()" +} +``` diff --git a/CatVodTVSpider/app/build.gradle b/CatVodTVSpider/app/build.gradle new file mode 100644 index 0000000..9cd7510 --- /dev/null +++ b/CatVodTVSpider/app/build.gradle @@ -0,0 +1,47 @@ +plugins { + id 'com.android.application' + id 'ru.cleverpumpkin.proguard-dictionaries-generator' +} + +android { + compileSdkVersion rootProject.compileSdkVersion + + defaultConfig { + applicationId "com.github.catvod.demo" + minSdkVersion rootProject.miniSdkVersion + targetSdkVersion rootProject.targetSdkVersion + versionCode rootProject.appVersionCode + versionName rootProject.appVersionName + } + + buildTypes { + release { + minifyEnabled true + proguardFiles 'proguard-rules.pro' + proguardDictionaries { + dictionaryNames = [ + "build/class-dictionary", + "build/package-dictionary", + "build/obfuscation-dictionary" + ] + minLineLength 1 + maxLineLength 3 + linesCountInDictionary 100000 + } + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation "org.jsoup:jsoup:$jsoup_version" + implementation "cn.wanghaomiao:JsoupXpath:$jsoupxpath_version" + implementation "com.squareup.okhttp3:okhttp:$okhttp_version" + implementation ("com.github.liujingxing.rxhttp:rxhttp:$rxhttp_version") { + exclude group: "com.squareup.okhttp3" + } + annotationProcessor "com.github.liujingxing.rxhttp:rxhttp-compiler:$rxhttp_version" +} \ No newline at end of file diff --git a/CatVodTVSpider/app/proguard-rules.pro b/CatVodTVSpider/app/proguard-rules.pro new file mode 100644 index 0000000..3a69fb5 --- /dev/null +++ b/CatVodTVSpider/app/proguard-rules.pro @@ -0,0 +1,42 @@ +-optimizationpasses 0 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontskipnonpubliclibraryclassmembers +-dontpreverify +-dontoptimize +-verbose +-printmapping proguardMapping.txt +-optimizations !code/simplification/cast,!field/*,!class/merging/* +-keepattributes *Annotation*,InnerClasses +-keepattributes EnclosingMethod, InnerClasses +-keepattributes *Annotation* +-keepattributes Signature +-keepattributes LineNumberTable +-renamesourcefileattribute SourceFile + +-obfuscationdictionary build/obfuscation-dictionary.txt +-classobfuscationdictionary build/class-dictionary.txt +-packageobfuscationdictionary build/package-dictionary.txt + +-flattenpackagehierarchy com.github.catvod.spider.merge +-repackageclasses com.github.catvod.spider.merge + +-dontwarn okio.** +-keep class okio.**{*;} +-dontwarn com.squareup.okhttp.** +-dontwarn okhttp3.** +-keep class okhttp3.**{*;} +-dontwarn com.google.** +-keep class com.google.**{*;} +-dontwarn com.android.** +-keep class com.android.**{*;} +-dontwarn rxhttp.** +-keep class rxhttp.**{*;} +-dontwarn kotlin.** +-keep class kotlin.**{*;} +-dontwarn kotlinx.** +-keep class kotlinx.**{*;} + +-keep class com.github.catvod.crawler.* { *; } +-keep class com.github.catvod.spider.* { public ; } +-keep class com.github.catvod.parser.* { public ; } diff --git a/CatVodTVSpider/app/src/main/AndroidManifest.xml b/CatVodTVSpider/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3620cef --- /dev/null +++ b/CatVodTVSpider/app/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/Spider.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/Spider.java new file mode 100644 index 0000000..9f3bf4a --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/Spider.java @@ -0,0 +1,103 @@ +package com.github.catvod.crawler; + +import android.content.Context; + +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.List; + +public abstract class Spider { + + public static JSONObject empty = new JSONObject(); + + public void init(Context context) { + } + + public void init(Context context, String extend) { + init(context); + } + + /** + * 首页数据内容 + * + * @param filter 是否开启筛选 + * @return + */ + public String homeContent(boolean filter) { + return ""; + } + + /** + * 首页最近更新数据 如果上面的homeContent中不包含首页最近更新视频的数据 可以使用这个接口返回 + * + * @return + */ + public String homeVideoContent() { + return ""; + } + + /** + * 分类数据 + * + * @param tid + * @param pg + * @param filter + * @param extend + * @return + */ + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + return ""; + } + + /** + * 详情数据 + * + * @param ids + * @return + */ + public String detailContent(List ids) { + return ""; + } + + /** + * 搜索数据内容 + * + * @param key + * @param quick + * @return + */ + public String searchContent(String key, boolean quick) { + return ""; + } + + /** + * 播放信息 + * + * @param flag + * @param id + * @return + */ + public String playerContent(String flag, String id, List vipFlags) { + return ""; + } + + /** + * webview解析时使用 可自定义判断当前加载的 url 是否是视频 + * + * @param url + * @return + */ + public boolean isVideoFormat(String url) { + return false; + } + + /** + * 是否手动检测webview中加载的url + * + * @return + */ + public boolean manualVideoCheck() { + return false; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderDebug.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderDebug.java new file mode 100644 index 0000000..fc155a1 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderDebug.java @@ -0,0 +1,19 @@ +package com.github.catvod.crawler; + +public class SpiderDebug { + public static void log(Throwable th) { + try { + android.util.Log.d("SpiderLog", th.getMessage(), th); + } catch (Throwable th1) { + + } + } + + public static void log(String msg) { + try { + android.util.Log.d("SpiderLog", msg); + } catch (Throwable th1) { + + } + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderReq.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderReq.java new file mode 100644 index 0000000..6b5265f --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderReq.java @@ -0,0 +1,299 @@ +package com.github.catvod.crawler; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.Response; +import rxhttp.RxHttpPlugins; +import rxhttp.wrapper.param.RxHttp; +import rxhttp.wrapper.parse.Parser; + +public class SpiderReq { + + private static final String defaultTag = "sp_req_default"; + + private static OkHttpClient defaultClient = initDefaultClient(); + + private static OkHttpClient initDefaultClient() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.readTimeout(15, TimeUnit.SECONDS); + builder.writeTimeout(15, TimeUnit.SECONDS); + builder.connectTimeout(15, TimeUnit.SECONDS); + builder.retryOnConnectionFailure(true); + return builder.build(); + } + + /** + * 取消请求 + * + * @param tag + */ + public static void cancel(String tag) { + try { + RxHttpPlugins.cancelAll(tag); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + + /** + * Get + * + * @param su + * @return + */ + public static SpiderReqResult get(SpiderUrl su) { + return get(defaultClient, su.url, defaultTag, su.headers); + } + + /** + * Get whit custom tag + * + * @param su + * @param tag + * @return + */ + public static SpiderReqResult get(SpiderUrl su, String tag) { + return get(defaultClient, su.url, tag, su.headers); + } + + /** + * Get with custom http client + * + * @param client + * @param su + * @return + */ + public static SpiderReqResult get(OkHttpClient client, SpiderUrl su) { + return get(client, su.url, defaultTag, su.headers); + } + + /** + * Get with custom http client & tag + * + * @param client + * @param su + * @param tag + * @return + */ + + public static SpiderReqResult get(OkHttpClient client, SpiderUrl su, String tag) { + return get(client, su.url, tag, su.headers); + } + + /** + * Get base + * + * @param client + * @param url + * @param tag + * @param headers + * @return + */ + public static SpiderReqResult get(OkHttpClient client, String url, String tag, HashMap headers) { + try { + return RxHttp.get(url) + .setOkClient(client) + .tag(tag) + .addAllHeader(headers) + .execute(new Parser() { + @Override + public SpiderReqResult onParse(@NotNull Response response) throws IOException { + String content = ""; + if (response.body() != null) { + content = response.body().string(); + } + Map> headers = response.headers().toMultimap(); + return new SpiderReqResult(headers, content); + } + }); + } catch (Throwable e) { + SpiderDebug.log(e); + } + return SpiderReqResult.empty; + } + + /** + * Header + * + * @param url + * @param headers + * @return + */ + public static SpiderReqResult header(String url, HashMap headers) { + return header(defaultClient, url, defaultTag, headers); + } + + /** + * Header + * + * @param client + * @param url + * @param tag + * @param headers + * @return + */ + public static SpiderReqResult header(OkHttpClient client, String url, String tag, HashMap headers) { + try { + return RxHttp.head(url) + .setOkClient(client) + .tag(tag) + .addAllHeader(headers) + .execute(new Parser() { + @Override + public SpiderReqResult onParse(@NotNull Response response) throws IOException { + Map> headers = response.headers().toMultimap(); + return new SpiderReqResult(headers, ""); + } + }); + } catch (Throwable e) { + SpiderDebug.log(e); + } + return SpiderReqResult.empty; + } + + /** + * Post Body + * + * @param url + * @param body + * @param headers + * @return + */ + public static SpiderReqResult postBody(String url, RequestBody body, HashMap headers) { + return postBody(defaultClient, url, body, defaultTag, headers); + } + + /** + * Post Body + * + * @param client + * @param url + * @param body + * @param tag + * @param headers + * @return + */ + public static SpiderReqResult postBody(OkHttpClient client, String url, RequestBody body, String tag, HashMap headers) { + try { + return RxHttp.postBody(url) + .setOkClient(client) + .setBody(body) + .tag(tag) + .addAllHeader(headers) + .execute(new Parser() { + @Override + public SpiderReqResult onParse(@NotNull Response response) throws IOException { + String content = ""; + if (response.body() != null) { + content = response.body().string(); + } + Map> headers = response.headers().toMultimap(); + return new SpiderReqResult(headers, content); + } + }); + } catch (Throwable e) { + SpiderDebug.log(e); + } + return SpiderReqResult.empty; + } + + /** + * Post Form + * + * @param url + * @param form + * @param headers + * @return + */ + public static SpiderReqResult postForm(String url, HashMap form, HashMap headers) { + return postForm(defaultClient, url, form, defaultTag, headers); + } + + /** + * Post Form + * + * @param client + * @param url + * @param form + * @param tag + * @param headers + * @return + */ + public static SpiderReqResult postForm(OkHttpClient client, String url, HashMap form, String tag, HashMap headers) { + try { + return RxHttp.postForm(url) + .setOkClient(client) + .addAll(form) + .tag(tag) + .addAllHeader(headers) + .execute(new Parser() { + @Override + public SpiderReqResult onParse(@NotNull Response response) throws IOException { + String content = ""; + if (response.body() != null) { + content = response.body().string(); + } + Map> headers = response.headers().toMultimap(); + return new SpiderReqResult(headers, content); + } + }); + } catch (Throwable e) { + SpiderDebug.log(e); + } + return SpiderReqResult.empty; + } + + /** + * Post Json + * + * @param url + * @param json + * @param headers + * @return + */ + public static SpiderReqResult postJson(String url, HashMap json, HashMap headers) { + return postJson(defaultClient, url, json, defaultTag, headers); + } + + /** + * Post Json + * + * @param client + * @param url + * @param json + * @param tag + * @param headers + * @return + */ + public static SpiderReqResult postJson(OkHttpClient client, String url, HashMap json, String tag, HashMap headers) { + try { + return RxHttp.postJson(url) + .setOkClient(client) + .addAll(json) + .tag(tag) + .addAllHeader(headers) + .execute(new Parser() { + @Override + public SpiderReqResult onParse(@NotNull Response response) throws IOException { + String content = ""; + if (response.body() != null) { + content = response.body().string(); + } + Map> headers = response.headers().toMultimap(); + return new SpiderReqResult(headers, content); + } + }); + } catch (Throwable e) { + SpiderDebug.log(e); + } + return SpiderReqResult.empty; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderReqResult.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderReqResult.java new file mode 100644 index 0000000..808f665 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderReqResult.java @@ -0,0 +1,17 @@ +package com.github.catvod.crawler; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SpiderReqResult { + SpiderReqResult(Map> headers, String content) { + this.headers = headers; + this.content = content; + } + + public static SpiderReqResult empty = new SpiderReqResult(new HashMap>(), ""); + + public Map> headers; + public String content; +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderUrl.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderUrl.java new file mode 100644 index 0000000..7260f92 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/crawler/SpiderUrl.java @@ -0,0 +1,20 @@ +package com.github.catvod.crawler; + +import java.util.HashMap; + +public class SpiderUrl { + + public static SpiderUrl Empty = new SpiderUrl(); + + public String url = ""; + public HashMap headers = new HashMap<>(); + + public SpiderUrl() { + } + + public SpiderUrl(String url, HashMap headers) { + this.url = url; + if (headers != null) + this.headers = headers; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/demo/MainActivity.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/demo/MainActivity.java new file mode 100644 index 0000000..e0f5b5f --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/demo/MainActivity.java @@ -0,0 +1,136 @@ +package com.github.catvod.demo; + +import android.app.Activity; +import android.os.Bundle; + +import com.github.catvod.spider.AppYsV2; +import com.github.catvod.spider.XPath; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + new Thread(new Runnable() { + @Override + public void run() { + AppYsV2 aidi1 = new AppYsV2(); + aidi1.init(MainActivity.this, "https://vipmv.co/xgapp.php/v1/"); + String json = aidi1.homeContent(true); + System.out.println(json); + JSONObject homeContent = null; + try { + homeContent = new JSONObject(aidi1.homeVideoContent()); + System.out.println(homeContent.toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + System.out.println(aidi1.categoryContent("1", "1", false, null)); + if (homeContent != null) { + try { + List ids = new ArrayList(); + JSONArray array = homeContent.getJSONArray("list"); + for (int i = 0; i < array.length() && i < 3; i++) { + try { + ids.clear(); + ids.add(array.getJSONObject(i).getString("vod_id")); + System.out.println(aidi1.detailContent(ids)); + JSONObject detailContent = new JSONObject(aidi1.detailContent(ids)).getJSONArray("list").getJSONObject(0); + String[] playFlags = detailContent.getString("vod_play_from").split("\\$\\$\\$"); + String[] playUrls = detailContent.getString("vod_play_url").split("\\$\\$\\$"); + for (int j = 0; j < playFlags.length; j++) { + String pu = playUrls[j].split("#")[0].split("\\$")[1]; + System.out.println(aidi1.playerContent(playFlags[j], pu, new ArrayList<>())); + } + } catch (Throwable th) { + + } + } + } catch (Throwable th) { + + } + } + System.out.println(aidi1.searchContent("陪你一起", false)); + System.out.println(aidi1.searchContent("顶楼", false)); + + XPath aidi = new XPath(); + aidi.init(MainActivity.this, "{\n" + + " \"ua\": \"\",\n" + + " \"homeUrl\": \"http://www.9rmb.com\",\n" + + " \"cateNode\": \"//ul[contains(@class,'navbar-nav')]/li/a[contains(@href, '.html') and not(contains(@href, '6'))]\",\n" + + " \"cateName\": \"/text()\",\n" + + " \"cateId\": \"/@href\",\n" + + " \"cateIdR\": \"/type/(\\\\d+).html\",\n" + + " \"cateManual\": {},\n" + + " \"homeVodNode\": \"//div[@class='col-md-12 movie-item-out']//a[not(contains(@href, '6'))]/parent::*/parent::*/parent::*/div[contains(@class, 'movie-item-out') and position()<10]/div[@class='movie-item']/a\",\n" + + " \"homeVodName\": \"/@title\",\n" + + " \"homeVodId\": \"/@href\",\n" + + " \"homeVodIdR\": \"/show/(\\\\w+).html\",\n" + + " \"homeVodImg\": \"/img/@src\",\n" + + " \"homeVodMark\": \"/button/text()\",\n" + + " \"cateUrl\": \"http://www.9rmb.com/type/{cateId}/{catePg}.html\",\n" + + " \"cateVodNode\": \"//div[@class='movie-item']/a\",\n" + + " \"cateVodName\": \"/@title\",\n" + + " \"cateVodId\": \"/@href\",\n" + + " \"cateVodIdR\": \"/show/(\\\\w+).html\",\n" + + " \"cateVodImg\": \"/img/@src\",\n" + + " \"cateVodMark\": \"/button/text()\",\n" + + " \"dtUrl\": \"http://www.9rmb.com/show/{vid}.html\",\n" + + " \"dtNode\": \"//div[@class='container-fluid']\",\n" + + " \"dtName\": \"//div[@class='col-md-9']//div[@class='col-md-4']//img/@alt\",\n" + + " \"dtNameR\": \"\",\n" + + " \"dtImg\": \"//div[@class='col-md-9']//div[@class='col-md-4']//img/@src\",\n" + + " \"dtImgR\": \"\",\n" + + " \"dtCate\": \"//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '类型')]/parent::*/following-sibling::*/text()\",\n" + + " \"dtCateR\": \"\",\n" + + " \"dtYear\": \"//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '日期')]/parent::*/following-sibling::*/text()\",\n" + + " \"dtYearR\": \"\",\n" + + " \"dtArea\": \"//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '国家')]/parent::*/following-sibling::*/text()\",\n" + + " \"dtAreaR\": \"\",\n" + + " \"dtMark\": \"//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '状态')]/parent::*/following-sibling::*/text()\",\n" + + " \"dtMarkR\": \"\",\n" + + " \"dtActor\": \"//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '主演')]/parent::*/following-sibling::*/text()\",\n" + + " \"dtActorR\": \"\",\n" + + " \"dtDirector\": \"//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '导演')]/parent::*/following-sibling::*/text()\",\n" + + " \"dtDirectorR\": \"\",\n" + + " \"dtDesc\": \"//p[@class='summary']/text()\",\n" + + " \"dtDescR\": \"\",\n" + + " \"dtFromNode\": \"//div[contains(@class,'resource-list')]/div[@class='panel-heading']/strong\",\n" + + " \"dtFromName\": \"/text()\",\n" + + " \"dtFromNameR\": \"\\\\S+\\\\.(\\\\S+) \\\\(\",\n" + + " \"dtUrlNode\": \"//div[contains(@class,'resource-list')]/ul[@class='dslist-group']\",\n" + + " \"dtUrlSubNode\": \"/li/a\",\n" + + " \"dtUrlId\": \"@href\",\n" + + " \"dtUrlIdR\": \"/play/(\\\\S+).html\",\n" + + " \"dtUrlName\": \"/text()\",\n" + + " \"dtUrlNameR\": \"\",\n" + + " \"playUrl\": \"http://www.9rmb.com/play/{playUrl}.html\",\n" + + " \"playUa\": \"\",\n" + + " \"searchUrl\": \"http://www.9rmb.com/search?wd={wd}\",\n" + + " \"scVodNode\": \"//div[@class='movie-item']/a\",\n" + + " \"scVodName\": \"/@title\",\n" + + " \"scVodId\": \"/@href\",\n" + + " \"scVodIdR\": \"/show/(\\\\w+).html\",\n" + + " \"scVodImg\": \"/img/@src\",\n" + + " \"scVodMark\": \"/button/text()\"\n" + + "}\n"); + System.out.println(aidi.homeContent(true)); + System.out.println(aidi.homeVideoContent()); + System.out.println(aidi.categoryContent("2", "1", false, null)); + List ids = new ArrayList(); + ids.add("25603"); + System.out.println(aidi.detailContent(ids)); + System.out.println(aidi.playerContent("", "11111", new ArrayList<>())); + System.out.println(aidi.searchContent("陪你一起", false)); + } + }).start(); + } +} \ No newline at end of file diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/live/TxtSubscribe.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/live/TxtSubscribe.java new file mode 100644 index 0000000..73a3a44 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/live/TxtSubscribe.java @@ -0,0 +1,135 @@ +package com.github.catvod.live; + +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; + +public class TxtSubscribe { + + public static void subscribe(LinkedHashMap>> allLives, String url, HashMap headers) { + String content = OkHttpUtil.string(url, headers); + parse(allLives, content); + } + + public static void parse(LinkedHashMap>> allLives, String txt) { + try { + BufferedReader br = new BufferedReader(new StringReader(txt)); + String line = br.readLine(); + LinkedHashMap> noGroup = new LinkedHashMap<>(); + LinkedHashMap> groupLives = noGroup; + while (line != null) { + if (line.trim().isEmpty()) { + line = br.readLine(); + continue; + } + String[] lineInfo = line.split(","); + if (lineInfo.length < 2) { + line = br.readLine(); + continue; + } + if (line.contains("#genre#")) { + String group = lineInfo[0].trim(); + if (!allLives.containsKey(group)) { + groupLives = new LinkedHashMap<>(); + allLives.put(group, groupLives); + } else { + groupLives = allLives.get(group); + } + } else { + String title = lineInfo[0].trim(); + String[] urlMix = lineInfo[1].trim().split("#"); + for (int j = 0; j < urlMix.length; j++) { + String url = urlMix[j].trim(); + if (url.isEmpty()) + continue; + if (url.startsWith("http") || url.startsWith("rtsp") || url.startsWith("rtmp")) { + ArrayList urls = null; + if (!groupLives.containsKey(title)) { + urls = new ArrayList<>(); + groupLives.put(title, urls); + } else { + urls = groupLives.get(title); + } + if (!urls.contains(url)) + urls.add(url); + } else { + // SpiderDebug.log("Skip " + url); + } + } + } + line = br.readLine(); + } + br.close(); + if (!noGroup.isEmpty()) { + allLives.put("未分组", noGroup); + } + } catch (Throwable th) { + + } + } + + public static String live2Json(LinkedHashMap>> lives) { + JSONArray groups = new JSONArray(); + Iterator groupKeys = lives.keySet().iterator(); + while (groupKeys.hasNext()) { + String group = groupKeys.next(); + JSONArray channels = new JSONArray(); + LinkedHashMap> allChannel = lives.get(group); + if (allChannel.isEmpty()) + continue; + Iterator channelKeys = allChannel.keySet().iterator(); + while (channelKeys.hasNext()) { + String channel = channelKeys.next(); + ArrayList allUrls = allChannel.get(channel); + if (allUrls.isEmpty()) + continue; + JSONArray urls = new JSONArray(); + for (int i = 0; i < allUrls.size(); i++) { + urls.put(allUrls.get(i)); + } + JSONObject newChannel = new JSONObject(); + try { + newChannel.put("name", channel); + newChannel.put("urls", urls); + } catch (JSONException e) { + } + channels.put(newChannel); + } + JSONObject newGroup = new JSONObject(); + try { + newGroup.put("group", group); + newGroup.put("channels", channels); + } catch (JSONException e) { + } + groups.put(newGroup); + } + return groups.toString(); + } + + public static Object[] load(String ext) { + try { + LinkedHashMap>> allLives = new LinkedHashMap<>(); + TxtSubscribe.subscribe(allLives, ext, null); + String json = TxtSubscribe.live2Json(allLives); + Object[] result = new Object[3]; + result[0] = 200; + result[1] = "text/plain; charset=utf-8"; + ByteArrayInputStream baos = new ByteArrayInputStream(json.getBytes("UTF-8")); + result[2] = baos; + return result; + } catch (Throwable th) { + th.printStackTrace(); + } + return null; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonBasic.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonBasic.java new file mode 100644 index 0000000..940001b --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonBasic.java @@ -0,0 +1,73 @@ +package com.github.catvod.parser; + +import android.util.Base64; + +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Set; + +public class JsonBasic { + public static JSONObject parse(LinkedHashMap jx, String url) { + try { + SpiderDebug.log("Load Json Parse Basic..."); + if (jx.size() > 0) { + Set jxNames = jx.keySet(); + for (String jxName : jxNames) { + String parseUrl = jx.get(jxName); + HashMap reqHeaders = getReqHeader(parseUrl); + try { + String realUrl = reqHeaders.get("url"); + reqHeaders.remove("url"); + SpiderDebug.log(realUrl + url); + String json = OkHttpUtil.string(realUrl + url, reqHeaders); + JSONObject taskResult = Misc.jsonParse(url, json); + if (taskResult == null) + continue; + taskResult.put("jxFrom", jxName); + SpiderDebug.log(taskResult.toString()); + return taskResult; + } catch (Throwable th) { + SpiderDebug.log(th); + } + } + } + } catch (Throwable th) { + SpiderDebug.log(th); + } + return new JSONObject(); + } + + public static HashMap getReqHeader(String url) { + HashMap reqHeaders = new HashMap<>(); + reqHeaders.put("url", url); + if (url.contains("cat_ext")) { + try { + int start = url.indexOf("cat_ext="); + int end = url.indexOf("&", start); + String ext = url.substring(start + 8, end); + ext = new String(Base64.decode(ext, Base64.DEFAULT | Base64.URL_SAFE | Base64.NO_WRAP)); + String newUrl = url.substring(0, start) + url.substring(end + 1); + JSONObject jsonObject = new JSONObject(ext); + if (jsonObject.has("header")) { + JSONObject headerJson = jsonObject.optJSONObject("header"); + Iterator keys = headerJson.keys(); + while (keys.hasNext()) { + String key = keys.next(); + reqHeaders.put(key, headerJson.optString(key, "")); + } + } + reqHeaders.put("url", newUrl); + } catch (Throwable th) { + + } + } + return reqHeaders; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonParallel.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonParallel.java new file mode 100644 index 0000000..0de41a6 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonParallel.java @@ -0,0 +1,94 @@ +package com.github.catvod.parser; + +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * 并发解析,直到获得第一个结果 + *

+ * 默认解析超时时间为15秒,如果需要请自定义SpiderReq的HttpClient + *

+ * Author: CatVod + */ +public class JsonParallel { + private static final String ParseOKTag = "p_json_parse"; + + public static JSONObject parse(LinkedHashMap jx, String url) { + try { + if (jx.size() > 0) { + ExecutorService executorService = Executors.newFixedThreadPool(3); + CompletionService completionService = new ExecutorCompletionService(executorService); + List> futures = new ArrayList<>(); + Set jxNames = jx.keySet(); + for (String jxName : jxNames) { + String parseUrl = jx.get(jxName); + futures.add(completionService.submit(new Callable() { + @Override + public JSONObject call() throws Exception { + try { + HashMap reqHeaders = JsonBasic.getReqHeader(parseUrl); + String realUrl = reqHeaders.get("url"); + reqHeaders.remove("url"); + SpiderDebug.log(realUrl + url); + String json = OkHttpUtil.string(realUrl + url, ParseOKTag, reqHeaders); + JSONObject taskResult = Misc.jsonParse(url, json); + taskResult.put("jxFrom", jxName); + SpiderDebug.log(taskResult.toString()); + return taskResult; + } catch (Throwable th) { + SpiderDebug.log(th); + return null; + } + } + })); + } + JSONObject pTaskResult = null; + for (int i = 0; i < futures.size(); ++i) { + Future completed = completionService.take(); + try { + pTaskResult = completed.get(); + if (pTaskResult != null) { + OkHttpUtil.cancel(ParseOKTag); + for (int j = 0; j < futures.size(); j++) { + try { + futures.get(j).cancel(true); + } catch (Throwable th) { + SpiderDebug.log(th); + } + } + futures.clear(); + break; + } + } catch (Throwable th) { + SpiderDebug.log(th); + } + } + try { + executorService.shutdownNow(); + } catch (Throwable th) { + SpiderDebug.log(th); + } + if (pTaskResult != null) + return pTaskResult; + } + } catch (Throwable th) { + SpiderDebug.log(th); + } + return new JSONObject(); + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonSequence.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonSequence.java new file mode 100644 index 0000000..de78928 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/JsonSequence.java @@ -0,0 +1,48 @@ +package com.github.catvod.parser; + +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Set; + +/** + * 依次解析,失败尝试下一个 + *

+ * 默认解析超时时间为15秒,如果需要请自定义SpiderReq的HttpClient + *

+ * Author: CatVod + */ +public class JsonSequence { + public static JSONObject parse(LinkedHashMap jx, String url) { + try { + if (jx.size() > 0) { + Set jxNames = jx.keySet(); + for (String jxName : jxNames) { + String parseUrl = jx.get(jxName); + try { + HashMap reqHeaders = JsonBasic.getReqHeader(parseUrl); + String realUrl = reqHeaders.get("url"); + reqHeaders.remove("url"); + SpiderDebug.log(realUrl + url); + String json = OkHttpUtil.string(realUrl + url, reqHeaders); + JSONObject taskResult = Misc.jsonParse(url, json); + if (taskResult == null) + continue; + taskResult.put("jxFrom", jxName); + return taskResult; + } catch (Throwable th) { + SpiderDebug.log(th); + } + } + } + } catch (Throwable th) { + SpiderDebug.log(th); + } + return new JSONObject(); + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/MixDemo.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/MixDemo.java new file mode 100644 index 0000000..921ddaf --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/parser/MixDemo.java @@ -0,0 +1,189 @@ +package com.github.catvod.parser; + +import android.util.Base64; + +import com.github.catvod.crawler.SpiderDebug; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; + +public class MixDemo { + + /* + "parses": [ + { + "name": "聚合", + "type": 3, + "url": "Demo" + }, + { + "name": "解析", + "type": 1, + "url": "https://192.168.10.88/jx.php?url=", + "ext": { + "flag": [ + "qq", + "iqiyi", + "qiyi", + "爱奇艺", + "腾讯", + "letv", + "sohu", + "tudou", + "pptv", + "mgtv", + "wasu", + "bilibili" + ] + } + }] + */ + + public static HashMap> flagWebJx = new HashMap<>(); + + static HashMap> configs = null; + + public static JSONObject parse(LinkedHashMap> jx, String nameMe, String flag, String url) { + try { + if (configs == null) { + configs = new HashMap<>(); + Iterator keys = jx.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + HashMap parseBean = jx.get(key); + String type = parseBean.get("type"); + if (type.equals("1") || type.equals("0")) { + try { + JSONArray flags = new JSONObject(parseBean.get("ext")).getJSONArray("flag"); + for (int j = 0; j < flags.length(); j++) { + String flagKey = flags.getString(j); + ArrayList flagJx = configs.get(flagKey); + if (flagJx == null) { + flagJx = new ArrayList<>(); + configs.put(flagKey, flagJx); + } + flagJx.add(key); + } + } catch (Throwable th) { + + } + } + } + } + // 通过上面的配置获得解析列表 + LinkedHashMap jsonJx = new LinkedHashMap<>(); + ArrayList webJx = new ArrayList<>(); + ArrayList flagJx = configs.get(flag); + if (flagJx != null && !flagJx.isEmpty()) { + for (int i = 0; i < flagJx.size(); i++) { + String key = flagJx.get(i); + HashMap parseBean = jx.get(key); + String type = parseBean.get("type"); + if (type.equals("1")) { + jsonJx.put(key, mixUrl(parseBean.get("url"), parseBean.get("ext"))); + } else if (type.equals("0")) { + webJx.add(parseBean.get("url")); + } + } + } else { + Iterator keys = jx.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + HashMap parseBean = jx.get(key); + String type = parseBean.get("type"); + if (type.equals("1")) { + jsonJx.put(key, mixUrl(parseBean.get("url"), parseBean.get("ext"))); + } else if (type.equals("0")) { + webJx.add(parseBean.get("url")); + } + } + } + if (!webJx.isEmpty()) { + flagWebJx.put(flag, webJx); + } + // 优先使用json并发解析 + JSONObject jsonResult = JsonParallel.parse(jsonJx, url); + if (jsonResult != null && jsonResult.has("url")) { + return jsonResult; + } + // json解析没有得到结果 用webview解析 + if (!webJx.isEmpty()) { + JSONObject webResult = new JSONObject(); + webResult.put("url", "proxy://do=parseMix&flag=" + flag + "&url=" + Base64.encodeToString(url.getBytes(), Base64.DEFAULT | Base64.URL_SAFE | Base64.NO_WRAP)); + webResult.put("parse", 1); + return webResult; + } + } catch (Throwable th) { + SpiderDebug.log(th); + } + return new JSONObject(); + } + + private static String mixUrl(String url, String ext) { + if (ext.trim().length() > 0) { + int idx = url.indexOf("?"); + if (idx > 0) { + return url.substring(0, idx + 1) + "cat_ext=" + Base64.encodeToString(ext.getBytes(), Base64.DEFAULT | Base64.URL_SAFE | Base64.NO_WRAP) + "&" + url.substring(idx + 1); + } + } + return url; + } + + public static Object[] loadHtml(String flag, String url) { + try { + url = new String(Base64.decode(url, Base64.DEFAULT | Base64.URL_SAFE | Base64.NO_WRAP), "UTF-8"); + String html = "\n" + + "\n" + + "\n" + + "\n" + + "解析\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""; + + StringBuilder jxs = new StringBuilder(); + if (flagWebJx.containsKey(flag)) { + ArrayList jxUrls = flagWebJx.get(flag); + for (int i = 0; i < jxUrls.size(); i++) { + jxs.append("\""); + jxs.append(jxUrls.get(i)); + jxs.append("\""); + if (i < jxUrls.size() - 1) { + jxs.append(","); + } + } + } + html = html.replace("#url#", url).replace("#jxs#", jxs.toString()); + Object[] result = new Object[3]; + result[0] = 200; + result[1] = "text/html; charset=\"UTF-8\""; + ByteArrayInputStream baos = new ByteArrayInputStream(html.toString().getBytes("UTF-8")); + result[2] = baos; + return result; + } catch (Throwable th) { + th.printStackTrace(); + } + return null; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Aidi.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Aidi.java new file mode 100644 index 0000000..d72c5f8 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Aidi.java @@ -0,0 +1,466 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Demo for spider + *

+ * Author: CatVod + */ +public class Aidi extends Spider { + private static final String siteUrl = "https://aidi.tv"; + private static final String siteHost = "aidi.tv"; + + /** + * 播放源配置 + */ + private JSONObject playerConfig; + /** + * 筛选配置 + */ + private JSONObject filterConfig; + + private Pattern regexCategory = Pattern.compile("/v/(\\w+).html"); + private Pattern regexVid = Pattern.compile("/movie/(\\w+).html"); + private Pattern regexPlay = Pattern.compile("\\S+/play/(\\w+)-(\\d+)-(\\d+).html"); + private Pattern regexPage = Pattern.compile("/show/(\\S+).html"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"xg_app_player\":{\"sh\":\"app全局解析\",\"pu\":\"\",\"sn\":0,\"or\":999},\"dplayer\":{\"sh\":\"dplayer\",\"pu\":\"\",\"sn\":0,\"or\":999},\"videojs\":{\"sh\":\"videojs-H5播放器\",\"pu\":\"\",\"sn\":0,\"or\":999},\"iva\":{\"sh\":\"iva-H5播放器\",\"pu\":\"\",\"sn\":0,\"or\":999},\"iframe\":{\"sh\":\"iframe外链数据\",\"pu\":\"\",\"sn\":0,\"or\":999},\"link\":{\"sh\":\"外链数据\",\"pu\":\"top.\",\"sn\":0,\"or\":999},\"swf\":{\"sh\":\"Flash文件\",\"pu\":\"\",\"sn\":0,\"or\":999},\"flv\":{\"sh\":\"Flv文件\",\"pu\":\"\",\"sn\":0,\"or\":999},\"qiepian\":{\"sh\":\"爱迪云播\",\"pu\":\"\",\"sn\":0,\"or\":999},\"ckm3u8\":{\"sh\":\"爱迪影视\",\"pu\":\"\",\"sn\":0,\"or\":999},\"xin\":{\"sh\":\"爱迪高速\",\"pu\":\"\",\"sn\":0,\"or\":999},\"ppyun\":{\"sh\":\"爱迪云链\",\"pu\":\"\",\"sn\":0,\"or\":999},\"jisu\":{\"sh\":\"极速路线\",\"pu\":\"\",\"sn\":0,\"or\":999},\"ppayun\":{\"sh\":\"pp云\",\"pu\":\"https://wy.ppayun.cn/api/ShowVideoWy/973ed6d6891c43269706718cb0aedb72/\",\"sn\":0,\"or\":999},\"yun3edu\":{\"sh\":\"爱迪云三\",\"pu\":\"\",\"sn\":0,\"or\":999},\"guoji\":{\"sh\":\"國際路線\",\"pu\":\"\",\"sn\":0,\"or\":999},\"ddyunp\":{\"sh\":\"蓝光线路\",\"pu\":\"\",\"sn\":0,\"or\":999},\"eduyun\":{\"sh\":\"爱迪云二\",\"pu\":\"\",\"sn\":0,\"or\":999},\"dbm3u8\":{\"sh\":\"720P线路\",\"pu\":\"\",\"sn\":0,\"or\":999},\"yjm3u8\":{\"sh\":\"备用线路\",\"pu\":\"\",\"sn\":0,\"or\":999}}"); + filterConfig = new JSONObject("{\"dianying\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"dianying\"},{\"n\":\"动作片\",\"v\":\"dongzuopian\"},{\"n\":\"喜剧片\",\"v\":\"xijupian\"},{\"n\":\"爱情片\",\"v\":\"aiqingpian\"},{\"n\":\"科幻片\",\"v\":\"kehuanpian\"},{\"n\":\"恐怖片\",\"v\":\"kongbupian\"},{\"n\":\"剧情片\",\"v\":\"juqingpian\"},{\"n\":\"战争片\",\"v\":\"zhanzhengpian\"},{\"n\":\"传记\",\"v\":\"zhuanji\"}]},{\"key\":3,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"喜剧\",\"v\":\"喜剧\"},{\"n\":\"爱情\",\"v\":\"爱情\"},{\"n\":\"恐怖\",\"v\":\"恐怖\"},{\"n\":\"动作\",\"v\":\"动作\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"剧情\",\"v\":\"剧情\"},{\"n\":\"战争\",\"v\":\"战争\"},{\"n\":\"警匪\",\"v\":\"警匪\"},{\"n\":\"犯罪\",\"v\":\"犯罪\"},{\"n\":\"动画\",\"v\":\"动画\"},{\"n\":\"奇幻\",\"v\":\"奇幻\"},{\"n\":\"武侠\",\"v\":\"武侠\"},{\"n\":\"冒险\",\"v\":\"冒险\"},{\"n\":\"枪战\",\"v\":\"枪战\"},{\"n\":\"恐怖\",\"v\":\"恐怖\"},{\"n\":\"悬疑\",\"v\":\"悬疑\"},{\"n\":\"惊悚\",\"v\":\"惊悚\"},{\"n\":\"经典\",\"v\":\"经典\"},{\"n\":\"青春\",\"v\":\"青春\"},{\"n\":\"文艺\",\"v\":\"文艺\"},{\"n\":\"微电影\",\"v\":\"微电影\"},{\"n\":\"古装\",\"v\":\"古装\"},{\"n\":\"历史\",\"v\":\"历史\"},{\"n\":\"运动\",\"v\":\"运动\"},{\"n\":\"农村\",\"v\":\"农村\"},{\"n\":\"儿童\",\"v\":\"儿童\"},{\"n\":\"网络电影\",\"v\":\"网络电影\"},{\"n\":\"情色\",\"v\":\"情色\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"大陆\",\"v\":\"大陆\"},{\"n\":\"香港\",\"v\":\"香港\"},{\"n\":\"台湾\",\"v\":\"台湾\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"法国\",\"v\":\"法国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"德国\",\"v\":\"德国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"印度\",\"v\":\"印度\"},{\"n\":\"意大利\",\"v\":\"意大利\"},{\"n\":\"西班牙\",\"v\":\"西班牙\"},{\"n\":\"加拿大\",\"v\":\"加拿大\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"法语\",\"v\":\"法语\"},{\"n\":\"德语\",\"v\":\"德语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"lianxuju\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"lianxuju\"},{\"n\":\"国产剧\",\"v\":\"guochanju\"},{\"n\":\"港台剧\",\"v\":\"gangtaiju\"},{\"n\":\"韩剧\",\"v\":\"hanju\"},{\"n\":\"美剧\",\"v\":\"meiju\"},{\"n\":\"日剧\",\"v\":\"riju\"},{\"n\":\"英剧\",\"v\":\"yingju\"},{\"n\":\"泰剧\",\"v\":\"taiju\"}]},{\"key\":3,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"古装\",\"v\":\"古装\"},{\"n\":\"战争\",\"v\":\"战争\"},{\"n\":\"青春偶像\",\"v\":\"青春偶像\"},{\"n\":\"喜剧\",\"v\":\"喜剧\"},{\"n\":\"家庭\",\"v\":\"家庭\"},{\"n\":\"犯罪\",\"v\":\"犯罪\"},{\"n\":\"动作\",\"v\":\"动作\"},{\"n\":\"奇幻\",\"v\":\"奇幻\"},{\"n\":\"剧情\",\"v\":\"剧情\"},{\"n\":\"历史\",\"v\":\"历史\"},{\"n\":\"经典\",\"v\":\"经典\"},{\"n\":\"乡村\",\"v\":\"乡村\"},{\"n\":\"情景\",\"v\":\"情景\"},{\"n\":\"商战\",\"v\":\"商战\"},{\"n\":\"网剧\",\"v\":\"网剧\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"内地\",\"v\":\"内地\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"香港\",\"v\":\"香港\"},{\"n\":\"台湾\",\"v\":\"台湾\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"新加坡\",\"v\":\"新加坡\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"zongyi\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"zongyi\"},{\"n\":\"内地综艺\",\"v\":\"ndzy\"},{\"n\":\"港台综艺\",\"v\":\"gtzy\"},{\"n\":\"韩国综艺节目\",\"v\":\"variety\"},{\"n\":\"欧美综艺\",\"v\":\"oumeizongyi\"}]},{\"key\":3,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"选秀\",\"v\":\"选秀\"},{\"n\":\"情感\",\"v\":\"情感\"},{\"n\":\"访谈\",\"v\":\"访谈\"},{\"n\":\"播报\",\"v\":\"播报\"},{\"n\":\"旅游\",\"v\":\"旅游\"},{\"n\":\"音乐\",\"v\":\"音乐\"},{\"n\":\"美食\",\"v\":\"美食\"},{\"n\":\"纪实\",\"v\":\"纪实\"},{\"n\":\"曲艺\",\"v\":\"曲艺\"},{\"n\":\"生活\",\"v\":\"生活\"},{\"n\":\"游戏互动\",\"v\":\"游戏互动\"},{\"n\":\"财经\",\"v\":\"财经\"},{\"n\":\"求职\",\"v\":\"求职\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"内地\",\"v\":\"内地\"},{\"n\":\"港台\",\"v\":\"港台\"},{\"n\":\"日韩\",\"v\":\"日韩\"},{\"n\":\"欧美\",\"v\":\"欧美\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"dongman\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"dongman\"},{\"n\":\"日韩动漫\",\"v\":\"rihan\"},{\"n\":\"国产动漫\",\"v\":\"guoman\"},{\"n\":\"欧美动漫\",\"v\":\"oumei\"},{\"n\":\"动漫电影\",\"v\":\"cartoon\"}]},{\"key\":3,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"情感\",\"v\":\"情感\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"热血\",\"v\":\"热血\"},{\"n\":\"推理\",\"v\":\"推理\"},{\"n\":\"搞笑\",\"v\":\"搞笑\"},{\"n\":\"冒险\",\"v\":\"冒险\"},{\"n\":\"萝莉\",\"v\":\"萝莉\"},{\"n\":\"校园\",\"v\":\"校园\"},{\"n\":\"动作\",\"v\":\"动作\"},{\"n\":\"机战\",\"v\":\"机战\"},{\"n\":\"运动\",\"v\":\"运动\"},{\"n\":\"战争\",\"v\":\"战争\"},{\"n\":\"少年\",\"v\":\"少年\"},{\"n\":\"少女\",\"v\":\"少女\"},{\"n\":\"社会\",\"v\":\"社会\"},{\"n\":\"原创\",\"v\":\"原创\"},{\"n\":\"亲子\",\"v\":\"亲子\"},{\"n\":\"益智\",\"v\":\"益智\"},{\"n\":\"励志\",\"v\":\"励志\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国产\",\"v\":\"国产\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"欧美\",\"v\":\"欧美\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"jilu\":[{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}]}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + Document doc = Jsoup.parse(OkHttpUtil.string(siteUrl, getHeaders(siteUrl))); + // 分类节点 + Elements elements = doc.select("ul.nav_list > li a"); + JSONArray classes = new JSONArray(); + for (Element ele : elements) { + String name = ele.select("span").text(); + if (name.equals("樱花动漫")) { + name = "动漫"; + } + boolean show = name.equals("电影") || + name.equals("连续剧") || + name.equals("动漫") || + name.equals("纪录片") || + name.equals("综艺"); + if (show) { + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + // 把分类的id和名称取出来加到列表里 + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + if (filter) { + result.put("filters", filterConfig); + } + result.put("class", classes); + try { + // 取首页推荐视频列表 + Element homeList = doc.select("div.vod_row").get(1); + Elements list = homeList.select("div.cbox1 > ul.vodlist li a.vodlist_thumb"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.attr("title"); + String cover = vod.attr("data-original"); + String remark = vod.selectFirst("span.pic_text").text(); + Matcher matcher = regexVid.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String[] urlParams = new String[]{"", "", "", "", "", "", "", "", "", "", "", ""}; + urlParams[0] = tid; + urlParams[8] = pg; + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + urlParams[Integer.parseInt(key)] = URLEncoder.encode(value); + } + } + // 获取分类数据的url + String url = siteUrl + "/show/" + TextUtils.join("-", urlParams) + ".html"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + // 取页码相关信息 + Elements pageInfo = doc.select("ul.page li"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element li = pageInfo.get(i); + Element a = li.selectFirst("a"); + if (a == null) + continue; + String name = a.text(); + if (page == -1 && li.hasClass("active")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + page = 0; + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + // 取当前分类页的视频列表 + Elements list = doc.select("ul.vodlist li a.vodlist_thumb"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.attr("title"); + String cover = vod.attr("data-original"); + String remark = vod.selectFirst("span.pic_text").text(); + Matcher matcher = regexVid.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 60); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 60); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/movie/" + ids.get(0) + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + String cover = doc.selectFirst("div.detail_list_box a.vodlist_thumb img").attr("data-original"); + String title = doc.selectFirst("div.detail_list_box h2.title").text(); + String desc = Jsoup.parse(doc.selectFirst("meta[name=description]").attr("content")).text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.detail_list_box div.content_detail span"); + for (int i = 0; i < span_text_muted.size(); i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.equals("类型:")) { + category = text.nextElementSibling().text(); + } else if (info.equals("年份:")) { + year = text.nextElementSibling().text(); + } else if (info.equals("地区:")) { + area = text.nextElementSibling().text(); + } else if (info.equals("状态:")) { + remark = text.nextElementSibling().text(); + } else if (info.equals("导演:")) { + List directors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + directors.add(aa.get(j).text()); + } + director = TextUtils.join(",", directors); + } else if (info.equals("主演:")) { + List actors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + actors.add(aa.get(j).text()); + } + actor = TextUtils.join(",", actors); + } + } + + vodList.put("vod_id", ids.get(0)); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + // 取播放列表数据 + Elements sources = doc.select("div.play_source_tab > a"); + Elements sourceList = doc.select("div.play_list_box > div.playlist_full"); + + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + String sourceName = source.attr("alt"); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = sourceList.get(i).select("li > a"); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + // 播放页 url + String url = siteUrl + "/play/" + id + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { // 取直链 + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + String playUrl = pCfg.getString("pu"); + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", ""); + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 搜索 + * + * @param key + * @param quick 是否播放页的快捷搜索 + * @return + */ + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + String url = siteUrl + "/vsearch/-------------.html?wd=" + URLEncoder.encode(key) + "&submit="; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + + JSONArray videos = new JSONArray(); + Elements list = doc.select("div.search_box li.searchlist_item a.vodlist_thumb"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.attr("title"); + String cover = vod.attr("data-original"); + String remark = vod.selectFirst("span.pic_text").text(); + Matcher matcher = regexVid.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/AppYs.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/AppYs.java new file mode 100644 index 0000000..a406414 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/AppYs.java @@ -0,0 +1,1124 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * M浏览器中的App影视 + *

+ * Author: 群友 不负此生 + */ +public class AppYs extends Spider { + + private String extString = ""; + + @Override + public void init(Context context, String extend) { + super.init(context, extend); + this.extString = extend; + } + + @Override + public String homeContent(boolean filter) { + try { + String url = getCateUrl(extString); + JSONArray jsonArray = null; + if (!url.isEmpty()) { + SpiderDebug.log(url); + String json = OkHttpUtil.string(url, getHeaders(url)); + JSONObject obj = new JSONObject(json); + if (obj.has("list") && obj.get("list") instanceof JSONArray) { + jsonArray = obj.getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONObject && obj.getJSONObject("data").has("list") && obj.getJSONObject("data").get("list") instanceof JSONArray) { + jsonArray = obj.getJSONObject("data").getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONArray) { + jsonArray = obj.getJSONArray("data"); + } + } else { // 通过filter列表读分类 + String filterStr = getFilterTypes(url, null); + String[] classes = filterStr.split("\n")[0].split("\\+"); + jsonArray = new JSONArray(); + for (int i = 1; i < classes.length; i++) { + String[] kv = classes[i].trim().split("="); + if (kv.length < 2) + continue; + JSONObject newCls = new JSONObject(); + newCls.put("type_name", kv[0].trim()); + newCls.put("type_id", kv[1].trim()); + jsonArray.put(newCls); + } + } + JSONObject result = new JSONObject(); + JSONArray classes = new JSONArray(); + if (jsonArray != null) { + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jObj = jsonArray.getJSONObject(i); + String typeName = jObj.getString("type_name"); + if (isBan(typeName)) + continue; + String typeId = jObj.getString("type_id"); + JSONObject newCls = new JSONObject(); + newCls.put("type_id", typeId); + newCls.put("type_name", typeName); + JSONObject typeExtend = jObj.optJSONObject("type_extend"); + if (filter) { + String filterStr = getFilterTypes(url, typeExtend); + String[] filters = filterStr.split("\n"); + JSONArray filterArr = new JSONArray(); + for (int k = url.isEmpty() ? 1 : 0; k < filters.length; k++) { + String l = filters[k].trim(); + if (l.isEmpty()) + continue; + String[] oneLine = l.split("\\+"); + String type = oneLine[0].trim(); + String typeN = type; + if (type.contains("筛选")) { + type = type.replace("筛选", ""); + if (type.equals("class")) + typeN = "类型"; + else if (type.equals("area")) + typeN = "地区"; + else if (type.equals("lang")) + typeN = "语言"; + else if (type.equals("year")) + typeN = "年份"; + } + JSONObject jOne = new JSONObject(); + jOne.put("key", type); + jOne.put("name", typeN); + JSONArray valueArr = new JSONArray(); + for (int j = 1; j < oneLine.length; j++) { + JSONObject kvo = new JSONObject(); + String kv = oneLine[j].trim(); + int sp = kv.indexOf("="); + if (sp == -1) { + if (isBan(kv)) + continue; + kvo.put("n", kv); + kvo.put("v", kv); + } else { + String n = kv.substring(0, sp); + if (isBan(n)) + continue; + kvo.put("n", n.trim()); + kvo.put("v", kv.substring(sp + 1).trim()); + } + valueArr.put(kvo); + } + jOne.put("value", valueArr); + filterArr.put(jOne); + } + if (!result.has("filters")) { + result.put("filters", new JSONObject()); + } + result.getJSONObject("filters").put(typeId, filterArr); + } + classes.put(newCls); + } + } + result.put("class", classes); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String homeVideoContent() { + try { + String apiUrl = extString; + String url = getRecommendUrl(apiUrl); + boolean isTV = false; + if (url.isEmpty()) { + url = getCateFilterUrlPrefix(apiUrl) + "movie&page=1&area=&type=&start="; + isTV = true; + } + SpiderDebug.log(url); + String json = OkHttpUtil.string(url, getHeaders(url)); + JSONObject obj = new JSONObject(json); + JSONArray videos = new JSONArray(); + if (isTV) { + JSONArray jsonArray = obj.getJSONArray("data"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("nextlink")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.getString("pic")); + v.put("vod_remarks", vObj.getString("state")); + videos.put(v); + } + } else { + ArrayList arrays = new ArrayList<>(); + findJsonArray(obj, "vlist", arrays); + if (arrays.isEmpty()) { + findJsonArray(obj, "vod_list", arrays); + } + List ids = new ArrayList<>(); + for (JSONArray jsonArray : arrays) { + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + String vid = vObj.getString("vod_id"); + if (ids.contains(vid)) + continue; + ids.add(vid); + JSONObject v = new JSONObject(); + v.put("vod_id", vid); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } + } + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String apiUrl = extString; + String url = getCateFilterUrlPrefix(apiUrl) + tid + getCateFilterUrlSuffix(apiUrl); + url = url.replace("#PN#", pg); + url = url.replace("筛选class", (extend != null && extend.containsKey("class")) ? extend.get("class") : ""); + url = url.replace("筛选area", (extend != null && extend.containsKey("area")) ? extend.get("area") : ""); + url = url.replace("筛选lang", (extend != null && extend.containsKey("lang")) ? extend.get("lang") : ""); + url = url.replace("筛选year", (extend != null && extend.containsKey("year")) ? extend.get("year") : ""); + url = url.replace("排序", (extend != null && extend.containsKey("排序")) ? extend.get("排序") : ""); + SpiderDebug.log(url); + String json = OkHttpUtil.string(url, getHeaders(url)); + JSONObject obj = new JSONObject(json); + int totalPg = Integer.MAX_VALUE; + try { + if (obj.has("totalpage") && obj.get("totalpage") instanceof Integer) { + totalPg = obj.getInt("totalpage"); + } else if (obj.has("pagecount") && obj.get("pagecount") instanceof Integer) { + totalPg = obj.getInt("pagecount"); + } else if (obj.has("data") && obj.get("data") instanceof JSONObject && + (obj.getJSONObject("data").has("total") && obj.getJSONObject("data").get("total") instanceof Integer && + obj.getJSONObject("data").has("limit") && obj.getJSONObject("data").get("limit") instanceof Integer)) { + int limit = obj.getJSONObject("data").getInt("limit"); + int total = obj.getJSONObject("data").getInt("total"); + totalPg = total % limit == 0 ? (total / limit) : (total / limit + 1); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + + JSONArray jsonArray = null; + JSONArray videos = new JSONArray(); + if (obj.has("list") && obj.get("list") instanceof JSONArray) { + jsonArray = obj.getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONObject && obj.getJSONObject("data").has("list") && obj.getJSONObject("data").get("list") instanceof JSONArray) { + jsonArray = obj.getJSONObject("data").getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONArray) { + jsonArray = obj.getJSONArray("data"); + } + if (jsonArray != null) { + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + if (vObj.has("vod_id")) { + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("vod_id")); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } else { + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("nextlink")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.getString("pic")); + v.put("vod_remarks", vObj.getString("state")); + videos.put(v); + } + } + } + JSONObject result = new JSONObject(); + result.put("page", pg); + result.put("pagecount", totalPg); + result.put("limit", 90); + result.put("total", Integer.MAX_VALUE); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String detailContent(List ids) { + try { + String apiUrl = extString; + String url = getPlayUrlPrefix(apiUrl) + ids.get(0); + SpiderDebug.log(url); + String json = OkHttpUtil.string(url, getHeaders(url)); + JSONObject obj = new JSONObject(json); + JSONObject result = new JSONObject(); + JSONObject vod = new JSONObject(); + genPlayList(apiUrl, obj, json, vod, ids.get(0)); + JSONArray list = new JSONArray(); + list.put(vod); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String searchContent(String key, boolean quick) { + try { + String apiUrl = extString; + String url = getSearchUrl(apiUrl, URLEncoder.encode(key)); + String json = OkHttpUtil.string(url, getHeaders(url)); + JSONObject obj = new JSONObject(json); + JSONArray jsonArray = null; + JSONArray videos = new JSONArray(); + if (obj.has("list") && obj.get("list") instanceof JSONArray) { + jsonArray = obj.getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONObject && obj.getJSONObject("data").has("list") && obj.getJSONObject("data").get("list") instanceof JSONArray) { + jsonArray = obj.getJSONObject("data").getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONArray) { + jsonArray = obj.getJSONArray("data"); + } + if (jsonArray != null) { + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + if (vObj.has("vod_id")) { + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("vod_id")); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } else { + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("nextlink")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.getString("pic")); + v.put("vod_remarks", vObj.getString("state")); + videos.put(v); + } + } + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + String apiUrl = extString; + String parseUrl = getParseUrl(apiUrl, flag); + String playerUrl = getPlayerUrl(apiUrl, parseUrl, id); + JSONObject result = new JSONObject(); + getFinalVideo(playerUrl, result); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + private void findJsonArray(JSONObject obj, String match, ArrayList result) { + Iterator keys = obj.keys(); + while (keys.hasNext()) { + String k = keys.next(); + try { + Object o = obj.get(k); + if (k.equals(match) && o instanceof JSONArray) + result.add((JSONArray) o); + if (o instanceof JSONObject) { + findJsonArray((JSONObject) o, match, result); + } else if (o instanceof JSONArray) { + JSONArray array = (JSONArray) o; + for (int i = 0; i < array.length(); i++) { + findJsonArray(array.getJSONObject(i), match, result); + } + } + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + } + + private String jsonArr2Str(JSONArray array) { + try { + ArrayList strings = new ArrayList<>(); + for (int i = 0; i < array.length(); i++) { + strings.add(array.getString(i)); + } + return TextUtils.join(",", strings); + } catch (JSONException e) { + } + return ""; + } + + private static HashMap fakeVips = null; + private static final Object lock = new Object(); + + private static String stopVipFlag(String flag) { + synchronized (lock) { + if (fakeVips == null) { + fakeVips = new HashMap<>(); + fakeVips.put("youku", "优酷M"); + fakeVips.put("qq", "腾讯M"); + fakeVips.put("iqiyi", "爱奇艺M"); + fakeVips.put("qiyi", "奇艺M"); + fakeVips.put("letv", "乐视M"); + fakeVips.put("sohu", "搜狐M"); + fakeVips.put("tudou", "土豆M"); + fakeVips.put("pptv", "PPTVM"); + fakeVips.put("mgtv", "芒果TVM"); + fakeVips.put("wasu", "华数M"); + fakeVips.put("bilibili", "哔哩M"); + } + if (fakeVips.containsKey(flag)) { + return fakeVips.get(flag); + } + return flag; + } + } + + private HashMap getHeaders(String URL) { + HashMap headers = new HashMap<>(); + headers.put("User-Agent", UA(URL)); + return headers; + } + + private boolean isBan(String key) { + return key.equals("伦理") || key.equals("情色") || key.equals("福利"); + } + + // M 扩展方法 + + // ######重组搜索 + private String getSearchUrl(String URL, String KEY) { + if (URL.contains(".vod")) { + if (URL.contains("iopenyun.com")) { + return URL + "/list?wd=" + KEY + "&page="; + } else { + return URL + "?wd=" + KEY + "&page="; + } + } else if (URL.contains("api.php/app") || URL.contains("xgapp")) { + return URL + "search?text=" + KEY + "&pg="; + } else if (urlPattern1.matcher(URL).find()) { + if (URL.contains("esellauto") + || URL.contains("1.14.63.101") + || URL.contains("zjys") + || URL.contains("dcd") + || URL.contains("lxue") + || URL.contains("weetai.cn") + || URL.contains("haokanju1") + || URL.contains("fit:8") + || URL.contains("zjj.life") + || URL.contains("love9989") + || URL.contains("8d8q") + || URL.contains("lk.pxun") + || URL.contains("hgyx") + || URL.contains("521x5") + || URL.contains("lxyyy") + || URL.contains("0818tv") + || URL.contains("diyoui") + || URL.contains("diliktv") + || URL.contains("ppzhu") + || URL.contains("aitesucai") + || URL.contains("zz.ci") + || URL.contains("chxjon") + || URL.contains("watchmi") + || URL.contains("vipbp") + || URL.contains("bhtv") + || URL.contains("xfykl")) { + return URL + "?ac=list&" + "wd=" + KEY + "&page="; + } else { + return URL + "?ac=list&" + "zm=" + KEY + "&page="; + } + } + return ""; + } + + // ######UA + private static final Pattern urlPattern1 = Pattern.compile("api\\.php/.*?/vod"); + private static final Pattern urlPattern2 = Pattern.compile("api\\.php/.+?\\.vod"); + private static final Pattern parsePattern = Pattern.compile("/.+\\?.+="); + private static final Pattern parsePattern1 = Pattern.compile(".*(url|v|vid|php\\?id)="); + private static final Pattern parsePattern2 = Pattern.compile("https?://[^/]*"); + private static final Pattern[] parseMatchForDotVod = new Pattern[]{ + Pattern.compile("jx\\.+huimaojia\\.+com/player"), + Pattern.compile("py\\.+789pan\\.+cn/player/tm\\.php\\?url="), + Pattern.compile("ztys\\.+waruanzy\\.+com/player/\\?url="), + Pattern.compile("yingshi\\.+waruanzy\\.+com/789pan/\\?url="), + Pattern.compile("vip\\.+parwix\\.+com:4433/player/\\?url="), + Pattern.compile("api\\.+cxitco\\.+cn"), + Pattern.compile("/vip\\.+renrenmi.cc"), + Pattern.compile("yanbing\\.+parwix\\.+com:4433/player"), + Pattern.compile("json\\.+cantin\\.+cc/apijson\\.php"), + Pattern.compile("ffdm\\.+miaoletv\\.+com/\\?url="), + Pattern.compile("vip\\.+sylwl\\.+cn/api/\\?key="), + Pattern.compile("jx\\.+dikotv\\.+com/\\?url="), + Pattern.compile("zly\\.+xjqxz\\.+top/player/\\?url="), + Pattern.compile("5znn\\.+xyz/m3u8\\.+php"), + Pattern.compile("uid=1735&my="), + Pattern.compile("api\\.+xkvideo\\.+design/m3u8\\.+php\\?url="), + Pattern.compile("play\\.+szbodankyy\\.+com/xxoocnmb"), + Pattern.compile("vip\\.+fj6080\\.+xyz/player/\\?url="), + Pattern.compile("a\\.+dxzj88\\.+com/jiexi"), + Pattern.compile("host\\.+q-q\\.+wang/api"), + Pattern.compile("保佑") + }; + + private static final Pattern[] htmlVideoKeyMatch = new Pattern[]{ + Pattern.compile("player=new"), + Pattern.compile("

typeExtendKeys = typeExtend.keys(); + while (typeExtendKeys.hasNext()) { + String key = typeExtendKeys.next(); + if (key.equals("class") || key.equals("area") || key.equals("lang") || key.equals("year")) { + try { + str = str + "筛选" + key + "+全部=+" + typeExtend.getString(key).replace(",", "+") + "\n"; + } catch (JSONException e) { + } + } + } + } + if (URL.contains(".vod")) { + str += "\n" + "排序+全部=+最新=time+最热=hits+评分=score"; + } else if (URL.contains("api.php/app") || URL.contains("xgapp")) { + } else { + str = "分类+全部=+电影=movie+连续剧=tvplay+综艺=tvshow+动漫=comic+4K=movie_4k+体育=tiyu\n筛选class+全部=+喜剧+爱情+恐怖+动作+科幻+剧情+战争+警匪+犯罪+动画+奇幻+武侠+冒险+枪战+恐怖+悬疑+惊悚+经典+青春+文艺+微电影+古装+历史+运动+农村+惊悚+惊悚+伦理+情色+福利+三级+儿童+网络电影\n筛选area+全部=+大陆+香港+台湾+美国+英国+法国+日本+韩国+德国+泰国+印度+西班牙+加拿大+其他\n筛选year+全部=+2022+2021+2020+2019+2018+2017+2016+2015+2014+2013+2012+2011+2010+2009+2008+2007+2006+2005+2004+2003+2002+2001+2000"; + } + return str; + } + + // ######推荐地址 + String getRecommendUrl(String URL) { + if (URL.contains("api.php/app") || URL.contains("xgapp")) { + return URL + "index_video?token="; + } else if (URL.contains(".vod")) { + return URL + "/vodPhbAll"; + } else { + return ""; + } + } + + // ######播放器前缀地址 + String getPlayUrlPrefix(String URL) { + if (URL.contains("api.php/app") || URL.contains("xgapp")) { + if (URL.contains("dijiaxia")) { + URL = "https://www.dijiaxia.com/api.php/app/"; + return URL + "video_detail?id="; + } else if (URL.contains("1010dy")) { + URL = "http://www.1010dy.cc/api.php/app/"; + return URL + "video_detail?id="; + } else { + return URL + "video_detail?id="; + } + } else if (URL.contains(".vod")) { + if (URL.contains("iopenyun")) { + return URL + "/detailID?vod_id="; + } else { + return URL + "/detail?vod_id="; + } + } else { + return ""; + } + } + + // ######选集 + private final HashMap parseUrlMap = new HashMap<>(); + + private void genPlayList(String URL, JSONObject object, String json, JSONObject vod, String vid) throws JSONException { + ArrayList playUrls = new ArrayList<>(); + ArrayList playFlags = new ArrayList<>(); + if (URL.contains("api.php/app/")) { + JSONObject data = object.getJSONObject("data"); + vod.put("vod_id", data.optString("vod_id", vid)); + vod.put("vod_name", data.getString("vod_name")); + vod.put("vod_pic", data.getString("vod_pic")); + vod.put("type_name", data.optString("vod_class")); + vod.put("vod_year", data.optString("vod_year")); + vod.put("vod_area", data.optString("vod_area")); + vod.put("vod_remarks", data.optString("vod_remarks")); + vod.put("vod_actor", data.optString("vod_actor")); + vod.put("vod_director", data.optString("vod_director")); + vod.put("vod_content", data.optString("vod_content")); + JSONArray vodUrlWithPlayer = data.getJSONArray("vod_url_with_player"); + for (int i = 0; i < vodUrlWithPlayer.length(); i++) { + JSONObject from = vodUrlWithPlayer.getJSONObject(i); + String flag = stopVipFlag(from.getString("name")); + playFlags.add(flag); + playUrls.add(from.getString("url")); + String purl = from.optString("parse_api"); + if (purl.contains("jpg.hou.lu/jm/za/index.php")) { + purl = "http://vip.mengx.vip/home/api?type=ys&uid=3249696&key=aefqrtuwxyEFHKNOQY&url="; + } + parseUrlMap.put(flag, purl); + } + } else if (URL.contains("xgapp")) { + JSONObject data = object.getJSONObject("data").getJSONObject("vod_info"); + vod.put("vod_id", data.optString("vod_id", vid)); + vod.put("vod_name", data.getString("vod_name")); + vod.put("vod_pic", data.getString("vod_pic")); + vod.put("type_name", data.optString("vod_class")); + vod.put("vod_year", data.optString("vod_year")); + vod.put("vod_area", data.optString("vod_area")); + vod.put("vod_remarks", data.optString("vod_remarks")); + vod.put("vod_actor", data.optString("vod_actor")); + vod.put("vod_director", data.optString("vod_director")); + vod.put("vod_content", data.optString("vod_content")); + JSONArray vodUrlWithPlayer = data.getJSONArray("vod_url_with_player"); + for (int i = 0; i < vodUrlWithPlayer.length(); i++) { + JSONObject from = vodUrlWithPlayer.getJSONObject(i); + String flag = stopVipFlag(from.getString("name")); + playFlags.add(flag); + playUrls.add(from.getString("url")); + String purl = from.optString("parse_api"); + if (purl.contains("jpg.hou.lu/jm/za/index.php")) { + purl = "http://vip.mengx.vip/home/api?type=ys&uid=3249696&key=aefqrtuwxyEFHKNOQY&url="; + } + parseUrlMap.put(flag, purl); + } + } else if (/*urlPattern2.matcher(URL).find()*/URL.contains(".vod")) { + JSONObject data = object.getJSONObject("data"); + vod.put("vod_id", data.optString("vod_id", vid)); + vod.put("vod_name", data.getString("vod_name")); + vod.put("vod_pic", data.getString("vod_pic")); + vod.put("type_name", data.optString("vod_class")); + vod.put("vod_year", data.optString("vod_year")); + vod.put("vod_area", data.optString("vod_area")); + vod.put("vod_remarks", data.optString("vod_remarks")); + vod.put("vod_actor", data.optString("vod_actor")); + vod.put("vod_director", data.optString("vod_director")); + vod.put("vod_content", data.optString("vod_content")); + JSONArray vodUrlWithPlayer = data.getJSONArray("vod_play_list"); + for (int i = 0; i < vodUrlWithPlayer.length(); i++) { + JSONObject from = vodUrlWithPlayer.getJSONObject(i); + String flag = stopVipFlag(from.getJSONObject("player_info").getString("show")); + playFlags.add(flag); + playUrls.add(from.getString("url")); + try { + ArrayList parses = new ArrayList<>(); + String[] parse1 = from.getJSONObject("player_info").optString("parse").split(","); + String[] parse2 = from.getJSONObject("player_info").optString("parse2").split(","); + for (String p : parse1) { + if (parsePattern.matcher(p).find()) { + boolean add = true; + for (Pattern pt : parseMatchForDotVod) { + if (pt.matcher(p).find()) { + add = false; + break; + } + } + if (add) { + parses.add(p); + } + } + } + for (String p : parse2) { + if (parsePattern.matcher(p).find()) { + boolean add = true; + for (Pattern pt : parseMatchForDotVod) { + if (pt.matcher(p).find()) { + add = false; + break; + } + } + if (add) { + parses.add(p); + } + } + } + if (parses.size() > 0 && parses.get(0).contains("=")) { + String purl = parses.get(0); + if (purl.contains("http")) { + Matcher matcher = parsePattern1.matcher(purl); + if (matcher.find()) { + purl = matcher.group(0).replace("..", ".").replace("vip.aotian.love", "vip.gaotian.love"); + } + } else if (purl.contains("//")) { + Matcher matcher = parsePattern1.matcher(purl); + if (matcher.find()) { + purl = "http:" + matcher.group(0).replace("..", "."); + } + } else { + Matcher matcher = parsePattern2.matcher(URL); + if (matcher.find()) { + Matcher matcher1 = parsePattern1.matcher(URL); + if (matcher1.find()) { + purl = matcher.group(0) + matcher1.group(0).replace("..", "."); + } + } + } + parseUrlMap.put(flag, purl); + } else { + parseUrlMap.put(flag, "http://1.117.152.239:39000/?url="); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + } + } else if (urlPattern1.matcher(URL).find()) { + JSONObject data = object; + vod.put("vod_id", data.optString("vod_id", vid)); + vod.put("vod_name", data.getString("title")); + vod.put("vod_pic", data.getString("img_url")); + vod.put("type_name", jsonArr2Str(data.optJSONArray("type"))); + vod.put("vod_year", data.optString("pubtime")); + vod.put("vod_area", jsonArr2Str(data.optJSONArray("area"))); + vod.put("vod_remarks", data.optString("trunk")); + vod.put("vod_actor", jsonArr2Str(data.optJSONArray("actor"))); + vod.put("vod_director", jsonArr2Str(data.optJSONArray("director"))); + vod.put("vod_content", data.optString("intro")); + JSONObject playList = data.getJSONObject("videolist"); + Iterator playListKeys = playList.keys(); + while (playListKeys.hasNext()) { + String from = playListKeys.next(); + JSONArray playListUrls = playList.getJSONArray(from); + ArrayList urls = new ArrayList<>(); + for (int j = 0; j < playListUrls.length(); j++) { + JSONObject urlObj = playListUrls.getJSONObject(j); + urls.add(urlObj.getString("title") + "$" + urlObj.getString("url")); + } + playFlags.add(stopVipFlag(from)); + playUrls.add(TextUtils.join("#", urls)); + } + } +// for (int i = 0; i < playFlags.size(); i++) { +// String pu = playUrls.get(i).split("#")[0].split("\\$")[1]; +// System.out.println(pu + " is video?" + isVideoFormat(pu)); +// playerContent(playFlags.get(i), pu, new ArrayList<>()); +// } + vod.put("vod_play_from", TextUtils.join("$$$", playFlags)); + vod.put("vod_play_url", TextUtils.join("$$$", playUrls)); + } + + private String getParseUrl(String URL, String flag) { + String parseUrl = ""; + if (urlPattern2.matcher(URL).find()) { + if (parseUrlMap.containsKey(flag)) + parseUrl = parseUrlMap.get(flag); + } else if (URL.contains("api.php/app/") || URL.contains("xgapp")) { + if (parseUrlMap.containsKey(flag)) + parseUrl = parseUrlMap.get(flag); + } else { + parseUrl = URL; + } + if (parseUrl.contains("svip.jhyun.jx.cn") || parseUrl.contains("svip.jhdyw.vip")) { + if (flag.contains("人人迷")) { + parseUrl = "http://www.1080kan.cc/jiexi/rrmi.php?url="; + } else if (flag.contains("人人")) { + parseUrl = "http://www.1080kan.cc/jiexi/rr.php?url="; + } else if (flag.contains("番茄")) { + parseUrl = "http://www.1080kan.cc/jiexi/fq.php?url="; + } else { + parseUrl = "https://jx.parwix.com:4433/player/?url="; + } + } else if (parseUrl.contains("jhsj.manduhu.com") || parseUrl.contains("v.jhdyw.vip/nhdz666")) { + parseUrl = "https://jx.parwix.com:4433/player/?url="; + } else if (parseUrl.contains("x-n.cc")) { + parseUrl = "https://jx.parwix.com:4433/player/?url="; + } + return parseUrl; + } + + private String getPlayerUrl(String URL, String parseUrl, String playUrl) { + if (URL.contains("xgapp") || URL.contains("api.php/app/") || /*urlPattern2.matcher(URL).find()*/URL.contains(".vod")) { + if (playUrl.indexOf(".m3u8") > 15 || playUrl.indexOf(".mp4") > 15 || playUrl.contains("/obj/tos")) { + if (playUrl.contains("url=")) { + return "https://www.baidu.com/s?wd=" + playUrl.split("url=")[1]; + } else { + return "https://www.baidu.com/s?wd=" + playUrl; + } + } else if (!parseUrl.contains("url=") && playUrl.contains("RongXingVR")) { + return "https://www.baidu.com/s?wd=https://fast.rongxingvr.cn:8866/api/?key=nShWumGdMIbTwngTbI&url=" + playUrl; + } else if (!parseUrl.contains("url=") && playUrl.contains("LT")) { + return "https://www.baidu.com/s?wd=https://f7.pyxddc.com/bcjx/4k.php?url=" + playUrl; + } else if (!parseUrl.contains("url=") && playUrl.contains("renrenmi")) { + return "https://www.baidu.com/s?wd=https://kuba.renrenmi.cc:2266/api/?key=Y6UYLYtjImTCKe98JD&url=" + playUrl; + } else if (!parseUrl.contains("url=") && playUrl.contains(".html")) { + return "https://www.baidu.com/s?wd=http://1.117.152.239:39000/?url=" + playUrl; + } else if (playUrl.contains("xfy")) { + return "https://www.baidu.com/s?wd=http://jiexi.yunl.cc/api/?key=xYNESYSvHp1DV2ckKs&url=" + playUrl; + } else { + return "https://www.baidu.com/s?wd=" + parseUrl + playUrl; + } + } else if (urlPattern1.matcher(URL).find()) { + if (playUrl.indexOf(".m3u8") > 15 || playUrl.indexOf(".mp4") > 15 || playUrl.contains("/obj/tos")) { + if (playUrl.contains("url=")) { + return "https://www.baidu.com/s?wd=" + playUrl.split("url=")[1]; + } else { + return "https://www.baidu.com/s?wd=" + playUrl; + } + } else if (playUrl.contains("xfy")) { + if (playUrl.contains("url=")) { + return "https://www.baidu.com/s?wd=http://cache.dmtt.xyz/xfyjx/xfyjx.php?url=" + playUrl.split("url=")[1]; + } else { + return "https://www.baidu.com/s?wd=https://json.hfyrw.com/mao.go?url=" + playUrl; + } + } else if (playUrl.contains("www.bilibili.com")) { + String spUrl = playUrl.split("url=")[1]; + return "https://www.baidu.com/s?wd=https://jx.parwix.com:4433/player/?url=" + spUrl; + } else if (URL.contains("zhenfy") || URL.contains("cztv") || URL.contains("1.14.63.101") || URL.contains("fit:8") || URL.contains("diliktv.xyz") || URL.contains("ppzhu.vip") || URL.contains("api.8d8q.com") || URL.contains("haokanju1.cc") || URL.contains("cztv")) { + return "https://www.baidu.com/s?wd=" + playUrl + "&app=10000&account=272775028&password=qq272775028"; + } else if (URL.contains("lxyyy") || URL.contains("j.zjj.life") || URL.contains("lktv") || URL.contains("0818tv") || URL.contains("ruoxinew")) { + return "https://www.baidu.com/s?wd=https://play.tkys.tv/?url=" + playUrl.split("url=")[1]; + } else { + return "https://www.baidu.com/s?wd=" + playUrl; + } + } else { + return "https://www.baidu.com/s?wd=" + playUrl; + } + } + + // ######视频地址 + private void getFinalVideo(String uu, JSONObject result) throws JSONException { + if (uu.contains("baidu.com")) { + String playurl = uu.split("wd=")[1]; + if (playurl.contains("duoduozy.com") || playurl.contains("suoyo.cc")) { + String uuu = "https://www.6080kan.cc/app.php?url=" + playurl; + String content = OkHttpUtil.string(uuu, null); + JSONObject obj = new JSONObject(content); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", obj.getString("url")); + result.put("header", "{\"User-Agent\":\" Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36 SE 2.X MetaSr 1.0\",\"referer\":\" https://player.duoduozy.com\",\"origin\":\" https://dp.duoduozy.com\",\"Host\":\" cache.m3u8.suoyo.cc\"}"); + } else if (playurl.contains("xfy")) { + String content = OkHttpUtil.string(playurl, null); + JSONObject obj = new JSONObject(content); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", obj.getString("url")); + result.put("header", "{\"referer\":\" appguapi.lihaoyun.top:11543\",\"User-Agent\":\" Dart/2.14 (dart:io)\"}}"); + } else if (playurl.contains("api.iopenyun.com:88")) { + if (playurl.contains("html")) { + String content = OkHttpUtil.string("https://api.m3u8.tv:5678/home/api?type=ys&uid=233711&key=dgilouvFKNRSWX2467&url=" + playurl.split("=")[1], null); + JSONObject obj = new JSONObject(content); + String ppurl = obj.getString("url"); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", ppurl); + } else { + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", playurl); + result.put("ua", "Mozilla/5.0 Windows10"); + result.put("header", "{\"Referer\":\"https://user.iopenyun.com:520/user/video/\"}"); + } + } else if (playurl.contains("cat.wkfile.com")) { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", playurl); + result.put("header", "{\"User-Agent\":\" Lavf/58.12.100\",\"Referer\":\" wkfile.com\"}"); + } else if (!playurl.contains("=") && playurl.indexOf(".m3u8") > 15 || playurl.indexOf(".mp4") > 15 || playurl.contains("/obj/tos")) { + if (playurl.contains("hsl.ysgc.xyz")) { + String content = OkHttpUtil.string("https://jx.ysgc.xyz/?url=" + playurl, null); + JSONObject obj = new JSONObject(content); + String ppurl = obj.getString("url"); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", ppurl); + result.put("header", "{\"Referer\":\" https://ysgc.cc\"}"); + } else if (playurl.contains("1.ruifenglb.com")) { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", playurl); + result.put("header", "{\"Referer\":\" https://1.ruifenglb.com\"}"); + } else { + Matcher matcher = Pattern.compile(".*(http.*)").matcher(playurl); + if (matcher.find()) { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", matcher.group(1)); + } else { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", playurl); + } + } + } else if (playurl.contains("=")) { + HashMap headers = new HashMap(); + headers.put("User-Agent", "Mozilla/5.0 Android"); + Map> respHeaders = new TreeMap<>(); + String content = OkHttpUtil.stringNoRedirect(playurl, headers, respHeaders); + String redLoc = OkHttpUtil.getRedirectLocation(respHeaders); + if (redLoc != null) { + String finalurl = ""; + while (redLoc != null) { + finalurl = redLoc; + if (redLoc.indexOf(".mp4") > 30) { + break; + } else { + headers.put("User-Agent", "Mozilla/5.0 Android"); + content = OkHttpUtil.stringNoRedirect(finalurl, headers, respHeaders); + redLoc = OkHttpUtil.getRedirectLocation(respHeaders); + } + } + String realurl = finalurl; + if (realurl.contains("=http") || realurl.contains("url=")) { + if (content.contains(" 1) { + String realurl = jsonUrl; + if (playurl.contains("mgtv.com")) { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", realurl); + result.put("header", "{\"User-Agent\":\" Mozilla/5.0\", \"Referer\":\" \"}"); + } else if (playurl.contains("bilibili.com")) { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", realurl); + } else { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", realurl); + } + } else if (playurl.split("url=")[1].contains("http")) { + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", "http://1.117.152.239:39000/?url=" + playurl.split("url=")[1]); + } else if (playurl.split("url=")[1].contains("renrenmi")) { + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", "http://www.1080kan.cc/jiexi/rrmi.php?url=" + playurl.split("url=")[1]); + result.put("header", "{\"Referer\":\"http://www.1080kan.cc/\"}"); + } else { + String id = playurl.split("url=")[1]; + /* + String uuu = "https://vip.gaotian.love/api/?key=sRy0QAq8hqXRlrEtrq&url=" + id; + content = OkHttpUtil.string(uuu, null); + JSONObject obj = new JSONObject(content); + String realurl = obj.optString("url", ""); + if (realurl.isEmpty()) + realurl = obj.optString("msg", ""); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", realurl); + */ + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", "https://jx.banyung.xyz:7799/player/?url=" + playurl.split("url=")[1]); + } + } + } + } else { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", playurl); + } + } else { + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", uu); + } + } + + @Override + public boolean manualVideoCheck() { + return true; + } + + @Override + public boolean isVideoFormat(String url) { + return Misc.isVideoFormat(url); + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/AppYsV2.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/AppYsV2.java new file mode 100644 index 0000000..69c41b8 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/AppYsV2.java @@ -0,0 +1,833 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * M浏览器中的App影视 + *

+ * Author: 群友 不负此生 + */ +public class AppYsV2 extends Spider { + + @Override + public void init(Context context, String extend) { + super.init(context, extend); + try { + extInfos = extend.split("###"); + } catch (Throwable th) { + } + } + + @Override + public String homeContent(boolean filter) { + try { + String url = getCateUrl(getApiUrl()); + JSONArray jsonArray = null; + if (!url.isEmpty()) { + SpiderDebug.log(url); + String json = desc(OkHttpUtil.string(url, getHeaders(url)), (byte) 0); + JSONObject obj = new JSONObject(json); + if (obj.has("list") && obj.get("list") instanceof JSONArray) { + jsonArray = obj.getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONObject && obj.getJSONObject("data").has("list") && obj.getJSONObject("data").get("list") instanceof JSONArray) { + jsonArray = obj.getJSONObject("data").getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONArray) { + jsonArray = obj.getJSONArray("data"); + } + } else { // 通过filter列表读分类 + String filterStr = getFilterTypes(url, null); + String[] classes = filterStr.split("\n")[0].split("\\+"); + jsonArray = new JSONArray(); + for (int i = 1; i < classes.length; i++) { + String[] kv = classes[i].trim().split("="); + if (kv.length < 2) + continue; + JSONObject newCls = new JSONObject(); + newCls.put("type_name", kv[0].trim()); + newCls.put("type_id", kv[1].trim()); + jsonArray.put(newCls); + } + } + JSONObject result = new JSONObject(); + JSONArray classes = new JSONArray(); + if (jsonArray != null) { + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jObj = jsonArray.getJSONObject(i); + String typeName = jObj.getString("type_name"); + if (isBan(typeName)) + continue; + String typeId = jObj.getString("type_id"); + JSONObject newCls = new JSONObject(); + newCls.put("type_id", typeId); + newCls.put("type_name", typeName); + JSONObject typeExtend = jObj.optJSONObject("type_extend"); + if (filter) { + String filterStr = getFilterTypes(url, typeExtend); + String[] filters = filterStr.split("\n"); + JSONArray filterArr = new JSONArray(); + for (int k = url.isEmpty() ? 1 : 0; k < filters.length; k++) { + String l = filters[k].trim(); + if (l.isEmpty()) + continue; + String[] oneLine = l.split("\\+"); + String type = oneLine[0].trim(); + String typeN = type; + if (type.contains("筛选")) { + type = type.replace("筛选", ""); + if (type.equals("class")) + typeN = "类型"; + else if (type.equals("area")) + typeN = "地区"; + else if (type.equals("lang")) + typeN = "语言"; + else if (type.equals("year")) + typeN = "年份"; + } + JSONObject jOne = new JSONObject(); + jOne.put("key", type); + jOne.put("name", typeN); + JSONArray valueArr = new JSONArray(); + for (int j = 1; j < oneLine.length; j++) { + JSONObject kvo = new JSONObject(); + String kv = oneLine[j].trim(); + int sp = kv.indexOf("="); + if (sp == -1) { + if (isBan(kv)) + continue; + kvo.put("n", kv); + kvo.put("v", kv); + } else { + String n = kv.substring(0, sp); + if (isBan(n)) + continue; + kvo.put("n", n.trim()); + kvo.put("v", kv.substring(sp + 1).trim()); + } + valueArr.put(kvo); + } + jOne.put("value", valueArr); + filterArr.put(jOne); + } + if (!result.has("filters")) { + result.put("filters", new JSONObject()); + } + result.getJSONObject("filters").put(typeId, filterArr); + } + classes.put(newCls); + } + } + result.put("class", classes); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String homeVideoContent() { + try { + String apiUrl = getApiUrl(); + String url = getRecommendUrl(apiUrl); + boolean isTV = false; + if (url.isEmpty()) { + url = getCateFilterUrlPrefix(apiUrl) + "movie&page=1&area=&type=&start="; + isTV = true; + } + SpiderDebug.log(url); + String json = desc(OkHttpUtil.string(url, getHeaders(url)), (byte) 1); + JSONObject obj = new JSONObject(json); + JSONArray videos = new JSONArray(); + if (isTV) { + JSONArray jsonArray = obj.getJSONArray("data"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("nextlink")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.getString("pic")); + v.put("vod_remarks", vObj.getString("state")); + videos.put(v); + } + } else { + ArrayList arrays = new ArrayList<>(); + findJsonArray(obj, "vlist", arrays); + if (arrays.isEmpty()) { + findJsonArray(obj, "vod_list", arrays); + } + List ids = new ArrayList<>(); + for (JSONArray jsonArray : arrays) { + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + String vid = vObj.getString("vod_id"); + if (ids.contains(vid)) + continue; + ids.add(vid); + JSONObject v = new JSONObject(); + v.put("vod_id", vid); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } + } + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String apiUrl = getApiUrl(); + String url = getCateFilterUrlPrefix(apiUrl) + tid + getCateFilterUrlSuffix(apiUrl); + url = url.replace("#PN#", pg); + url = url.replace("筛选class", (extend != null && extend.containsKey("class")) ? extend.get("class") : ""); + url = url.replace("筛选area", (extend != null && extend.containsKey("area")) ? extend.get("area") : ""); + url = url.replace("筛选lang", (extend != null && extend.containsKey("lang")) ? extend.get("lang") : ""); + url = url.replace("筛选year", (extend != null && extend.containsKey("year")) ? extend.get("year") : ""); + url = url.replace("排序", (extend != null && extend.containsKey("排序")) ? extend.get("排序") : ""); + SpiderDebug.log(url); + String json = desc(OkHttpUtil.string(url, getHeaders(url)), (byte) 2); + JSONObject obj = new JSONObject(json); + int totalPg = Integer.MAX_VALUE; + try { + if (obj.has("totalpage") && obj.get("totalpage") instanceof Integer) { + totalPg = obj.getInt("totalpage"); + } else if (obj.has("pagecount") && obj.get("pagecount") instanceof Integer) { + totalPg = obj.getInt("pagecount"); + } else if (obj.has("data") && obj.get("data") instanceof JSONObject && + (obj.getJSONObject("data").has("total") && obj.getJSONObject("data").get("total") instanceof Integer && + obj.getJSONObject("data").has("limit") && obj.getJSONObject("data").get("limit") instanceof Integer)) { + int limit = obj.getJSONObject("data").getInt("limit"); + int total = obj.getJSONObject("data").getInt("total"); + totalPg = total % limit == 0 ? (total / limit) : (total / limit + 1); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + + JSONArray jsonArray = null; + JSONArray videos = new JSONArray(); + if (obj.has("list") && obj.get("list") instanceof JSONArray) { + jsonArray = obj.getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONObject && obj.getJSONObject("data").has("list") && obj.getJSONObject("data").get("list") instanceof JSONArray) { + jsonArray = obj.getJSONObject("data").getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONArray) { + jsonArray = obj.getJSONArray("data"); + } + if (jsonArray != null) { + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + if (vObj.has("vod_id")) { + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("vod_id")); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } else { + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("nextlink")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.getString("pic")); + v.put("vod_remarks", vObj.getString("state")); + videos.put(v); + } + } + } + JSONObject result = new JSONObject(); + result.put("page", pg); + result.put("pagecount", totalPg); + result.put("limit", 90); + result.put("total", Integer.MAX_VALUE); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String detailContent(List ids) { + try { + String apiUrl = getApiUrl(); + String url = getPlayUrlPrefix(apiUrl) + ids.get(0); + SpiderDebug.log(url); + String json = desc(OkHttpUtil.string(url, getHeaders(url)), (byte) 3); + JSONObject obj = new JSONObject(json); + JSONObject result = new JSONObject(); + JSONObject vod = new JSONObject(); + genPlayList(apiUrl, obj, json, vod, ids.get(0)); + JSONArray list = new JSONArray(); + list.put(vod); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String searchContent(String key, boolean quick) { + try { + String apiUrl = getApiUrl(); + String url = getSearchUrl(apiUrl, URLEncoder.encode(key)); + //System.out.println(url); + String json = desc(OkHttpUtil.string(url, getHeaders(url)), (byte) 5); + JSONObject obj = new JSONObject(json); + JSONArray jsonArray = null; + JSONArray videos = new JSONArray(); + if (obj.has("list") && obj.get("list") instanceof JSONArray) { + jsonArray = obj.getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONObject && obj.getJSONObject("data").has("list") && obj.getJSONObject("data").get("list") instanceof JSONArray) { + jsonArray = obj.getJSONObject("data").getJSONArray("list"); + } else if (obj.has("data") && obj.get("data") instanceof JSONArray) { + jsonArray = obj.getJSONArray("data"); + } + if (jsonArray != null) { + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + if (vObj.has("vod_id")) { + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("vod_id")); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } else { + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("nextlink")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.getString("pic")); + v.put("vod_remarks", vObj.getString("state")); + videos.put(v); + } + } + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + ArrayList parseUrls = parseUrlMap.get(flag); + if (parseUrls == null) + parseUrls = new ArrayList<>(); + if (!parseUrls.isEmpty()) { + JSONObject result = getFinalVideo(flag, parseUrls, id); + if (result != null) + return result.toString(); + } + if (Misc.isVideoFormat(id)) { + JSONObject result = new JSONObject(); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", id); + return result.toString(); + } else { + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("jx", "1"); + result.put("url", id); + return result.toString(); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + private void findJsonArray(JSONObject obj, String match, ArrayList result) { + Iterator keys = obj.keys(); + while (keys.hasNext()) { + String k = keys.next(); + try { + Object o = obj.get(k); + if (k.equals(match) && o instanceof JSONArray) + result.add((JSONArray) o); + if (o instanceof JSONObject) { + findJsonArray((JSONObject) o, match, result); + } else if (o instanceof JSONArray) { + JSONArray array = (JSONArray) o; + for (int i = 0; i < array.length(); i++) { + findJsonArray(array.getJSONObject(i), match, result); + } + } + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + } + + private String jsonArr2Str(JSONArray array) { + try { + ArrayList strings = new ArrayList<>(); + for (int i = 0; i < array.length(); i++) { + strings.add(array.getString(i)); + } + return TextUtils.join(",", strings); + } catch (JSONException e) { + } + return ""; + } + + private HashMap getHeaders(String URL) { + HashMap headers = new HashMap<>(); + headers.put("User-Agent", UA(URL)); + return headers; + } + + private boolean isBan(String key) { + return key.equals("伦理") || key.equals("情色") || key.equals("福利"); + } + + // M 扩展方法 + + // ######重组搜索 + private String getSearchUrl(String URL, String KEY) { + if (URL.contains(".vod")) { + if (URL.contains("iopenyun.com")) { + return URL + "/list?wd=" + KEY + "&page="; + } else { + return URL + "?wd=" + KEY + "&page="; + } + } else if (URL.contains("api.php/app") || URL.contains("xgapp")) { + return URL + "search?text=" + KEY + "&pg="; + } else if (urlPattern1.matcher(URL).find()) { + if (URL.contains("esellauto") + || URL.contains("1.14.63.101") + || URL.contains("zjys") + || URL.contains("dcd") + || URL.contains("lxue") + || URL.contains("weetai.cn") + || URL.contains("haokanju1") + || URL.contains("fit:8") + || URL.contains("zjj.life") + || URL.contains("love9989") + || URL.contains("8d8q") + || URL.contains("lk.pxun") + || URL.contains("hgyx") + || URL.contains("521x5") + || URL.contains("lxyyy") + || URL.contains("0818tv") + || URL.contains("diyoui") + || URL.contains("diliktv") + || URL.contains("ppzhu") + || URL.contains("aitesucai") + || URL.contains("zz.ci") + || URL.contains("chxjon") + || URL.contains("watchmi") + || URL.contains("vipbp") + || URL.contains("bhtv") + || URL.contains("xfykl")) { + return URL + "?ac=list&" + "wd=" + KEY + "&page="; + } else { + return URL + "?ac=list&" + "zm=" + KEY + "&page="; + } + } + return ""; + } + + // ######UA + private static final Pattern urlPattern1 = Pattern.compile("api\\.php/.*?/vod"); + private static final Pattern urlPattern2 = Pattern.compile("api\\.php/.+?\\.vod"); + private static final Pattern parsePattern = Pattern.compile("/.+\\?.+="); + private static final Pattern parsePattern1 = Pattern.compile(".*(url|v|vid|php\\?id)="); + private static final Pattern parsePattern2 = Pattern.compile("https?://[^/]*"); + + private static final Pattern[] htmlVideoKeyMatch = new Pattern[]{ + Pattern.compile("player=new"), + Pattern.compile("

typeExtendKeys = typeExtend.keys(); + while (typeExtendKeys.hasNext()) { + String key = typeExtendKeys.next(); + if (key.equals("class") || key.equals("area") || key.equals("lang") || key.equals("year")) { + try { + str = str + "筛选" + key + "+全部=+" + typeExtend.getString(key).replace(",", "+") + "\n"; + } catch (JSONException e) { + } + } + } + } + if (URL.contains(".vod")) { + str += "\n" + "排序+全部=+最新=time+最热=hits+评分=score"; + } else if (URL.contains("api.php/app") || URL.contains("xgapp")) { + } else { + str = "分类+全部=+电影=movie+连续剧=tvplay+综艺=tvshow+动漫=comic+4K=movie_4k+体育=tiyu\n筛选class+全部=+喜剧+爱情+恐怖+动作+科幻+剧情+战争+警匪+犯罪+动画+奇幻+武侠+冒险+枪战+恐怖+悬疑+惊悚+经典+青春+文艺+微电影+古装+历史+运动+农村+惊悚+惊悚+伦理+情色+福利+三级+儿童+网络电影\n筛选area+全部=+大陆+香港+台湾+美国+英国+法国+日本+韩国+德国+泰国+印度+西班牙+加拿大+其他\n筛选year+全部=+2022+2021+2020+2019+2018+2017+2016+2015+2014+2013+2012+2011+2010+2009+2008+2007+2006+2005+2004+2003+2002+2001+2000"; + } + return str; + } + + // ######推荐地址 + String getRecommendUrl(String URL) { + if (URL.contains("api.php/app") || URL.contains("xgapp")) { + return URL + "index_video?token="; + } else if (URL.contains(".vod")) { + return URL + "/vodPhbAll"; + } else { + return ""; + } + } + + // ######播放器前缀地址 + String getPlayUrlPrefix(String URL) { + if (URL.contains("api.php/app") || URL.contains("xgapp")) { + if (URL.contains("dijiaxia")) { + URL = "https://www.dijiaxia.com/api.php/app/"; + return URL + "video_detail?id="; + } else if (URL.contains("1010dy")) { + URL = "http://www.1010dy.cc/api.php/app/"; + return URL + "video_detail?id="; + } else { + return URL + "video_detail?id="; + } + } else if (URL.contains(".vod")) { + if (URL.contains("iopenyun")) { + return URL + "/detailID?vod_id="; + } else { + return URL + "/detail?vod_id="; + } + } else { + return ""; + } + } + + // ######选集 + private final HashMap> parseUrlMap = new HashMap<>(); + + private void genPlayList(String URL, JSONObject object, String json, JSONObject vod, String vid) throws JSONException { + ArrayList playUrls = new ArrayList<>(); + ArrayList playFlags = new ArrayList<>(); + if (URL.contains("api.php/app/")) { + JSONObject data = object.getJSONObject("data"); + vod.put("vod_id", data.optString("vod_id", vid)); + vod.put("vod_name", data.getString("vod_name")); + vod.put("vod_pic", data.getString("vod_pic")); + vod.put("type_name", data.optString("vod_class")); + vod.put("vod_year", data.optString("vod_year")); + vod.put("vod_area", data.optString("vod_area")); + vod.put("vod_remarks", data.optString("vod_remarks")); + vod.put("vod_actor", data.optString("vod_actor")); + vod.put("vod_director", data.optString("vod_director")); + vod.put("vod_content", data.optString("vod_content")); + JSONArray vodUrlWithPlayer = data.getJSONArray("vod_url_with_player"); + for (int i = 0; i < vodUrlWithPlayer.length(); i++) { + JSONObject from = vodUrlWithPlayer.getJSONObject(i); + String flag = from.optString("code").trim(); + if (flag.isEmpty()) + flag = from.getString("name").trim(); + playFlags.add(flag); + playUrls.add(from.getString("url")); + String purl = from.optString("parse_api").trim(); + ArrayList parseUrls = parseUrlMap.get(flag); + if (parseUrls == null) { + parseUrls = new ArrayList<>(); + parseUrlMap.put(flag, parseUrls); + } + if (!purl.isEmpty() && !parseUrls.contains(purl)) + parseUrls.add(purl); + } + } else if (URL.contains("xgapp")) { + JSONObject data = object.getJSONObject("data").getJSONObject("vod_info"); + vod.put("vod_id", data.optString("vod_id", vid)); + vod.put("vod_name", data.getString("vod_name")); + vod.put("vod_pic", data.getString("vod_pic")); + vod.put("type_name", data.optString("vod_class")); + vod.put("vod_year", data.optString("vod_year")); + vod.put("vod_area", data.optString("vod_area")); + vod.put("vod_remarks", data.optString("vod_remarks")); + vod.put("vod_actor", data.optString("vod_actor")); + vod.put("vod_director", data.optString("vod_director")); + vod.put("vod_content", data.optString("vod_content")); + JSONArray vodUrlWithPlayer = data.getJSONArray("vod_url_with_player"); + for (int i = 0; i < vodUrlWithPlayer.length(); i++) { + JSONObject from = vodUrlWithPlayer.getJSONObject(i); + String flag = from.optString("code").trim(); + if (flag.isEmpty()) + flag = from.getString("name").trim(); + playFlags.add(flag); + playUrls.add(from.getString("url")); + String purl = from.optString("parse_api").trim(); + ArrayList parseUrls = parseUrlMap.get(flag); + if (parseUrls == null) { + parseUrls = new ArrayList<>(); + parseUrlMap.put(flag, parseUrls); + } + if (!purl.isEmpty() && !parseUrls.contains(purl)) + parseUrls.add(purl); + } + } else if (/*urlPattern2.matcher(URL).find()*/URL.contains(".vod")) { + JSONObject data = object.getJSONObject("data"); + vod.put("vod_id", data.optString("vod_id", vid)); + vod.put("vod_name", data.getString("vod_name")); + vod.put("vod_pic", data.getString("vod_pic")); + vod.put("type_name", data.optString("vod_class")); + vod.put("vod_year", data.optString("vod_year")); + vod.put("vod_area", data.optString("vod_area")); + vod.put("vod_remarks", data.optString("vod_remarks")); + vod.put("vod_actor", data.optString("vod_actor")); + vod.put("vod_director", data.optString("vod_director")); + vod.put("vod_content", data.optString("vod_content")); + JSONArray vodUrlWithPlayer = data.getJSONArray("vod_play_list"); + for (int i = 0; i < vodUrlWithPlayer.length(); i++) { + JSONObject from = vodUrlWithPlayer.getJSONObject(i); + String flag = from.getJSONObject("player_info").optString("from").trim(); + if (flag.isEmpty()) + flag = from.getJSONObject("player_info").optString("show").trim(); + playFlags.add(flag); + playUrls.add(from.getString("url")); + try { + ArrayList parses = new ArrayList<>(); + String[] parse1 = from.getJSONObject("player_info").optString("parse").split(","); + String[] parse2 = from.getJSONObject("player_info").optString("parse2").split(","); + parses.addAll(Arrays.asList(parse1)); + parses.addAll(Arrays.asList(parse2)); + ArrayList parseUrls = parseUrlMap.get(flag); + if (parseUrls == null) { + parseUrls = new ArrayList<>(); + parseUrlMap.put(flag, parseUrls); + } + for (String purl : parses) { + if (purl.contains("http")) { + Matcher matcher = parsePattern1.matcher(purl); + if (matcher.find()) { + purl = matcher.group(0); + } + } else if (purl.contains("//")) { + Matcher matcher = parsePattern1.matcher(purl); + if (matcher.find()) { + purl = "http:" + matcher.group(0); + } + } else { + Matcher matcher = parsePattern2.matcher(URL); + if (matcher.find()) { + Matcher matcher1 = parsePattern1.matcher(URL); + if (matcher1.find()) { + purl = matcher.group(0) + matcher1.group(0); + } + } + } + purl = purl.replace("..", ".").trim(); + if (!purl.isEmpty() && !parseUrls.contains(purl)) + parseUrls.add(purl); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + } + } else if (urlPattern1.matcher(URL).find()) { + JSONObject data = object; + vod.put("vod_id", data.optString("vod_id", vid)); + vod.put("vod_name", data.getString("title")); + vod.put("vod_pic", data.getString("img_url")); + vod.put("type_name", jsonArr2Str(data.optJSONArray("type"))); + vod.put("vod_year", data.optString("pubtime")); + vod.put("vod_area", jsonArr2Str(data.optJSONArray("area"))); + vod.put("vod_remarks", data.optString("trunk")); + vod.put("vod_actor", jsonArr2Str(data.optJSONArray("actor"))); + vod.put("vod_director", jsonArr2Str(data.optJSONArray("director"))); + vod.put("vod_content", data.optString("intro")); + JSONObject playList = data.getJSONObject("videolist"); + Iterator playListKeys = playList.keys(); + while (playListKeys.hasNext()) { + String flag = playListKeys.next(); + ArrayList parseUrls = parseUrlMap.get(flag); + if (parseUrls == null) { + parseUrls = new ArrayList<>(); + parseUrlMap.put(flag, parseUrls); + } + JSONArray playListUrls = playList.getJSONArray(flag); + ArrayList urls = new ArrayList<>(); + for (int j = 0; j < playListUrls.length(); j++) { + JSONObject urlObj = playListUrls.getJSONObject(j); + String url = urlObj.getString("url"); + if (url.contains("url=")) { + int spIdx = url.indexOf("url=") + 4; + String pUrl = url.substring(0, spIdx).trim(); + if (!pUrl.isEmpty() && !parseUrls.contains(pUrl)) + parseUrls.add(pUrl); + urls.add(urlObj.getString("title") + "$" + url.substring(spIdx).trim()); + } else { + urls.add(urlObj.getString("title") + "$" + url); + } + } + playFlags.add(flag); + playUrls.add(TextUtils.join("#", urls)); + } + } + vod.put("vod_play_from", TextUtils.join("$$$", playFlags)); + vod.put("vod_play_url", TextUtils.join("$$$", playUrls)); + } + + // ######视频地址 + private JSONObject getFinalVideo(String flag, ArrayList parseUrls, String url) throws JSONException { + String htmlPlayUrl = ""; + for (String parseUrl : parseUrls) { + if (parseUrl.isEmpty() || parseUrl.equals("null")) + continue; + String playUrl = parseUrl + url; + String content = desc(OkHttpUtil.string(playUrl, null), (byte) 4); + JSONObject tryJson = null; + try { + tryJson = Misc.jsonParse(url, content); + } catch (Throwable th) { + + } + if (tryJson != null && tryJson.has("url") && tryJson.has("header")) { + tryJson.put("header", tryJson.getJSONObject("header").toString()); + return tryJson; + } + if (content.contains(" 1) { + String descFlag = extInfos[1]; + if (descFlag.equals("nftv")) { + + } + } + return src; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Auete.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Auete.java new file mode 100644 index 0000000..e51c577 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Auete.java @@ -0,0 +1,533 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Base64; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Auete extends Spider { + private static final String siteUrl = "https://auete.com"; + private static final String siteHost = "auete.com"; + + /** + * 播放源配置 + */ + private JSONObject playerConfig; + /** + * 筛选配置 + */ + private JSONObject filterConfig; + //分类页URL规则 + private Pattern regexCategory = Pattern.compile("/(\\w+)/index.html"); + //详情页URL规则 + private Pattern regexVid = Pattern.compile("/(\\w+/\\w+/\\w+)/"); + //播放页URL规则 + private Pattern regexPlay = Pattern.compile("(/\\w+/\\w+/\\w+/play-\\d+-\\d+.html)"); + //筛选页URL规则 + //private Pattern regexPage = Pattern.compile("/\\w+/\\w+/index(\\d+).html"); + private Pattern regexPage = Pattern.compile("/index(\\d+).html"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"dphd\":{\"sh\":\"云播A线\",\"pu\":\"\",\"sn\":0,\"or\":999},\"cyun\":{\"sh\":\"云播C线\",\"pu\":\"\",\"sn\":0,\"or\":999},\"dbm3u8\":{\"sh\":\"云播D线\",\"pu\":\"\",\"sn\":0,\"or\":999},\"i8i\":{\"sh\":\"云播E线\",\"pu\":\"\",\"sn\":0,\"or\":999},\"m3u8hd\":{\"sh\":\"云播F线\",\"pu\":\"https://auete.com/api/?url=\",\"sn\":1,\"or\":999},\"languang\":{\"sh\":\"云播G线\",\"pu\":\"https://auete.com/api/?url=\",\"sn\":1,\"or\":999},\"hyun\":{\"sh\":\"云播H线\",\"pu\":\"https://auete.com/api/?url=\",\"sn\":1,\"or\":999},\"kyun\":{\"sh\":\"云播K线\",\"pu\":\"https://auete.com/api/?url=\",\"sn\":1,\"or\":999},\"bpyueyu\":{\"sh\":\"云播粤语\",\"pu\":\"\",\"sn\":0,\"or\":999},\"bpguoyu\":{\"sh\":\"云播国语\",\"pu\":\"\",\"sn\":0,\"or\":999}}"); + filterConfig = new JSONObject("{\"Movie\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"喜剧片\",\"v\":\"xjp\"},{\"n\":\"动作片\",\"v\":\"dzp\"},{\"n\":\"爱情片\",\"v\":\"aqp\"},{\"n\":\"科幻片\",\"v\":\"khp\"},{\"n\":\"恐怖片\",\"v\":\"kbp\"},{\"n\":\"惊悚片\",\"v\":\"jsp\"},{\"n\":\"战争片\",\"v\":\"zzp\"},{\"n\":\"剧情片\",\"v\":\"jqp\"}]}],\"Tv\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"美剧\",\"v\":\"oumei\"},{\"n\":\"韩剧\",\"v\":\"hanju\"},{\"n\":\"日剧\",\"v\":\"riju\"},{\"n\":\"泰剧\",\"v\":\"yataiju\"},{\"n\":\"网剧\",\"v\":\"wangju\"},{\"n\":\"台剧\",\"v\":\"taiju\"},{\"n\":\"国产\",\"v\":\"neidi\"},{\"n\":\"港剧\",\"v\":\"tvbgj\"}]}],\"Zy\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国综\",\"v\":\"guozong\"},{\"n\":\"韩综\",\"v\":\"hanzong\"},{\"n\":\"美综\",\"v\":\"meizong\"}]}],\"Dm\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"动画\",\"v\":\"donghua\"},{\"n\":\"日漫\",\"v\":\"riman\"},{\"n\":\"国漫\",\"v\":\"guoman\"},{\"n\":\"美漫\",\"v\":\"meiman\"}]}],\"qita\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"纪录片\",\"v\":\"Jlp\"},{\"n\":\"经典片\",\"v\":\"Jdp\"},{\"n\":\"经典剧\",\"v\":\"Jdj\"},{\"n\":\"网大电影\",\"v\":\"wlp\"},{\"n\":\"国产老电影\",\"v\":\"laodianying\"}]}]}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + Document doc = Jsoup.parse(OkHttpUtil.string(siteUrl, getHeaders(siteUrl))); + // 分类节点 + Elements elements = doc.select("ul[class='navbar-nav mr-auto']> li a"); + JSONArray classes = new JSONArray(); + for (Element ele : elements) { + //分类名 + String name = ele.text(); + boolean show = name.equals("电影") || + name.equals("电视剧") || + name.equals("综艺") || + name.equals("动漫") || + name.equals("其他"); + if (show) { + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + // 把分类的id和名称取出来加到列表里 + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + if (filter) { + result.put("filters", filterConfig); + } + result.put("class", classes); + try { + // 取首页推荐视频列表 + Element homeList = doc.select("ul.threadlist").get(0); + Elements list = homeList.select("li"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst("h2 a").attr("title"); + String cover = vod.selectFirst("a img").attr("src"); + String remark = vod.selectFirst("a button").text(); + Matcher matcher = regexVid.matcher(vod.selectFirst("a").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + // 获取分类数据的url + String url = ""; + try { + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + if (value != null && value.length() != 0 && value != " ") { + url = siteUrl + "/" + tid + "/" + value; + } else { + url = siteUrl + "/" + tid; + } + ; + } + } else { + url = siteUrl + "/" + tid; + } + ; + if (pg.equals("1")) { + url = url + "/index.html"; + } else { + url = url + "/index" + pg + ".html"; + } + //System.out.println(url); + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + // 取页码相关信息 + Elements pageInfo = doc.select("ul.pagination li"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element li = pageInfo.get(i); + Element a = li.selectFirst("a"); + if (a == null) + continue; + String name = a.text(); + //System.out.println("名称"+name); + if (page == -1 && li.hasClass("active")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + //System.out.println("哈哈"+matcher.group(1)); + page = Integer.parseInt(matcher.group(1)); + } else { + Elements list = doc.select("ul.threadlist li"); + if (list.size() > 0) { + page = 1; + } else { + page = 0; + } + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + //System.out.println("尾页" + matcher.group(1)); + pageCount = Integer.parseInt(matcher.group(1)); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + // 取当前分类页的视频列表 + Elements list = doc.select("ul.threadlist li"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst("h2 a").attr("title"); + String cover = vod.selectFirst("a img").attr("src"); + String remark = vod.selectFirst("a button").text(); + Matcher matcher = regexVid.matcher(vod.selectFirst("a").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 20); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 20); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/" + ids.get(0) + "/"; + //System.out.println(url); + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + String cover = doc.selectFirst("div.cover a").attr("href"); + String title = doc.selectFirst("div.cover a").attr("title"); + Elements list1 = doc.select("div.message>p"); + String desc = doc.select("div.message>p").get(list1.size() - 1).text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.message>p"); + for (int i = 0; i < span_text_muted.size() - 2; i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.contains("分类")) { + try { + category = text.text().split(": ")[1]; + } catch (Exception e) { + category = ""; + } + } else if (info.contains("年份")) { + try { + year = text.text().split(": ")[1]; + } catch (Exception e) { + year = ""; + } + } else if (info.contains("地区")) { + try { + area = text.text().split(": ")[1]; + } catch (Exception e) { + area = ""; + } + } else if (info.contains("影片备注")) { + try { + remark = text.text().split(": ")[1]; + } catch (Exception e) { + remark = ""; + } + } else if (info.contains("导演")) { + try { + director = text.text().split(": ")[1]; + } catch (Exception e) { + director = ""; + } + } else if (info.contains("主演")) { + try { + actor = text.text().split(": ")[1]; + } catch (Exception e) { + actor = ""; + } + } + } + + vodList.put("vod_id", ids.get(0)); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + //System.out.println(vodList.toString()); + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + // 取播放列表数据 + Elements sources = doc.select("div#player_list>h2"); + //System.out.println(sources.size()); + Elements sourceList = doc.select("div#player_list>ul"); + //System.out.println(sourceList.size()); + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + //System.out.println(sources.text().split(":")[0].split("』")[1]); + String sourceName = source.text().split("』")[1].split(":")[0]; + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = sourceList.get(i).select("li > a"); + //System.out.println(playListA.size()); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + // 播放页 url + String url = siteUrl + id; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + /*取得script下面的JS变量*/ + Elements e = doc.select("div>script"); + /*循环遍历script下面的JS变量*/ + String player = ""; + String pn = ""; + JSONObject result = new JSONObject(); + for (Element element : e) { + /*取得JS变量数组*/ + String[] data = element.data().toString().split("var"); + /*取得单个JS变量*/ + for (String variable : data) { + /*过滤variable为空的数据*/ + if (variable.contains("=")) { + /*取到满足条件的JS变量*/ + if (variable.contains("now")) { + String[] kvp = variable.split("="); + player = kvp[1].replaceAll("\"", "").replaceAll(";", ""); + if (player.startsWith("base64")) { + String[] plist1 = player.split("\\("); + String[] plist2 = plist1[1].split("\\)"); + // 解码 + player = new String(Base64.decode(plist2[0].getBytes(), Base64.DEFAULT)); + //byte[] base64Data = Base64.getDecoder().decode(plist2[0]); + //player = new String(base64Data, "utf-8"); + } + + if (!player.startsWith("http")) { + player = siteUrl + player; + } + } + if (variable.contains("pn")) { + String[] kvp = variable.split("="); + pn = kvp[1].replaceAll("\"", "").replaceAll(";", ""); + } + } + } + if (playerConfig.has(pn)) { + JSONObject pCfg = playerConfig.getJSONObject(pn); + String videoUrl = player; + String playUrl = pCfg.getString("pu"); + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", ""); + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 搜索 + * + * @param key + * @param quick 是否播放页的快捷搜索 + * @return + */ + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + String url = siteUrl + "/search.php?searchword=" + URLEncoder.encode(key); + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + + JSONArray videos = new JSONArray(); + Elements list = doc.select("div.card-body>ul>li.media"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.select("div.media-body>div.subject>a>span").text(); + String cover = ""; + String remark = vod.selectFirst("div.media-body>div.d-flex>div.text-muted>span").text(); + Matcher matcher = regexVid.matcher(vod.select("div.media-body>div.subject>a").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + + // 视频封面 + String vodurl = siteUrl + "/" + id + "/"; + Document voddoc = Jsoup.parse(OkHttpUtil.string(vodurl, getHeaders(vodurl))); + cover = voddoc.selectFirst("div.cover a").attr("href"); + + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Buka.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Buka.java new file mode 100644 index 0000000..328c2ed --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Buka.java @@ -0,0 +1,483 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 真不卡 播放好像还有问题 需要大佬继续修复 + *

+ * Author: 小黄瓜 + */ +public class Buka extends Spider { + private static final String siteUrl = "https://www.zhenbuka3.com"; + private static final String siteHost = "www.zhenbuka3.com"; + private static final String ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"; + + /** + * 播放源配置 + */ + private JSONObject playerConfig; + /** + * 筛选配置 + */ + private JSONObject filterConfig; + + private Pattern regexCategory = Pattern.compile("/vodtype/(\\w+)/"); + private Pattern regexVid = Pattern.compile("/voddetail/(\\w+)/"); + private Pattern regexPlay = Pattern.compile("/vodplay/(\\w+)-(\\d+)-(\\d+)/"); + private Pattern regexPage = Pattern.compile("/vodshow/(\\S+)/"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"qqpic\":{\"sh\":\"不卡超清[推荐]\",\"pu\":\"http://good-vip.mmiyue.com/zhenbuka2/player/index.php?video_id=\",\"sn\":0,\"or\":999},\"niuxyun\":{\"sh\":\"不卡备用[推荐]\",\"pu\":\"http://good-vip.mmiyue.com/jiekou/zbk-bkby/jx.php?id=\",\"sn\":0,\"or\":999},\"vipjx\":{\"sh\":\"不卡备用③[推荐]\",\"pu\":\"https://cq.mmiyue.com/zhenbuka2/player/index.php?url=\",\"sn\":0,\"or\":999},\"bkm3u8\":{\"sh\":\"不卡备用②[推荐]\",\"pu\":\"http://good-vip.mmiyue.com/zhenbuka2/player/index.php?url=//good-vip.mmiyue.com/zhenbuka2/api/dymp4.php?video_id=\",\"sn\":0,\"or\":999},\"cy\":{\"sh\":\"动漫云\",\"pu\":\"https://cq.mmiyue.com/jiekou/zbk-bkby/jx.php?id=\",\"sn\":0,\"or\":999},\"mgtv\":{\"sh\":\"不卡芒果\",\"pu\":\"https://cq.mmiyue.com/zhenbuka2/player/index.php?url=\",\"sn\":0,\"or\":999},\"qiyi\":{\"sh\":\"不卡奇艺\",\"pu\":\"https://cq.mmiyue.com/zhenbuka2/player/index.php?url=\",\"sn\":0,\"or\":999},\"qq\":{\"sh\":\"不卡企鹅\",\"pu\":\"https://cq.mmiyue.com/zhenbuka2/player/index.php?url=\",\"sn\":0,\"or\":999},\"youku\":{\"sh\":\"不卡优酷\",\"pu\":\"https://cq.mmiyue.com/zhenbuka2/player/index.php?url=\",\"sn\":0,\"or\":999},\"dbm3u8\":{\"sh\":\"资源备用[不推荐]\",\"pu\":\"\",\"sn\":0,\"or\":999},\"niuyun\":{\"sh\":\"超清备用\",\"pu\":\"https://api.l32c.cn/danmu/niuyun.php?id=\",\"sn\":0,\"or\":999},\"123kum3u8\":{\"sh\":\"资源备用[不推荐]\",\"pu\":\"\",\"sn\":0,\"or\":999},\"sixpan\":{\"sh\":\"不卡备用②[推荐]\",\"pu\":\"https://cq.mmiyue.com/zhenbuka2/player/index.php?url=//cq.mmiyue.com/m3u8/buka/\",\"sn\":0,\"or\":999},\"qq1\":{\"sh\":\"超清云播\",\"pu\":\"https://api.nixingle.com/zhenbuka/player/index.php?id=\",\"sn\":0,\"or\":999}}"); + filterConfig = new JSONObject("{\"1\":[{\"key\":0,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"动作片\",\"v\":\"6\"},{\"n\":\"喜剧片\",\"v\":\"7\"},{\"n\":\"爱情片\",\"v\":\"8\"},{\"n\":\"科幻片\",\"v\":\"9\"},{\"n\":\"恐怖片\",\"v\":\"10\"},{\"n\":\"剧情片\",\"v\":\"11\"},{\"n\":\"战争片\",\"v\":\"12\"},{\"n\":\"动画片\",\"v\":\"23\"},{\"n\":\"纪录片\",\"v\":\"25\"},{\"n\":\"犯罪片\",\"v\":\"26\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"中国香港\",\"v\":\"中国香港\"},{\"n\":\"中国台湾\",\"v\":\"中国台湾\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"法国\",\"v\":\"法国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"德国\",\"v\":\"德国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"印度\",\"v\":\"印度\"},{\"n\":\"意大利\",\"v\":\"意大利\"},{\"n\":\"西班牙\",\"v\":\"西班牙\"},{\"n\":\"加拿大\",\"v\":\"加拿大\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]}],\"2\":[{\"key\":0,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国产剧\",\"v\":\"13\"},{\"n\":\"港台剧\",\"v\":\"14\"},{\"n\":\"日韩剧\",\"v\":\"15\"},{\"n\":\"欧美剧\",\"v\":\"16\"},{\"n\":\"海外剧\",\"v\":\"20\"},{\"n\":\"纪录片\",\"v\":\"24\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"中国香港\",\"v\":\"中国香港\"},{\"n\":\"中国台湾\",\"v\":\"中国台湾\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"新加坡\",\"v\":\"新加坡\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]}],\"3\":[{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"中国香港\",\"v\":\"中国香港\"},{\"n\":\"中国台湾\",\"v\":\"中国台湾\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"英国\",\"v\":\"英国\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]}],\"4\":[{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"美国\",\"v\":\"美国\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]}]}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", ua); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + Document doc = Jsoup.parse(OkHttpUtil.string(siteUrl, getHeaders(siteUrl))); + // 分类节点 + Elements elements = doc.select("ul.dropdown > li a"); + JSONArray classes = new JSONArray(); + for (Element ele : elements) { + String name = ele.text(); + boolean show = true; + if (filter) { + show = name.equals("电影") || + name.equals("剧集") || + name.equals("动漫") || + name.equals("综艺"); + } + if (show) { + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + if (filter) { + result.put("filters", filterConfig); + } + result.put("class", classes); + try { + Elements list = doc.select("div.stui-pannel_bd ul.stui-vodlist li div.stui-vodlist__box"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + Matcher matcher = regexVid.matcher(vod.selectFirst(".stui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".stui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String[] urlParams = new String[]{"", "", "", "", "", "", "", "", "", "", "", ""}; + urlParams[0] = tid; + urlParams[8] = pg; + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + if (value.trim().length() == 0) + continue; + urlParams[Integer.parseInt(key)] = URLEncoder.encode(value); + } + } + // 获取分类数据的url + String url = siteUrl + "/vodshow/" + TextUtils.join("-", urlParams) + "/"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + Elements pageInfo = doc.select(".stui-page li"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element li = pageInfo.get(i); + Element a = li.selectFirst("a"); + if (a == null) + continue; + String name = a.text(); + if (page == -1 && li.hasClass("active")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + page = 0; + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + Elements list = doc.select("ul.stui-vodlist li div.stui-vodlist__box"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".stui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + Matcher matcher = regexVid.matcher(vod.selectFirst(".stui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 48); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 48); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/voddetail/" + ids.get(0) + "/"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + String cover = doc.selectFirst("a.stui-vodlist__thumb img").attr("data-original"); + String title = doc.selectFirst("div.stui-content__detail h1.title").text(); + String desc = Jsoup.parse(doc.selectFirst("meta[name=description]").attr("content")).text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.stui-content__detail span.text-muted"); + for (int i = 0; i < span_text_muted.size(); i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.equals("类型:")) { + category = text.nextElementSibling().text(); + } else if (info.equals("年份:")) { + year = text.nextSibling().outerHtml().trim(); + } else if (info.equals("地区:")) { + area = text.nextSibling().outerHtml().trim(); + } else if (info.equals("更新:")) { + remark = text.nextSibling().outerHtml().trim(); + } else if (info.equals("导演:")) { + List directors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + directors.add(aa.get(j).text()); + } + director = TextUtils.join(",", directors); + } else if (info.equals("主演:")) { + List actors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + actors.add(aa.get(j).text()); + } + actor = TextUtils.join(",", actors); + } + } + + vodList.put("vod_id", ids.get(0)); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + Elements sources = doc.select("div.playlist"); + + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + String sourceName = source.selectFirst("h3.title").text().trim(); + String tabHref = source.attr("href"); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = source.select("ul.stui-content__playlist > li > a"); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + // 播放页 url + String url = siteUrl + "/vodplay/" + id + "/"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + String playUrl = pCfg.getString("pu"); + String from = player.getString("from"); + if (from.equals("qqpic")) { + // 感谢猫大神的直连解密代码 + HashMap hds = getHeaders(url); + hds.put("Host", "good-vip.mmiyue.com"); + hds.put("Referer", url); + String content1 = OkHttpUtil.string(playUrl + videoUrl, hds); + Document doc1 = Jsoup.parse(content1); + String url2 = "https:" + doc1.selectFirst("iframe#player").attr("src"); + hds.put("Host", "pcc.mmiyue.com"); + hds.put("Referer", "http://good-vip.mmiyue.com/"); + String content2 = OkHttpUtil.string(url2, hds); + String finder = "var id=\""; + start = content2.indexOf(finder) + finder.length(); + end = content2.indexOf('\"', start); + String vvid = content2.substring(start, end); + finder = "var sk=\""; + start = content2.indexOf(finder) + finder.length(); + end = content2.indexOf('\"', start); + String skey = content2.substring(start, end); + finder = "var pt=\""; + start = content2.indexOf(finder) + finder.length(); + end = content2.indexOf('\"', start); + String pt = content2.substring(start, end); + finder = "var ti=\""; + start = content2.indexOf(finder) + finder.length(); + end = content2.indexOf('\"', start); + String time = content2.substring(start, end); + int bkn = 0x195c; + for (int j = 0x0, all = skey.length(); all > j; ++j) { + bkn += ((bkn << 5) + (byte) skey.charAt(j)); + } + bkn = 0x7fffffff & bkn; + int gtk1 = 0x0; + for (int j = 0x0; j < pt.length(); j += 0x4) { + gtk1 += Integer.parseInt(String.format("%c%c%c%c", pt.charAt(j), pt.charAt(j + 0x1), pt.charAt(j + 0x2), pt.charAt(j + 0x3)), 0x10); + gtk1 %= 0x400a; + } + int offset = gtk1 % 10; + int gtk = 0x0; + for (int j = 0x0; j < pt.length(); j++) { + gtk += ((byte) pt.charAt(j)) * (j + offset); + gtk %= gtk1; + } + String finalVideoUrl = "https://pcc.mmiyue.com/hls/play/" + vvid + "%7C" + time + "%7C" + skey + "%7C" + pt + "%7C" + bkn + "%7C" + gtk + ".m3u8"; + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", ""); + result.put("url", finalVideoUrl); + result.put("header", ""); + } else { + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", ""); + } + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 搜索 + * + * @param key + * @param quick 是否播放页的快捷搜索 + * @return + */ + @Override + public String searchContent(String key, boolean quick) { + // 搜索有验证码 不支持 + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Cokemv.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Cokemv.java new file mode 100644 index 0000000..23afa72 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Cokemv.java @@ -0,0 +1,492 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Cokemv extends Spider { + private static final String siteUrl = "https://cokemv.me"; + private static final String siteHost = "cokemv.me"; + + /** + * 播放源配置 + */ + private JSONObject playerConfig; + /** + * 筛选配置 + */ + private JSONObject filterConfig; + private Pattern regexCategory = Pattern.compile("/vodtype/(\\d+).html"); + private Pattern regexVid = Pattern.compile("/voddetail/(\\d+).html"); + private Pattern regexPlay = Pattern.compile("/vodplay/(\\d+)-(\\d+)-(\\d+).html"); + private Pattern regexPage = Pattern.compile("/vodshow/(\\S+).html"); + + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"app\":{\"sh\":\"APP\",\"pu\":\"\",\"sn\":0,\"or\":999},\"cokeqie01\":{\"sh\":\"極速路線\",\"pu\":\"\",\"sn\":0,\"or\":999},\"xin\":{\"sh\":\"高速路線\",\"pu\":\"\",\"sn\":0,\"or\":999},\"90mm\":{\"sh\":\"藍光號路\",\"pu\":\"\",\"sn\":0,\"or\":999},\"age01\":{\"sh\":\"動漫一線\",\"pu\":\"\",\"sn\":0,\"or\":999},\"age02\":{\"sh\":\"動漫二線\",\"pu\":\"\",\"sn\":0,\"or\":999},\"mahua\":{\"sh\":\"采集路線\",\"pu\":\"\",\"sn\":0,\"or\":999},\"toutiao\":{\"sh\":\"蓝光不卡\",\"pu\":\"\",\"sn\":0,\"or\":999}}"); + filterConfig = new JSONObject("{\"5\":[{\"key\":3,\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"喜剧\",\"v\":\"喜劇\"},{\"n\":\"爱情\",\"v\":\"愛情\"},{\"n\":\"恐怖\",\"v\":\"恐怖\"},{\"n\":\"动作\",\"v\":\"動作\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"剧情\",\"v\":\"劇情\"},{\"n\":\"战争\",\"v\":\"戰爭\"},{\"n\":\"犯罪\",\"v\":\"犯罪\"},{\"n\":\"动画\",\"v\":\"動畫\"},{\"n\":\"奇幻\",\"v\":\"奇幻\"},{\"n\":\"悬疑\",\"v\":\"懸疑\"},{\"n\":\"微电影\",\"v\":\"微電影\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"國語\"},{\"n\":\"英语\",\"v\":\"英語\"},{\"n\":\"粤语\",\"v\":\"粵語\"},{\"n\":\"闽南语\",\"v\":\"閩南語\"},{\"n\":\"韩语\",\"v\":\"韓語\"},{\"n\":\"日语\",\"v\":\"日語\"},{\"n\":\"法语\",\"v\":\"法語\"},{\"n\":\"德语\",\"v\":\"德語\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":5,\"name\":\"字母\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"A\",\"v\":\"A\"},{\"n\":\"B\",\"v\":\"B\"},{\"n\":\"C\",\"v\":\"C\"},{\"n\":\"D\",\"v\":\"D\"},{\"n\":\"E\",\"v\":\"E\"},{\"n\":\"F\",\"v\":\"F\"},{\"n\":\"G\",\"v\":\"G\"},{\"n\":\"H\",\"v\":\"H\"},{\"n\":\"I\",\"v\":\"I\"},{\"n\":\"J\",\"v\":\"J\"},{\"n\":\"K\",\"v\":\"K\"},{\"n\":\"L\",\"v\":\"L\"},{\"n\":\"M\",\"v\":\"M\"},{\"n\":\"N\",\"v\":\"N\"},{\"n\":\"O\",\"v\":\"O\"},{\"n\":\"P\",\"v\":\"P\"},{\"n\":\"Q\",\"v\":\"Q\"},{\"n\":\"R\",\"v\":\"R\"},{\"n\":\"S\",\"v\":\"S\"},{\"n\":\"T\",\"v\":\"T\"},{\"n\":\"U\",\"v\":\"U\"},{\"n\":\"V\",\"v\":\"V\"},{\"n\":\"W\",\"v\":\"W\"},{\"n\":\"X\",\"v\":\"X\"},{\"n\":\"Y\",\"v\":\"Y\"},{\"n\":\"Z\",\"v\":\"Z\"},{\"n\":\"0-9\",\"v\":\"0-9\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"2\":[{\"key\":0,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"2\"},{\"n\":\"大陆剧\",\"v\":\"13\"},{\"n\":\"香港剧\",\"v\":\"14\"},{\"n\":\"韩国剧\",\"v\":\"15\"},{\"n\":\"欧美剧\",\"v\":\"16\"},{\"n\":\"日本剧\",\"v\":\"20\"},{\"n\":\"台湾剧\",\"v\":\"21\"},{\"n\":\"泰国剧\",\"v\":\"22\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"中国香港\",\"v\":\"中国香港\"},{\"n\":\"中国台湾\",\"v\":\"中国台湾\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"新加坡\",\"v\":\"新加坡\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":5,\"name\":\"字母\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"A\",\"v\":\"A\"},{\"n\":\"B\",\"v\":\"B\"},{\"n\":\"C\",\"v\":\"C\"},{\"n\":\"D\",\"v\":\"D\"},{\"n\":\"E\",\"v\":\"E\"},{\"n\":\"F\",\"v\":\"F\"},{\"n\":\"G\",\"v\":\"G\"},{\"n\":\"H\",\"v\":\"H\"},{\"n\":\"I\",\"v\":\"I\"},{\"n\":\"J\",\"v\":\"J\"},{\"n\":\"K\",\"v\":\"K\"},{\"n\":\"L\",\"v\":\"L\"},{\"n\":\"M\",\"v\":\"M\"},{\"n\":\"N\",\"v\":\"N\"},{\"n\":\"O\",\"v\":\"O\"},{\"n\":\"P\",\"v\":\"P\"},{\"n\":\"Q\",\"v\":\"Q\"},{\"n\":\"R\",\"v\":\"R\"},{\"n\":\"S\",\"v\":\"S\"},{\"n\":\"T\",\"v\":\"T\"},{\"n\":\"U\",\"v\":\"U\"},{\"n\":\"V\",\"v\":\"V\"},{\"n\":\"W\",\"v\":\"W\"},{\"n\":\"X\",\"v\":\"X\"},{\"n\":\"Y\",\"v\":\"Y\"},{\"n\":\"Z\",\"v\":\"Z\"},{\"n\":\"0-9\",\"v\":\"0-9\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"1\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"1\"},{\"n\":\"动作片\",\"v\":\"6\"},{\"n\":\"喜剧片\",\"v\":\"7\"},{\"n\":\"爱情片\",\"v\":\"8\"},{\"n\":\"科幻片\",\"v\":\"9\"},{\"n\":\"恐怖片\",\"v\":\"10\"},{\"n\":\"剧情片\",\"v\":\"11\"},{\"n\":\"战争片\",\"v\":\"12\"},{\"n\":\"犯罪片\",\"v\":\"23\"},{\"n\":\"奇幻片\",\"v\":\"24\"},{\"n\":\"悬疑片\",\"v\":\"25\"},{\"n\":\"记录片\",\"v\":\"27\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"中国香港\",\"v\":\"中国香港\"},{\"n\":\"中国台湾\",\"v\":\"中国台湾\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"法国\",\"v\":\"法国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"德国\",\"v\":\"德国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"印度\",\"v\":\"印度\"},{\"n\":\"意大利\",\"v\":\"意大利\"},{\"n\":\"西班牙\",\"v\":\"西班牙\"},{\"n\":\"加拿大\",\"v\":\"加拿大\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"法语\",\"v\":\"法语\"},{\"n\":\"德语\",\"v\":\"德语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":5,\"name\":\"字母\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"A\",\"v\":\"A\"},{\"n\":\"B\",\"v\":\"B\"},{\"n\":\"C\",\"v\":\"C\"},{\"n\":\"D\",\"v\":\"D\"},{\"n\":\"E\",\"v\":\"E\"},{\"n\":\"F\",\"v\":\"F\"},{\"n\":\"G\",\"v\":\"G\"},{\"n\":\"H\",\"v\":\"H\"},{\"n\":\"I\",\"v\":\"I\"},{\"n\":\"J\",\"v\":\"J\"},{\"n\":\"K\",\"v\":\"K\"},{\"n\":\"L\",\"v\":\"L\"},{\"n\":\"M\",\"v\":\"M\"},{\"n\":\"N\",\"v\":\"N\"},{\"n\":\"O\",\"v\":\"O\"},{\"n\":\"P\",\"v\":\"P\"},{\"n\":\"Q\",\"v\":\"Q\"},{\"n\":\"R\",\"v\":\"R\"},{\"n\":\"S\",\"v\":\"S\"},{\"n\":\"T\",\"v\":\"T\"},{\"n\":\"U\",\"v\":\"U\"},{\"n\":\"V\",\"v\":\"V\"},{\"n\":\"W\",\"v\":\"W\"},{\"n\":\"X\",\"v\":\"X\"},{\"n\":\"Y\",\"v\":\"Y\"},{\"n\":\"Z\",\"v\":\"Z\"},{\"n\":\"0-9\",\"v\":\"0-9\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"4\":[{\"key\":0,\"name\":\"分类\",\"value\":[{\"n\":\"全部\",\"v\":\"4\"},{\"n\":\"动画电影\",\"v\":\"41\"}]},{\"key\":3,\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"情感\",\"v\":\"情感\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"热血\",\"v\":\"热血\"},{\"n\":\"推理\",\"v\":\"推理\"},{\"n\":\"搞笑\",\"v\":\"搞笑\"},{\"n\":\"冒险\",\"v\":\"冒险\"},{\"n\":\"萝莉\",\"v\":\"萝莉\"},{\"n\":\"校园\",\"v\":\"校园\"},{\"n\":\"动作\",\"v\":\"动作\"},{\"n\":\"机战\",\"v\":\"机战\"},{\"n\":\"运动\",\"v\":\"运动\"},{\"n\":\"战争\",\"v\":\"战争\"},{\"n\":\"少年\",\"v\":\"少年\"},{\"n\":\"少女\",\"v\":\"少女\"},{\"n\":\"社会\",\"v\":\"社会\"},{\"n\":\"原创\",\"v\":\"原创\"},{\"n\":\"亲子\",\"v\":\"亲子\"},{\"n\":\"益智\",\"v\":\"益智\"},{\"n\":\"励志\",\"v\":\"励志\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"大陆\",\"v\":\"大陆\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"中国香港\",\"v\":\"中国香港\"},{\"n\":\"中国台湾\",\"v\":\"中国台湾\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"新加坡\",\"v\":\"新加坡\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":5,\"name\":\"字母\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"A\",\"v\":\"A\"},{\"n\":\"B\",\"v\":\"B\"},{\"n\":\"C\",\"v\":\"C\"},{\"n\":\"D\",\"v\":\"D\"},{\"n\":\"E\",\"v\":\"E\"},{\"n\":\"F\",\"v\":\"F\"},{\"n\":\"G\",\"v\":\"G\"},{\"n\":\"H\",\"v\":\"H\"},{\"n\":\"I\",\"v\":\"I\"},{\"n\":\"J\",\"v\":\"J\"},{\"n\":\"K\",\"v\":\"K\"},{\"n\":\"L\",\"v\":\"L\"},{\"n\":\"M\",\"v\":\"M\"},{\"n\":\"N\",\"v\":\"N\"},{\"n\":\"O\",\"v\":\"O\"},{\"n\":\"P\",\"v\":\"P\"},{\"n\":\"Q\",\"v\":\"Q\"},{\"n\":\"R\",\"v\":\"R\"},{\"n\":\"S\",\"v\":\"S\"},{\"n\":\"T\",\"v\":\"T\"},{\"n\":\"U\",\"v\":\"U\"},{\"n\":\"V\",\"v\":\"V\"},{\"n\":\"W\",\"v\":\"W\"},{\"n\":\"X\",\"v\":\"X\"},{\"n\":\"Y\",\"v\":\"Y\"},{\"n\":\"Z\",\"v\":\"Z\"},{\"n\":\"0-9\",\"v\":\"0-9\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"3\":[{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"韩国\",\"v\":\"韩国\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":4,\"name\":\"语言\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语\",\"v\":\"国语\"},{\"n\":\"英语\",\"v\":\"英语\"},{\"n\":\"粤语\",\"v\":\"粤语\"},{\"n\":\"闽南语\",\"v\":\"闽南语\"},{\"n\":\"韩语\",\"v\":\"韩语\"},{\"n\":\"日语\",\"v\":\"日语\"},{\"n\":\"其它\",\"v\":\"其它\"}]},{\"key\":5,\"name\":\"字母\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"A\",\"v\":\"A\"},{\"n\":\"B\",\"v\":\"B\"},{\"n\":\"C\",\"v\":\"C\"},{\"n\":\"D\",\"v\":\"D\"},{\"n\":\"E\",\"v\":\"E\"},{\"n\":\"F\",\"v\":\"F\"},{\"n\":\"G\",\"v\":\"G\"},{\"n\":\"H\",\"v\":\"H\"},{\"n\":\"I\",\"v\":\"I\"},{\"n\":\"J\",\"v\":\"J\"},{\"n\":\"K\",\"v\":\"K\"},{\"n\":\"L\",\"v\":\"L\"},{\"n\":\"M\",\"v\":\"M\"},{\"n\":\"N\",\"v\":\"N\"},{\"n\":\"O\",\"v\":\"O\"},{\"n\":\"P\",\"v\":\"P\"},{\"n\":\"Q\",\"v\":\"Q\"},{\"n\":\"R\",\"v\":\"R\"},{\"n\":\"S\",\"v\":\"S\"},{\"n\":\"T\",\"v\":\"T\"},{\"n\":\"U\",\"v\":\"U\"},{\"n\":\"V\",\"v\":\"V\"},{\"n\":\"W\",\"v\":\"W\"},{\"n\":\"X\",\"v\":\"X\"},{\"n\":\"Y\",\"v\":\"Y\"},{\"n\":\"Z\",\"v\":\"Z\"},{\"n\":\"0-9\",\"v\":\"0-9\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}]}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + Document doc = Jsoup.parse(OkHttpUtil.string(siteUrl, getHeaders(siteUrl))); + // 分类节点 + Elements elements = doc.select("ul.myui-header__menu>li.hidden-sm a"); + JSONArray classes = new JSONArray(); + for (Element ele : elements) { + String name = ele.text(); + boolean show = name.equals("抖音电影") || + name.equals("电视剧") || + name.equals("电影") || + name.equals("动漫") || + name.equals("综艺"); + if (show) { + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + // 把分类的id和名称取出来加到列表里 + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + if (filter) { + result.put("filters", filterConfig); + } + result.put("class", classes); + try { + // 取首页推荐视频列表 + Element homeList = doc.select("ul.myui-vodlist").get(0); + Elements list = homeList.select("div.myui-vodlist__box>a"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.attr("title"); + String cover = vod.attr("data-original"); + String remark = vod.selectFirst("span.pic-tag>span").text(); + Matcher matcher = regexVid.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String[] urlParams = new String[]{"", "", "", "", "", "", "", "", "", "", "", ""}; + urlParams[0] = tid; + urlParams[8] = pg; + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + urlParams[Integer.parseInt(key)] = URLEncoder.encode(value); + } + } + // 获取分类数据的url + String url = siteUrl + "/vodshow/" + TextUtils.join("-", urlParams) + ".html"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + // 取页码相关信息 + Elements pageInfo = doc.select("ul.myui-page>li"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element li = pageInfo.get(i); + Element a = li.selectFirst("a"); + if (a == null) + continue; + String name = a.text(); + if (page == -1 && a.hasClass("btn-warm")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + page = 0; + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + // 取当前分类页的视频列表 + Elements list = doc.select("ul.myui-vodlist>li>div>a"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.attr("title"); + String cover = vod.attr("data-original"); + String remark = vod.selectFirst("span.pic-tag>span").text(); + Matcher matcher = regexVid.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 48); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 48); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/voddetail/" + ids.get(0) + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + Elements sourcediv = doc.select("div.myui-panel-box>div"); + String cover = sourcediv.get(1).select("div.myui-content__thumb>a>img").attr("data-original"); + String title = sourcediv.get(1).select("div.myui-content__thumb>a").attr("title"); + String category = "", area = "", year = "", remark = "", director = "", actor = "", desc = ""; + Elements data = doc.select("p.data"); + category = data.get(0).select("a").get(0).text(); + area = data.get(0).select("a").get(1).text(); + year = data.get(0).select("a").get(2).text(); + remark = data.get(1).select("span").get(1).text(); + List actors = new ArrayList<>(); + Elements aa = data.get(2).select("a"); + for (int j = 0; j < aa.size(); j++) { + actors.add(aa.get(j).text()); + } + actor = TextUtils.join(",", actors); + List directors = new ArrayList<>(); + Elements bb = data.get(3).select("a"); + for (int j = 0; j < bb.size(); j++) { + directors.add(bb.get(j).text()); + } + director = TextUtils.join(",", directors); + desc = doc.selectFirst("div.col-pd>span.data>p").text().trim(); + + vodList.put("vod_id", ids.get(0)); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + // 取播放列表数据 + Elements sources = doc.select("div.myui-panel__head>ul").get(0).select("li"); + Elements sourceList = doc.select("div.tab-content>div.tab-pane"); + + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + String sourceName = source.text(); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = sourceList.get(i).select("ul>li>a"); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + //定义播放用的headers + JSONObject headers = new JSONObject(); + //headers.put("Host", " cokemv.co"); + headers.put("origin", " https://cokemv.me"); + headers.put("User-Agent", " Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", " */*"); + headers.put("Accept-Language", " zh-CN,zh;q=0.9,en-US;q=0.3,en;q=0.7"); + headers.put("Accept-Encoding", " gzip, deflate"); + + + // 播放页 url + String url = siteUrl + "/vodplay/" + id + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { // 取直链 + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + String playUrl = pCfg.getString("pu"); + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", headers.toString()); + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 搜索 + * 搜索有验证码,暂时不会处理 + * + * @param key + * @param quick 是否播放页的快捷搜索 + * @return + */ +// @Override +// public String searchContent(String key, boolean quick) { +// try { +// if (quick) +// return ""; +// String url = siteUrl + "/vodsearch/-------------.html?wd=" + URLEncoder.encode(key) + "&submit="; +// Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); +// JSONObject result = new JSONObject(); +// +// JSONArray videos = new JSONArray(); +// Elements list = doc.selectFirst("ul.myui-vodlist__media").select("li>a"); +// for (int i = 0; i < list.size(); i++) { +// Element vod = list.get(i); +// String title = vod.attr("title"); +// String cover = vod.attr("data-original"); +// String remark = vod.selectFirst("span.pic-tag").text(); +// Matcher matcher = regexVid.matcher(vod.attr("href")); +// if (!matcher.find()) +// continue; +// String id = matcher.group(1); +// JSONObject v = new JSONObject(); +// v.put("vod_id", id); +// v.put("vod_name", title); +// v.put("vod_pic", cover); +// v.put("vod_remarks", remark); +// videos.put(v); +// } +// +// result.put("list", videos); +// return result.toString(); +// } catch (Exception e) { +// SpiderDebug.log(e); +// } +// return ""; +// } +//} + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + long currentTime = System.currentTimeMillis(); + String url = siteUrl + "/index.php/ajax/suggest?mid=1&wd=" + URLEncoder.encode(key) + "&limit=10×tamp=" + currentTime; + JSONObject searchResult = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + if (searchResult.getInt("total") > 0) { + JSONArray lists = new JSONArray(searchResult.getString("list")); + for (int i = 0; i < lists.length(); i++) { + JSONObject vod = lists.getJSONObject(i); + String id = vod.getString("id"); + String title = vod.getString("name"); + String cover = vod.getString("pic"); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", ""); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Enlienli.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Enlienli.java new file mode 100644 index 0000000..a273cd4 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Enlienli.java @@ -0,0 +1,252 @@ +package com.github.catvod.spider; + +import android.content.Context; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +/** + * 嗯哩嗯哩 + *

+ * Author: 群友 不负此生 + */ +public class Enlienli extends Spider { + + private String siteUrl = "https://api.app.kongbuya.com"; + + @Override + public void init(Context context) { + super.init(context); + } + + private HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("Connection", "Keep-Alive"); + headers.put("User-Agent", "okhttp/4.0.1"); + return headers; + } + + @Override + public String homeContent(boolean filter) { + try { + JSONArray jsonArray = new JSONArray(OkHttpUtil.string(siteUrl + "/api.php/provide/home_nav?", getHeaders(siteUrl))); + JSONArray classes = new JSONArray(); + JSONObject filterConfig = new JSONObject(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jObj = jsonArray.getJSONObject(i); + String typeName = jObj.getString("name"); + String typeId = jObj.getString("id"); + if (typeName.equals("推荐")) { + continue; + } + JSONObject newCls = new JSONObject(); + newCls.put("type_id", typeId); + newCls.put("type_name", typeName); + classes.put(newCls); + try { + JSONArray typeExtend = jObj.getJSONArray("msg"); + JSONArray extendsAll = new JSONArray(); + for (int j = 0; j < typeExtend.length(); j++) { + JSONObject typeExtendObj = typeExtend.getJSONObject(j); + String typeExtendKey = typeExtendObj.getString("name"); + JSONArray newTypeExtendKeys = typeExtendObj.getJSONArray("data"); + if (newTypeExtendKeys.length() <= 1) + continue; + String typeExtendName = newTypeExtendKeys.getString(0); + JSONObject newTypeExtend = new JSONObject(); + newTypeExtend.put("key", typeExtendKey); + newTypeExtend.put("name", typeExtendName); + JSONArray newTypeExtendKV = new JSONArray(); + { + JSONObject kvAll = new JSONObject(); + kvAll.put("n", "全部"); + kvAll.put("v", ""); + newTypeExtendKV.put(kvAll); + } + for (int k = 1; k < newTypeExtendKeys.length(); k++) { + JSONObject kv = new JSONObject(); + kv.put("n", newTypeExtendKeys.getString(k)); + kv.put("v", newTypeExtendKeys.getString(k)); + newTypeExtendKV.put(kv); + } + newTypeExtend.put("value", newTypeExtendKV); + extendsAll.put(newTypeExtend); + } + filterConfig.put(typeId, extendsAll); + } catch (Exception e) { + SpiderDebug.log(e); + } + + } + JSONObject result = new JSONObject(); + result.put("class", classes); + if (filter) { + result.put("filters", filterConfig); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String homeVideoContent() { + try { + String url = siteUrl + "/api.php/provide/home_data?page=1&id=0"; + JSONObject jsonObject = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONArray jsonArray = new JSONArray(); + if (jsonObject.has("tv")) { + JSONArray data = jsonObject.getJSONObject("tv").getJSONArray("data"); + for (int i = 0; i < data.length(); i++) { + jsonArray.put(data.getJSONObject(i)); + } + } + + if (jsonObject.has("video")) { + JSONArray vs = jsonObject.getJSONArray("video"); + for (int i = 0; i < vs.length(); i++) { + JSONArray data = vs.getJSONObject(i).getJSONArray("data"); + for (int j = 0; j < data.length(); j++) { + jsonArray.put(data.getJSONObject(j)); + } + } + } + + JSONArray videos = new JSONArray(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("id")); + v.put("vod_name", vObj.getString("name")); + v.put("vod_pic", vObj.getString("img")); + v.put("vod_remarks", vObj.getString("qingxidu")); + videos.put(v); + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String url = siteUrl + "/api.php/provide/vod_list?page=" + pg + "&id=" + tid; + Set keys = extend.keySet(); + for (String key : keys) { + url += "&" + key + "=" + URLEncoder.encode(extend.get(key)); + } + JSONArray jsonArray = new JSONArray(OkHttpUtil.string(url, getHeaders(url))); + JSONArray videos = new JSONArray(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("id")); + v.put("vod_name", vObj.getString("name")); + v.put("vod_pic", vObj.getString("img")); + v.put("vod_remarks", vObj.getString("qingxidu")); + videos.put(v); + } + JSONObject result = new JSONObject(); + int limit = 20; + int page = Integer.parseInt(pg); + int total = Integer.MAX_VALUE; + int pageCount = videos.length() < 20 ? page : page + 1; + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", limit); + result.put("total", total); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String detailContent(List ids) { + try { + String url = siteUrl + "/api.php/provide/vod_detail?token=&id=" + ids.get(0) + "&ac=vod_detail"; + JSONObject jsonObject = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject vodList = new JSONObject(); + vodList.put("vod_id", ids.get(0)); + vodList.put("vod_name", jsonObject.getString("name")); + vodList.put("vod_pic", jsonObject.getString("img")); + vodList.put("type_name", jsonObject.getString("type")); + vodList.put("vod_year", ""); + vodList.put("vod_area", ""); + vodList.put("vod_remarks", jsonObject.getString("remarks")); + vodList.put("vod_actor", jsonObject.getString("actor")); + vodList.put("vod_director", jsonObject.getString("director")); + vodList.put("vod_content", jsonObject.getString("info")); + + vodList.put("vod_play_from", jsonObject.getString("playcode")); + vodList.put("vod_play_url", jsonObject.getString("playlist")); + + JSONObject result = new JSONObject(); + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + JSONObject result = new JSONObject(); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", id); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String searchContent(String key, boolean quick) { + if (quick) + return ""; + try { + String url = siteUrl + "/api.php/provide/search_result?video_name=" + URLEncoder.encode(key); + JSONObject jsonObject = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONArray jsonArray = jsonObject.getJSONObject("result").getJSONArray("search_result"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("id")); + v.put("vod_name", vObj.getString("video_name")); + v.put("vod_pic", vObj.getString("img")); + //v.put("vod_remarks", vObj.getString("")); + videos.put(v); + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Imaple.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Imaple.java new file mode 100644 index 0000000..7779054 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Imaple.java @@ -0,0 +1,465 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Demo for self study + *

+ * Source from Author: CatVod + */ + +public class Imaple extends Spider { + + private static final String siteUrl = "https://imaple.co/"; + private static final String siteHost = "imaple.co"; + + protected JSONObject playerConfig; + protected JSONObject filterConfig; + + protected Pattern regexCategory = Pattern.compile("/type/(\\S+).html"); + protected Pattern regexVid = Pattern.compile("/vod/(\\d+).html"); + protected Pattern regexPlay = Pattern.compile("/play/(\\d+)-(\\d+)-(\\d+).html"); + protected Pattern regexPage = Pattern.compile("\\S+/page/(\\d+)\\S+"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"kbm3u8\":{\"sh\":\"秒播雲\",\"sn\":0,\"pu\":\"\",\"or\":999},\"bjm3u8\":{\"sh\":\"八戒雲\",\"sn\":0,\"pu\":\"\",\"or\":999},\"605m3u8\":{\"sh\":\"光速雲\",\"sn\":0,\"pu\":\"\",\"or\":999},\"mlm3u8\":{\"sh\":\"藍光線一\",\"sn\":0,\"pu\":\"\",\"or\":999},\"alizy\":{\"sh\":\"藍光線三\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"dplayer\":{\"sh\":\"動漫專線\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"qdyun\":{\"sh\":\"驱动云\",\"sn\":0,\"pu\":\"\",\"or\":999},\"wjm3u8\":{\"sh\":\"理想雲\",\"sn\":0,\"pu\":\"\",\"or\":999},\"fanqie\":{\"sh\":\"番茄资源\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"youku\":{\"sh\":\"奇藝雲\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"qq\":{\"sh\":\"騰訊雲\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"mgtv\":{\"sh\":\"芒果雲\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"sohu\":{\"sh\":\"搜狐雲\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"88zym3u8\":{\"sh\":\"優雅雲\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"bilibili\":{\"sh\":\"嗶哩雲\",\"sn\":1,\"pu\":\"https://player.imaple.tv/player/?url=\",\"or\":999},\"dbm3u8\":{\"sh\":\"急速雲\",\"sn\":0,\"pu\":\"\",\"or\":999},\"tkm3u8\":{\"sh\":\"天空雲\",\"sn\":0,\"pu\":\"\",\"or\":999}}"); + filterConfig = new JSONObject("{\"1\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"動作片\",\"v\":\"6\"},{\"n\":\"喜劇片\",\"v\":\"7\"},{\"n\":\"愛情片\",\"v\":\"8\"},{\"n\":\"科幻片\",\"v\":\"9\"},{\"n\":\"恐怖片\",\"v\":\"10\"},{\"n\":\"劇情片\",\"v\":\"11\"},{\"n\":\"戰爭片\",\"v\":\"12\"},{\"n\":\"紀錄片\",\"v\":\"20\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"大陸\",\"v\":\"大陸\"},{\"n\":\"香港\",\"v\":\"香港\"},{\"n\":\"台灣\",\"v\":\"台灣\"},{\"n\":\"美國\",\"v\":\"美國\"},{\"n\":\"法國\",\"v\":\"法國\"},{\"n\":\"英國\",\"v\":\"英國\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韓國\",\"v\":\"韓國\"},{\"n\":\"德國\",\"v\":\"德國\"},{\"n\":\"泰國\",\"v\":\"泰國\"},{\"n\":\"印度\",\"v\":\"印度\"},{\"n\":\"意大利\",\"v\":\"意大利\"},{\"n\":\"西班牙\",\"v\":\"西班牙\"},{\"n\":\"加拿大\",\"v\":\"加拿大\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"2\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"大陸劇\",\"v\":\"13\"},{\"n\":\"港劇\",\"v\":\"14\"},{\"n\":\"台劇\",\"v\":\"15\"},{\"n\":\"日劇\",\"v\":\"16\"},{\"n\":\"韓劇\",\"v\":\"23\"},{\"n\":\"美劇\",\"v\":\"24\"},{\"n\":\"海外劇\",\"v\":\"25\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"3\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"港台綜藝\",\"v\":\"29\"},{\"n\":\"日韓綜藝\",\"v\":\"30\"},{\"n\":\"大陸綜藝\",\"v\":\"31\"},{\"n\":\"歐美綜藝\",\"v\":\"32\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"4\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"港台動漫\",\"v\":\"33\"},{\"n\":\"日韓動漫\",\"v\":\"34\"},{\"n\":\"大陸動漫\",\"v\":\"35\"},{\"n\":\"歐美動漫\",\"v\":\"36\"},{\"n\":\"海外動漫\",\"v\":\"37\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}]}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + String url = siteUrl + '/'; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements elements = doc.select("ul.myui-header__menu>li>a"); + JSONArray classes = new JSONArray(); + ArrayList allClass = new ArrayList<>(); + for (Element ele : elements) { + String name = ele.text(); + boolean show = !filter || (name.equals("電影") || name.equals("電視劇") || name.equals("綜藝") || name.equals("動漫")); + if (allClass.contains(name)) + show = false; + if (show) { + allClass.add(name); + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + // 把分类的id和名称取出来加到列表里 + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + if (filter) { + result.put("filters", filterConfig); + } + result.put("class", classes); + try { + // 取首页推荐视频列表 + Elements list = doc.select("li.col-lg-6 >div.myui-vodlist__box"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".myui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + + Matcher matcher = regexVid.matcher(vod.selectFirst(".myui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String url = siteUrl + "/show/"; + if (extend != null && extend.size() > 0 && extend.containsKey("tid") && extend.get("tid").length() > 0) { + url += extend.get("tid"); + } else { + url += tid; + } + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + if (value.length() > 0) { + url += "/" + key + "/" + URLEncoder.encode(value); + } + } + } + url += "/page/" + pg + ".html"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + // 取页码相关信息 + Elements pageInfo = doc.select(".myui-page li a"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element a = pageInfo.get(i); + String name = a.text(); + if (page == -1 && a.hasClass("btn-warm")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1).trim()); + } else { + page = 0; + } + } + if (name.equals("尾頁")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1).trim()); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + // 取当前分类页的视频列表 + Elements list = doc.select("li.col-lg-6 >div.myui-vodlist__box"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".myui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + + Matcher matcher = regexVid.matcher(vod.selectFirst(".myui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 48); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 48); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/vod/" + ids.get(0) + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + String vid = doc.selectFirst("span.mac_hits").attr("data-id"); + + String cover = doc.selectFirst("a.myui-vodlist__thumb img").attr("data-original"); + String title = doc.selectFirst("div.myui-content__detail h1.title").text(); + String desc = Jsoup.parse(doc.selectFirst("meta[name=description]").attr("content")).text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.myui-content__detail span.text-muted"); + for (int i = 0; i < span_text_muted.size(); i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.equals("分類:")) { + category = text.nextElementSibling().text(); + } else if (info.equals("年份:")) { + year = text.nextElementSibling().text(); + } else if (info.equals("地區:")) { + area = text.nextElementSibling().text(); + } else if (info.equals("更新:")) { + remark = text.nextElementSibling().text(); + } else if (info.equals("導演:")) { + List directors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + directors.add(aa.get(j).text()); + } + director = TextUtils.join(",", directors); + } else if (info.equals("主演:")) { + List actors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + actors.add(aa.get(j).text()); + } + actor = TextUtils.join(",", actors); + } + } + + vodList.put("vod_id", vid); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + // 取播放列表数据 + Elements sources = doc.select("div.myui-panel__head>ul").get(0).select("li"); + Elements sourceList = doc.select("div.tab-content>div.tab-pane"); + + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + String sourceName = source.text(); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = sourceList.get(i).select("ul>li>a"); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + //定义播放用的headers + JSONObject headers = new JSONObject(); + //headers.put("Host", " cokemv.co"); + headers.put("origin", " https://imaple.co"); + headers.put("User-Agent", " Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", " */*"); + headers.put("Accept-Language", " zh-CN,zh;q=0.9,en-US;q=0.3,en;q=0.7"); + headers.put("Accept-Encoding", " gzip, deflate"); + + + // 播放页 url + String url = siteUrl + "/play/" + id + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { // 取直链 + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + String playUrl = pCfg.getString("pu"); + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", headers.toString()); + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + long currentTime = System.currentTimeMillis(); + String url = siteUrl + "/index.php/ajax/suggest?mid=1&wd=" + URLEncoder.encode(key) + "&limit=10×tamp=" + currentTime; + JSONObject searchResult = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + if (searchResult.getInt("total") > 0) { + JSONArray lists = new JSONArray(searchResult.getString("list")); + for (int i = 0; i < lists.length(); i++) { + JSONObject vod = lists.getJSONObject(i); + String id = vod.getString("id"); + String title = vod.getString("name"); + String cover = vod.getString("pic"); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", ""); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Init.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Init.java new file mode 100644 index 0000000..2061c7b --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Init.java @@ -0,0 +1,11 @@ +package com.github.catvod.spider; + +import android.content.Context; + +import com.github.catvod.crawler.SpiderDebug; + +public class Init { + public static void init(Context context) { + SpiderDebug.log("自定义爬虫代码加载成功!"); + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Juhi.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Juhi.java new file mode 100644 index 0000000..8d33a94 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Juhi.java @@ -0,0 +1,472 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Juhi extends Spider { + + private static final String siteUrl = "https://www.juhi.cc/"; + private static final String siteHost = "juhi.cc"; + + protected JSONObject playerConfig; + protected JSONObject filterConfig; + + protected Pattern regexCategory = Pattern.compile("/vodtype/(\\d+)/"); + protected Pattern regexVid = Pattern.compile("/voddetail/(\\d+)/"); + protected Pattern regexPlay = Pattern.compile("/vodplay/(\\d+)-(\\d+)-(\\d+)/"); + protected Pattern regexPage = Pattern.compile("/vodshow/(\\S+)/"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"tkm3u8\":{\"sh\":\"✦剧嗨专线\",\"pu\":\"https://jx.huishij.com/yun/?url=\",\"sn\":1,\"or\":999},\"if101\":{\"sh\":\"✦蓝光超清\",\"pu\":\"\",\"sn\":0,\"or\":999},\"dbm3u8\":{\"sh\":\"北美高清①\",\"pu\":\"\",\"sn\":0,\"or\":999},\"dbyun\":{\"sh\":\"北美高清②\",\"pu\":\"\",\"sn\":0,\"or\":999},\"wjm3u8\":{\"sh\":\"北美高清③\",\"pu\":\"\",\"sn\":0,\"or\":999},\"kbm3u8\":{\"sh\":\"全球线路①\",\"pu\":\"\",\"sn\":0,\"or\":999},\"hnyun\":{\"sh\":\"全球线路②\",\"pu\":\"\",\"sn\":0,\"or\":999},\"hnm3u8\":{\"sh\":\"全球线路③\",\"pu\":\"\",\"sn\":0,\"or\":999},\"kbzy\":{\"sh\":\"全球线路④\",\"pu\":\"\",\"sn\":0,\"or\":999},\"fanqie\":{\"sh\":\"全球线路⑤\",\"pu\":\"\",\"sn\":0,\"or\":999},\"kuaiyun\":{\"sh\":\"快速线路\",\"pu\":\"https://qimihe.com/?url=\",\"sn\":1,\"or\":999},\"leyum3u8\":{\"sh\":\"午夜专线1\",\"pu\":\"\",\"sn\":0,\"or\":999},\"lym3u8\":{\"sh\":\"午夜专线2\",\"pu\":\"\",\"sn\":0,\"or\":999},\"dplayer\":{\"sh\":\"DPlayer-H5播放器\",\"pu\":\"\",\"sn\":0,\"or\":999},\"videojs\":{\"sh\":\"videojs-H5播放器\",\"pu\":\"\",\"sn\":0,\"or\":999},\"iva\":{\"sh\":\"iva-H5播放器\",\"pu\":\"\",\"sn\":0,\"or\":999},\"iframe\":{\"sh\":\"iframe外链数据\",\"pu\":\"\",\"sn\":0,\"or\":999},\"link\":{\"sh\":\"外链数据\",\"pu\":\"\",\"sn\":0,\"or\":999},\"swf\":{\"sh\":\"Flash文件\",\"pu\":\"\",\"sn\":0,\"or\":999},\"flv\":{\"sh\":\"Flv文件\",\"pu\":\"\",\"sn\":0,\"or\":999},\"bjyun\":{\"sh\":\"bjyun\",\"pu\":\"\",\"sn\":0,\"or\":999},\"bjm3u8\":{\"sh\":\"bjm3u8\",\"pu\":\"\",\"sn\":0,\"or\":999},\"wasu\":{\"sh\":\"华数直连\",\"pu\":\"\",\"sn\":0,\"or\":999},\"funshion\":{\"sh\":\"风行直连\",\"pu\":\"\",\"sn\":0,\"or\":999},\"pptv\":{\"sh\":\"PPTV直连\",\"pu\":\"https://jx.m3u8.tv/jiexi/?url=\",\"sn\":1,\"or\":999},\"letv\":{\"sh\":\"乐视直连\",\"pu\":\"https://jx.m3u8.tv/jiexi/?url=\",\"sn\":1,\"or\":999},\"mgtv\":{\"sh\":\"芒果直连\",\"pu\":\"https://jx.m3u8.tv/jiexi/?url=\",\"sn\":1,\"or\":999},\"youku\":{\"sh\":\"优酷直连\",\"pu\":\"https://jx.m3u8.tv/jiexi/?url=\",\"sn\":1,\"or\":999},\"qiyi\":{\"sh\":\"爱奇艺直连\",\"pu\":\"https://jx.m3u8.tv/jiexi/?url=\",\"sn\":1,\"or\":999},\"qq\":{\"sh\":\"腾讯直连\",\"pu\":\"https://jx.m3u8.tv/jiexi/?url=\",\"sn\":1,\"or\":999},\"m1905\":{\"sh\":\"M1905直连\",\"pu\":\"https://jx.m3u8.tv/jiexi/?url=\",\"sn\":1,\"or\":999},\"sohu\":{\"sh\":\"搜狐直连\",\"pu\":\"https://jx.m3u8.tv/jiexi/?url=\",\"sn\":1,\"or\":999}}"); + filterConfig = new JSONObject("{\"20\":[{\"key\":\"0\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"20\"},{\"n\":\"动作片\",\"v\":\"24\"},{\"n\":\"喜剧片\",\"v\":\"25\"},{\"n\":\"愛情片\",\"v\":\"26\"},{\"n\":\"科幻片\",\"v\":\"27\"},{\"n\":\"恐怖片\",\"v\":\"28\"},{\"n\":\"劇情片\",\"v\":\"11\"},{\"n\":\"戰爭片\",\"v\":\"30\"},{\"n\":\"动画电影\",\"v\":\"33\"}]},{\"key\":1,\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"大陆\",\"v\":\"大陆\"},{\"n\":\"香港\",\"v\":\"香港\"},{\"n\":\"台湾\",\"v\":\"台湾\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"法国\",\"v\":\"法国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"德国\",\"v\":\"德国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"印度\",\"v\":\"印度\"},{\"n\":\"意大利\",\"v\":\"意大利\"},{\"n\":\"西班牙\",\"v\":\"西班牙\"},{\"n\":\"加拿大\",\"v\":\"加拿大\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"21\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国产剧\",\"v\":\"34\"},{\"n\":\"港剧\",\"v\":\"35\"},{\"n\":\"韩剧\",\"v\":\"37\"},{\"n\":\"欧美剧\",\"v\":\"36\"},{\"n\":\"日本剧\",\"v\":\"38\"},{\"n\":\"台湾剧\",\"v\":\"39\"},{\"n\":\"海外剧\",\"v\":\"40\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"22\":[{\"key\":0,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"22\"},{\"n\":\"大陆综艺\",\"v\":\"41\"},{\"n\":\"港台综艺\",\"v\":\"42\"},{\"n\":\"日韩综艺\",\"v\":\"43\"},{\"n\":\"欧美综艺\",\"v\":\"44\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"23\":[{\"key\":0,\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"23\"},{\"n\":\"国产动漫\",\"v\":\"45\"},{\"n\":\"日本动漫\",\"v\":\"46\"},{\"n\":\"欧美动漫\",\"v\":\"47\"}]},{\"key\":11,\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":2,\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"time\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}]}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + Document doc = Jsoup.parse(OkHttpUtil.string(siteUrl, getHeaders(siteUrl))); + // 分类节点 + Elements elements = doc.select("ul.nav-menu > li > a"); + JSONArray classes = new JSONArray(); + for (Element ele : elements) { + //分类名 + String name = ele.text(); + boolean show = name.equals("电影") || + name.equals("电视剧") || + name.equals("综艺") || + name.equals("动漫"); + if (show) { + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + // 把分类的id和名称取出来加到列表里 + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + if (filter) { + result.put("filters", filterConfig); + } + result.put("class", classes); + try { + // 取首页推荐视频列表 + Element homeList = doc.select("ul.myui-vodlist").get(0); + Elements list = homeList.select("li > div > a"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.attr("title"); + String cover = vod.attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + Matcher matcher = regexVid.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String[] urlParams = new String[]{"", "", "", "", "", "", "", "", "", "", "", ""}; + urlParams[0] = tid; + urlParams[8] = pg; + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + urlParams[Integer.parseInt(key)] = URLEncoder.encode(value); + } + } + // 获取分类数据的url + String url = siteUrl + "/vodshow/" + TextUtils.join("-", urlParams) + "/"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + + // 取页码相关信息 + Elements pageInfo = doc.select("ul.myui-page>li"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element li = pageInfo.get(i); + Element a = li.selectFirst("a"); + if (a == null) + continue; + String name = a.text(); + if (page == -1 && a.hasClass("btn-warm")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + page = 0; + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + // 取当前分类页的视频列表 + Elements list = doc.select("ul.myui-vodlist > li > div > a"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.attr("title"); + String cover = vod.attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + Matcher matcher = regexVid.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 48); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 48); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/voddetail/" + ids.get(0) + "/"; + //System.out.println(url); + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + String cover = doc.selectFirst("div.myui-content__thumb > a > img").attr("src"); + String title = doc.selectFirst("div.myui-content__thumb > a").attr("title"); + String desc = Jsoup.parse(doc.selectFirst("meta[name=description]").attr("content")).text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.myui-content__detail span.text-muted"); + for (int i = 0; i < span_text_muted.size(); i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.equals("分类:")) { + category = text.nextElementSibling().text(); + } else if (info.equals("年份:")) { + year = text.nextElementSibling().text(); + } else if (info.equals("地区:")) { + area = text.nextElementSibling().text(); + } else if (info.equals("更新:")) { + remark = text.nextElementSibling().text(); + } else if (info.equals("导演:")) { + List directors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + directors.add(aa.get(j).text()); + } + director = TextUtils.join(",", directors); + } else if (info.equals("主演:")) { + List actors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + actors.add(aa.get(j).text()); + } + actor = TextUtils.join(",", actors); + } + } + + vodList.put("vod_id", ids.get(0)); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + //System.out.println(vodList.toString()); + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + // 取播放列表数据 + Elements sources = doc.select("ul.nav-btn > li > a"); + //System.out.println(sources.size()); + Elements sourceList = doc.select("div.tab-content > div"); + //System.out.println(sourceList.size()); + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + //System.out.println(sources.text().split(":")[0].split("』")[1]); + String sourceName = source.text(); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = sourceList.get(i).select("ul > li > a"); + //System.out.println(playListA.size()); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + //定义播放用的headers + JSONObject headers = new JSONObject(); + //headers.put("Host", " cokemv.co"); + //headers.put("origin", " https://juhi.cc"); + //headers.put("User-Agent", " Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + //headers.put("Accept", " */*"); + //headers.put("Accept-Language", " zh-CN,zh;q=0.9,en-US;q=0.3,en;q=0.7"); + //headers.put("Accept-Encoding", " gzip, deflate, br"); + + + // 播放页 url + String url = siteUrl + "/vodplay/" + id + "/"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { // 取直链 + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + String playUrl = pCfg.getString("pu"); + if (Misc.isVip(videoUrl)) { // 使用jx:1 + try { + result.put("parse", 1); + result.put("jx", "1"); + result.put("url", videoUrl); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", headers.toString()); + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + long currentTime = System.currentTimeMillis(); + String url = siteUrl + "/index.php/ajax/suggest?mid=1&wd=" + URLEncoder.encode(key) + "&limit=10×tamp=" + currentTime; + //Document doc = Jsoup.parse(srr.content); + JSONObject searchResult = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + if (searchResult.getInt("total") > 0) { + JSONArray lists = new JSONArray(searchResult.getString("list")); + for (int i = 0; i < lists.length(); i++) { + JSONObject vod = lists.getJSONObject(i); + String id = vod.getString("id"); + String title = vod.getString("name"); + String cover = vod.getString("pic"); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", ""); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Jumi.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Jumi.java new file mode 100644 index 0000000..1f3b010 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Jumi.java @@ -0,0 +1,466 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Demo for self study + *

+ * Source from Author: CatVod + */ + +public class Jumi extends Spider { + + private static final String siteUrl = "https://jumi.tv/"; + private static final String siteHost = "jumi.tv"; + + protected JSONObject playerConfig; + protected JSONObject filterConfig; + + protected Pattern regexCategory = Pattern.compile("/type/(\\S+).html"); + protected Pattern regexVid = Pattern.compile("/vod/(\\d+).html"); + protected Pattern regexPlay = Pattern.compile("/play/(\\d+)-(\\d+)-(\\d+).html"); + protected Pattern regexPage = Pattern.compile("\\S+/page/(\\d+)\\S+"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"mlm3u8\":{\"sh\":\"極速①線\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"alizy\":{\"sh\":\"極速③線\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"weiyun\":{\"sh\":\"1080P\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"dbm3u8\":{\"sh\":\"急速雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"tkm3u8\":{\"sh\":\"天空雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"wjm3u8\":{\"sh\":\"無盡雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"605m3u8\":{\"sh\":\"光速雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"kbm3u8\":{\"sh\":\"秒播雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"bjm3u8\":{\"sh\":\"八戒雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"hnm3u8\":{\"sh\":\"紅牛雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"bdxm3u8\":{\"sh\":\"北鬥雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"youku\":{\"sh\":\"優酷雲\",\"sn\":1,\"pu\":\"https://www.mgtv.com.jumi.tv/player/?url=\",\"or\":999},\"mgtv\":{\"sh\":\"芒果雲\",\"sn\":1,\"pu\":\"https://www.mgtv.com.jumi.tv/player/?url=\",\"or\":999},\"88zym3u8\":{\"sh\":\"88雲\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"qiyi\":{\"sh\":\"奇藝雲\",\"sn\":1,\"pu\":\"https://www.mgtv.com.jumi.tv/player/?url=\",\"or\":999},\"dplayer\":{\"sh\":\"動漫專線\",\"sn\":1,\"pu\":\"https://jx.parwix.com:4433/aliplayer/?url=\",\"or\":999},\"qq\":{\"sh\":\"騰訊雲\",\"sn\":1,\"pu\":\"https://www.mgtv.com.jumi.tv/player/?url=\",\"or\":999},\"bilibili\":{\"sh\":\"嗶哩雲\",\"sn\":1,\"pu\":\"https://www.mgtv.com.jumi.tv/player/?url=\",\"or\":999},\"sohu\":{\"sh\":\"搜狐雲\",\"sn\":1,\"pu\":\"https://www.mgtv.com.jumi.tv/player/?url=\",\"or\":999}}"); + filterConfig = new JSONObject("{\"1\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"動作片\",\"v\":\"6\"},{\"n\":\"喜劇片\",\"v\":\"7\"},{\"n\":\"愛情片\",\"v\":\"8\"},{\"n\":\"科幻片\",\"v\":\"9\"},{\"n\":\"恐怖片\",\"v\":\"10\"},{\"n\":\"劇情片\",\"v\":\"11\"},{\"n\":\"戰爭片\",\"v\":\"12\"},{\"n\":\"紀錄片\",\"v\":\"20\"}]},{\"key\":\"class\",\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"驚悚\",\"v\":\"驚悚\"},{\"n\":\"懸疑\",\"v\":\"懸疑\"},{\"n\":\"魔幻\",\"v\":\"魔幻\"},{\"n\":\"罪案\",\"v\":\"罪案\"},{\"n\":\"災難\",\"v\":\"災難\"},{\"n\":\"動畫\",\"v\":\"動畫\"},{\"n\":\"古裝\",\"v\":\"古裝\"},{\"n\":\"青春\",\"v\":\"青春\"},{\"n\":\"歌舞\",\"v\":\"歌舞\"},{\"n\":\"文藝\",\"v\":\"文藝\"},{\"n\":\"生活\",\"v\":\"生活\"},{\"n\":\"歷史\",\"v\":\"歷史\"},{\"n\":\"勵志\",\"v\":\"勵志\"},{\"n\":\"搞笑\",\"v\":\"搞笑\"},{\"n\":\"愛情\",\"v\":\"愛情\"},{\"n\":\"喜劇\",\"v\":\"喜劇\"},{\"n\":\"恐怖\",\"v\":\"恐怖\"},{\"n\":\"動作\",\"v\":\"動作\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"劇情\",\"v\":\"劇情\"},{\"n\":\"戰爭\",\"v\":\"戰爭\"},{\"n\":\"犯罪\",\"v\":\"犯罪\"},{\"n\":\"奇幻\",\"v\":\"奇幻\"},{\"n\":\"武俠\",\"v\":\"武俠\"},{\"n\":\"冒險\",\"v\":\"冒險\"},{\"n\":\"經典\",\"v\":\"經典\"},{\"n\":\"兒童\",\"v\":\"兒童\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"大陸\",\"v\":\"大陸\"},{\"n\":\"香港\",\"v\":\"香港\"},{\"n\":\"台灣\",\"v\":\"台灣\"},{\"n\":\"美國\",\"v\":\"美國\"},{\"n\":\"法國\",\"v\":\"法國\"},{\"n\":\"英國\",\"v\":\"英國\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韓國\",\"v\":\"韓國\"},{\"n\":\"德國\",\"v\":\"德國\"},{\"n\":\"泰國\",\"v\":\"泰國\"},{\"n\":\"印度\",\"v\":\"印度\"},{\"n\":\"意大利\",\"v\":\"意大利\"},{\"n\":\"西班牙\",\"v\":\"西班牙\"},{\"n\":\"加拿大\",\"v\":\"加拿大\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"2\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"陸劇\",\"v\":\"13\"},{\"n\":\"港劇\",\"v\":\"14\"},{\"n\":\"台劇\",\"v\":\"22\"},{\"n\":\"日劇\",\"v\":\"15\"},{\"n\":\"韓劇\",\"v\":\"23\"},{\"n\":\"美劇\",\"v\":\"16\"},{\"n\":\"海外劇\",\"v\":\"24\"}]},{\"key\":\"class\",\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"言情\",\"v\":\"言情\"},{\"n\":\"都市\",\"v\":\"都市\"},{\"n\":\"家庭\",\"v\":\"家庭\"},{\"n\":\"生活\",\"v\":\"生活\"},{\"n\":\"偶像\",\"v\":\"偶像\"},{\"n\":\"喜劇\",\"v\":\"喜劇\"},{\"n\":\"歷史\",\"v\":\"歷史\"},{\"n\":\"古裝\",\"v\":\"古裝\"},{\"n\":\"武俠\",\"v\":\"武俠\"},{\"n\":\"刑偵\",\"v\":\"刑偵\"},{\"n\":\"戰爭\",\"v\":\"戰爭\"},{\"n\":\"神話\",\"v\":\"神話\"},{\"n\":\"軍旅\",\"v\":\"軍旅\"},{\"n\":\"諜戰\",\"v\":\"諜戰\"},{\"n\":\"商戰\",\"v\":\"商戰\"},{\"n\":\"校園\",\"v\":\"校園\"},{\"n\":\"穿越\",\"v\":\"穿越\"},{\"n\":\"懸疑\",\"v\":\"懸疑\"},{\"n\":\"犯罪\",\"v\":\"犯罪\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"愛情\",\"v\":\"愛情\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"大陸\",\"v\":\"大陸\"},{\"n\":\"香港\",\"v\":\"香港\"},{\"n\":\"台灣\",\"v\":\"台灣\"},{\"n\":\"美國\",\"v\":\"美國\"},{\"n\":\"法國\",\"v\":\"法國\"},{\"n\":\"英國\",\"v\":\"英國\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韓國\",\"v\":\"韓國\"},{\"n\":\"德國\",\"v\":\"德國\"},{\"n\":\"泰國\",\"v\":\"泰國\"},{\"n\":\"印度\",\"v\":\"印度\"},{\"n\":\"意大利\",\"v\":\"意大利\"},{\"n\":\"西班牙\",\"v\":\"西班牙\"},{\"n\":\"加拿大\",\"v\":\"加拿大\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"3\":[{\"key\":\"class\",\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"选秀\",\"v\":\"选秀\"},{\"n\":\"情感\",\"v\":\"情感\"},{\"n\":\"访谈\",\"v\":\"访谈\"},{\"n\":\"播报\",\"v\":\"播报\"},{\"n\":\"旅游\",\"v\":\"旅游\"},{\"n\":\"音乐\",\"v\":\"音乐\"},{\"n\":\"美食\",\"v\":\"美食\"},{\"n\":\"纪实\",\"v\":\"纪实\"},{\"n\":\"曲艺\",\"v\":\"曲艺\"},{\"n\":\"生活\",\"v\":\"生活\"},{\"n\":\"游戏互动\",\"v\":\"游戏互动\"},{\"n\":\"财经\",\"v\":\"财经\"},{\"n\":\"求职\",\"v\":\"求职\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"大陆\",\"v\":\"大陆\"},{\"n\":\"港台\",\"v\":\"港台\"},{\"n\":\"日韩\",\"v\":\"日韩\"},{\"n\":\"欧美\",\"v\":\"欧美\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"4\":[{\"key\":\"class\",\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"情感\",\"v\":\"情感\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"热血\",\"v\":\"热血\"},{\"n\":\"推理\",\"v\":\"推理\"},{\"n\":\"搞笑\",\"v\":\"搞笑\"},{\"n\":\"冒险\",\"v\":\"冒险\"},{\"n\":\"萝莉\",\"v\":\"萝莉\"},{\"n\":\"校园\",\"v\":\"校园\"},{\"n\":\"动作\",\"v\":\"动作\"},{\"n\":\"机战\",\"v\":\"机战\"},{\"n\":\"运动\",\"v\":\"运动\"},{\"n\":\"战争\",\"v\":\"战争\"},{\"n\":\"少年\",\"v\":\"少年\"},{\"n\":\"少女\",\"v\":\"少女\"},{\"n\":\"社会\",\"v\":\"社会\"},{\"n\":\"原创\",\"v\":\"原创\"},{\"n\":\"亲子\",\"v\":\"亲子\"},{\"n\":\"益智\",\"v\":\"益智\"},{\"n\":\"励志\",\"v\":\"励志\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国产\",\"v\":\"国产\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"欧美\",\"v\":\"欧美\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}]}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + String url = siteUrl + '/'; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements elements = doc.select("ul.nav-menu>li>a"); + JSONArray classes = new JSONArray(); + ArrayList allClass = new ArrayList<>(); + for (Element ele : elements) { + String name = ele.text(); + boolean show = !filter || (name.equals("電影") || name.equals("劇集") || name.equals("綜藝") || name.equals("動漫")); + if (allClass.contains(name)) + show = false; + if (show) { + allClass.add(name); + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + // 把分类的id和名称取出来加到列表里 + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + if (filter) { + result.put("filters", filterConfig); + } + result.put("class", classes); + try { + // 取首页推荐视频列表 + Elements list = doc.select("div.col-lg-wide-75 > ul.myui-vodlist li div.myui-vodlist__box"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".myui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + + Matcher matcher = regexVid.matcher(vod.selectFirst(".myui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String url = siteUrl + "/show/"; + if (extend != null && extend.size() > 0 && extend.containsKey("tid") && extend.get("tid").length() > 0) { + url += extend.get("tid"); + } else { + url += tid; + } + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + if (value.length() > 0) { + url += "/" + key + "/" + URLEncoder.encode(value); + } + } + } + url += "/page/" + pg + ".html"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + // 取页码相关信息 + Elements pageInfo = doc.select(".myui-page li a"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element a = pageInfo.get(i); + String name = a.text(); + if (page == -1 && a.hasClass("btn-warm")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1).trim()); + } else { + page = 0; + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1).trim()); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + // 取当前分类页的视频列表 + Elements list = doc.select("ul.myui-vodlist li div.myui-vodlist__box"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".myui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + + Matcher matcher = regexVid.matcher(vod.selectFirst(".myui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 48); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 48); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/vod/" + ids.get(0) + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + String vid = doc.selectFirst("span.mac_hits").attr("data-id"); + + String cover = doc.selectFirst("a.myui-vodlist__thumb img").attr("data-original"); + String title = doc.selectFirst("div.myui-content__detail h1.title").text(); + String desc = Jsoup.parse(doc.selectFirst("meta[name=description]").attr("content")).text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.myui-content__detail span.text-muted"); + for (int i = 0; i < span_text_muted.size(); i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.equals("分类:")) { + category = text.nextElementSibling().text(); + } else if (info.equals("年份:")) { + year = text.nextElementSibling().text(); + } else if (info.equals("地区:")) { + area = text.nextElementSibling().text(); + } else if (info.equals("更新:")) { + remark = text.nextElementSibling().text(); + } else if (info.equals("导演:")) { + List directors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + directors.add(aa.get(j).text()); + } + director = TextUtils.join(",", directors); + } else if (info.equals("主演:")) { + List actors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + actors.add(aa.get(j).text()); + } + actor = TextUtils.join(",", actors); + } + } + + vodList.put("vod_id", vid); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + // 取播放列表数据 + Elements sources = doc.select("div.myui-panel__head>ul").get(0).select("li"); + Elements sourceList = doc.select("div.tab-content>div.tab-pane"); + + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + String sourceName = source.text(); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = sourceList.get(i).select("ul>li>a"); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + //定义播放用的headers + JSONObject headers = new JSONObject(); + //headers.put("Host", " cokemv.co"); + headers.put("origin", " https://jumi.tv"); + headers.put("User-Agent", " Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", " */*"); + headers.put("Accept-Language", " zh-CN,zh;q=0.9,en-US;q=0.3,en;q=0.7"); + headers.put("Accept-Encoding", " gzip, deflate"); + headers.put("referer", " https://jumi.tv/"); + + + // 播放页 url + String url = siteUrl + "/play/" + id + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { // 取直链 + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + String playUrl = pCfg.getString("pu"); + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", headers.toString()); + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + long currentTime = System.currentTimeMillis(); + String url = siteUrl + "/index.php/ajax/suggest?mid=1&wd=" + URLEncoder.encode(key) + "&limit=10×tamp=" + currentTime; + JSONObject searchResult = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + if (searchResult.getInt("total") > 0) { + JSONArray lists = new JSONArray(searchResult.getString("list")); + for (int i = 0; i < lists.length(); i++) { + JSONObject vod = lists.getJSONObject(i); + String id = vod.getString("id"); + String title = vod.getString("name"); + String cover = vod.getString("pic"); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", ""); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/N0ys.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/N0ys.java new file mode 100644 index 0000000..4e63721 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/N0ys.java @@ -0,0 +1,471 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.net.Uri; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 90影视 + *

+ * Author: 小黄瓜 + */ +public class N0ys extends Spider { + private static final String siteUrl = "http://1090ys8.com"; + private static final String siteHost = "1090ys8.com"; + private static final String ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"; + + /** + * 播放源配置 + */ + private JSONObject playerConfig; + + private Pattern regexCategory = Pattern.compile("/whole/(\\w+).html"); + private Pattern regexVid = Pattern.compile("/show/(\\w+).html"); + private Pattern regexPlay = Pattern.compile("/play/(\\w+)-(\\d+)-(\\d+).html"); + private Pattern regexPage = Pattern.compile("/whole/\\d+/page/(\\d+).html"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"youbo\":{\"sh\":\"高速1\",\"sn\":\"0\",\"pu\":\"http://jx1090ys5.hongfanedu.top/x2.php?id=\",\"or\":999},\"wanpan\":{\"sh\":\"高速备用\",\"sn\":\"0\",\"pu\":\"\",\"or\":999},\"niuxyun\":{\"sh\":\"高速2\",\"sn\":\"1\",\"pu\":\"http://1090ys2.com/nxjx/jx.php?id=\",\"or\":999},\"bjm3u8\":{\"sh\":\"备用2\",\"sn\":\"0\",\"pu\":\"\",\"or\":999},\"dbm3u8\":{\"sh\":\"备用3\",\"sn\":\"0\",\"pu\":\"\",\"or\":999},\"nfmp4\":{\"sh\":\"高速3\",\"sn\":\"0\",\"pu\":\"\",\"or\":999},\"tkm3u8\":{\"sh\":\"备用1\",\"sn\":\"0\",\"pu\":\"\",\"or\":999},\"wjm3u8\":{\"sh\":\"备用4\",\"sn\":\"0\",\"pu\":\"\",\"or\":999}}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", ua); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + Document doc = Jsoup.parse(OkHttpUtil.string(siteUrl, getHeaders(siteUrl))); + // 分类节点 + Elements elements = doc.select("ul.type-slide > li a"); + JSONArray classes = new JSONArray(); + for (Element ele : elements) { + String name = ele.text(); + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + JSONObject result = new JSONObject(); + result.put("class", classes); + try { + Elements list = doc.select("div.stui-pannel_bd ul.stui-vodlist li div.stui-vodlist__box"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + Matcher matcher = regexVid.matcher(vod.selectFirst(".stui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".stui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + // 获取分类数据的url + String url = siteUrl + "/whole/" + tid + "/page/" + pg + ".html"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + Elements pageInfo = doc.select(".stui-page li"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element li = pageInfo.get(i); + Element a = li.selectFirst("a"); + if (a == null) + continue; + String name = a.text(); + if (page == -1 && li.hasClass("active")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1)); + } else { + page = 0; + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1)); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + Elements list = doc.select("ul.stui-vodlist li div.stui-vodlist__box"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".stui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + Matcher matcher = regexVid.matcher(vod.selectFirst(".stui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 30); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 30); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/show/" + ids.get(0) + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + String cover = doc.selectFirst("a.stui-vodlist__thumb img").attr("data-original"); + String title = doc.selectFirst("a.stui-vodlist__thumb").attr("title"); + String desc = Jsoup.parse(doc.selectFirst("meta[name=description]").attr("content")).text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.stui-content__detail span.text-muted"); + for (int i = 0; i < span_text_muted.size(); i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.equals("类型:")) { + category = text.nextSibling().outerHtml().trim(); + } else if (info.equals("年份:")) { + year = text.nextSibling().outerHtml().trim(); + } else if (info.equals("地区:")) { + area = text.nextSibling().outerHtml().trim(); + } else if (info.equals("更新:")) { + remark = text.nextSibling().outerHtml().trim(); + } else if (info.equals("导演:")) { + director = text.nextSibling().outerHtml().trim(); + } else if (info.equals("主演:")) { + actor = text.nextSibling().outerHtml().trim(); + } + } + + vodList.put("vod_id", ids.get(0)); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + Elements sources = doc.select("div.playlist"); + + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + String sourceName = source.selectFirst("h3.title").text().trim(); + String tabHref = source.attr("href"); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = source.select("ul.stui-content__playlist > li > a"); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + // 播放页 url + String url = "http://1090ys8.com/play/" + id + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + String playUrl = pCfg.getString("pu"); + String from = player.getString("from"); + if (from.equals("youbo")) { + // 感谢猫大神的直连解密代码 + HashMap hds = getHeaders(url); + hds.put("Host", "jx1090ys5.hongfanedu.top"); + hds.put("Referer", url); + String content1 = OkHttpUtil.string(playUrl + videoUrl, hds); + Document doc1 = Jsoup.parse(content1); + String url2 = doc1.selectFirst("iframe#WANG").attr("src"); + String url2host = Uri.parse(url2).getHost(); + hds.put("Host", url2host); + hds.put("Referer", "http://jx1090ys5.hongfanedu.top/"); + String content2 = OkHttpUtil.string(url2, hds); + String finder = "var id=\""; + start = content2.indexOf(finder) + finder.length(); + end = content2.indexOf('\"', start); + String vvid = content2.substring(start, end); + finder = "var sk=\""; + start = content2.indexOf(finder) + finder.length(); + end = content2.indexOf('\"', start); + String skey = content2.substring(start, end); + finder = "var pt=\""; + start = content2.indexOf(finder) + finder.length(); + end = content2.indexOf('\"', start); + String pt = content2.substring(start, end); + finder = "var ti=\""; + start = content2.indexOf(finder) + finder.length(); + end = content2.indexOf('\"', start); + String time = content2.substring(start, end); + int bkn = 0x195c; + for (int j = 0x0, all = skey.length(); all > j; ++j) { + bkn += ((bkn << 5) + (byte) skey.charAt(j)); + } + bkn = 0x7fffffff & bkn; + int gtk1 = 0x0; + for (int j = 0x0; j < pt.length(); j += 0x4) { + gtk1 += Integer.parseInt(String.format("%c%c%c%c", pt.charAt(j), pt.charAt(j + 0x1), pt.charAt(j + 0x2), pt.charAt(j + 0x3)), 0x10); + gtk1 %= 0x400a; + } + int offset = gtk1 % 10; + int gtk = 0x0; + for (int j = 0x0; j < pt.length(); j++) { + gtk += ((byte) pt.charAt(j)) * (j + offset); + gtk %= gtk1; + } + String finalVideoUrl = "http://" + url2host + "/hls/play/" + vvid + "%7C" + time + "%7C" + skey + "%7C" + pt + "%7C" + bkn + "%7C" + gtk + ".m3u8"; + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", ""); + result.put("url", finalVideoUrl); + result.put("header", "{\"Referer\":\" " + url2 + "\"}"); + } else { + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", ""); + } + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 搜索 + * + * @param key + * @param quick 是否播放页的快捷搜索 + * @return + */ + @Override + public String searchContent(String key, boolean quick) { + try { + long currentTime = System.currentTimeMillis(); + String url = siteUrl + "/index.php/ajax/suggest?mid=1&wd=" + URLEncoder.encode(key) + "&limit=10×tamp=" + currentTime; + JSONObject searchResult = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + if (searchResult.getInt("total") > 0) { + JSONArray lists = new JSONArray(searchResult.getString("list")); + for (int i = 0; i < lists.length(); i++) { + JSONObject vod = lists.getJSONObject(i); + String id = vod.getString("id"); + String title = vod.getString("name"); + String cover = vod.getString("pic"); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", ""); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Nekk.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Nekk.java new file mode 100644 index 0000000..1c21002 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Nekk.java @@ -0,0 +1,489 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Base64; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OKCallBack; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import okhttp3.Call; +import okhttp3.Headers; +import okhttp3.Response; + + +public class Nekk extends Spider { + + private static final String siteUrl = "https://www.9ela.com"; + private static final String siteHost = "www.9ela.com"; + + protected JSONObject playerConfig; + protected JSONObject filterConfig; + + protected Pattern regexCategory = Pattern.compile("/vodshow/(\\S+).html"); + protected Pattern regexVid = Pattern.compile("/voddetail/(\\d+).html"); + protected Pattern regexPlay = Pattern.compile("/vodplay/(\\d+)-(\\d+)-(\\d+).html"); + protected Pattern regexPage = Pattern.compile("\\S+/page/(\\d+)\\S+"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"9ekk\":{\"sh\":\"手机线路\",\"sn\":0,\"pu\":\"\",\"or\":999},\"ttee\":{\"sh\":\"9亿超清\",\"sn\":0,\"pu\":\"\",\"or\":999},\"9999ku\":{\"sh\":\"手机线路2\",\"sn\":0,\"pu\":\"\",\"or\":999},\"gsyun\":{\"sh\":\"九亿超清\",\"sn\":0,\"pu\":\"\",\"or\":999},\"kkyun\":{\"sh\":\"HD超清线路\",\"sn\":0,\"pu\":\"\",\"or\":999},\"ty01\":{\"sh\":\"超清线路\",\"sn\":0,\"pu\":\"\",\"or\":999},\"ppayun\":{\"sh\":\"九亿线路\",\"sn\":0,\"pu\":\"https://wy.mlkioiy.cn/api/GetDownUrlWy/d91ea50eb7eb4932b92aa8bb3c61560d/\",\"or\":999},\"8kdm\":{\"sh\":\"九亿动漫\",\"sn\":0,\"pu\":\"\",\"or\":999},\"jsyun\":{\"sh\":\"九亿备用\",\"sn\":0,\"pu\":\"\",\"or\":999},\"9evip\":{\"sh\":\"九亿解析-弃用\",\"sn\":0,\"pu\":\"\",\"or\":999},\"189pan\":{\"sh\":\"九亿超清11\",\"sn\":0,\"pu\":\"http://1987s.com/api/dp.php?url=\",\"or\":999},\"VIP\":{\"sh\":\"VIP线路\",\"sn\":0,\"pu\":\"\",\"or\":999},\"qq\":{\"sh\":\"九亿解析\",\"sn\":1,\"pu\":\"\",\"or\":999},\"mgtv\":{\"sh\":\"九亿解析\",\"sn\":1,\"pu\":\"\",\"or\":999},\"bjm3u8\":{\"sh\":\"线路三\",\"sn\":0,\"pu\":\"\",\"or\":999},\"dbm3u8\":{\"sh\":\"线路四\",\"sn\":0,\"pu\":\"\",\"or\":999},\"ckm3u8\":{\"sh\":\"线路二\",\"sn\":0,\"pu\":\"\",\"or\":999},\"mahua\":{\"sh\":\"线路四\",\"sn\":0,\"pu\":\"\",\"or\":999},\"123kum3u8\":{\"sh\":\"123kum3u8\",\"sn\":1,\"pu\":\"https://jx.123ku.com/123kudpbfq/?url=\",\"or\":999},\"pqc\":{\"sh\":\"测试\",\"sn\":1,\"pu\":\"https://jx.qiuzeqi.top/jx1/dp/index.php?url=\",\"or\":999},\"test\":{\"sh\":\"test\",\"sn\":1,\"pu\":\"https//1987s.com/api/dp.php?url=\",\"or\":999}}"); + filterConfig = new JSONObject("{\"dianying\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国语配音电影\",\"v\":\"gypy\"},{\"n\":\"最近热播电影\",\"v\":\"zuijinrebodianying\"},{\"n\":\"动作片\",\"v\":\"dongzuopian\"},{\"n\":\"喜剧片\",\"v\":\"xijupian\"},{\"n\":\"爱情片\",\"v\":\"aiqingpian\"},{\"n\":\"科幻片\",\"v\":\"kehuanpian\"},{\"n\":\"恐怖片\",\"v\":\"kongbupian\"},{\"n\":\"剧情片\",\"v\":\"juqingpian\"},{\"n\":\"战争片\",\"v\":\"zhanzhengpian\"}]},{\"key\":\"class\",\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"喜剧\",\"v\":\"喜剧\"},{\"n\":\"爱情\",\"v\":\"爱情\"},{\"n\":\"恐怖\",\"v\":\"恐怖\"},{\"n\":\"动作\",\"v\":\"动作\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"剧情\",\"v\":\"剧情\"},{\"n\":\"战争\",\"v\":\"战争\"},{\"n\":\"警匪\",\"v\":\"警匪\"},{\"n\":\"犯罪\",\"v\":\"犯罪\"},{\"n\":\"动画\",\"v\":\"动画\"},{\"n\":\"奇幻\",\"v\":\"奇幻\"},{\"n\":\"武侠\",\"v\":\"武侠\"},{\"n\":\"冒险\",\"v\":\"冒险\"},{\"n\":\"枪战\",\"v\":\"枪战\"},{\"n\":\"恐怖\",\"v\":\"恐怖\"},{\"n\":\"悬疑\",\"v\":\"悬疑\"},{\"n\":\"惊悚\",\"v\":\"惊悚\"},{\"n\":\"经典\",\"v\":\"经典\"},{\"n\":\"青春\",\"v\":\"青春\"},{\"n\":\"文艺\",\"v\":\"文艺\"},{\"n\":\"微电影\",\"v\":\"微电影\"},{\"n\":\"古装\",\"v\":\"古装\"},{\"n\":\"历史\",\"v\":\"历史\"},{\"n\":\"运动\",\"v\":\"运动\"},{\"n\":\"农村\",\"v\":\"农村\"},{\"n\":\"儿童\",\"v\":\"儿童\"},{\"n\":\"网络电影\",\"v\":\"网络电影\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"中国香港\",\"v\":\"中国香港\"},{\"n\":\"中国台湾\",\"v\":\"中国台湾\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"法国\",\"v\":\"法国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"德国\",\"v\":\"德国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"印度\",\"v\":\"印度\"},{\"n\":\"意大利\",\"v\":\"意大利\"},{\"n\":\"西班牙\",\"v\":\"西班牙\"},{\"n\":\"加拿大\",\"v\":\"加拿大\"},{\"n\":\"其他\",\"v\":\"其他\"},{\"n\":\"热门\",\"v\":\"热门\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"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\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"lianxuju\":[{\"key\":\"tid\",\"name\":\"类型\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"最近热播电视剧\",\"v\":\"zuijinrebodianshiju\"},{\"n\":\"国语配音剧集\",\"v\":\"guoyupeiyinhanju\"},{\"n\":\"热播美剧\",\"v\":\"rebomeiju\"},{\"n\":\"电视剧\",\"v\":\"guochanju\"},{\"n\":\"港台剧\",\"v\":\"gangtaiju\"},{\"n\":\"日韩剧\",\"v\":\"rihanju\"},{\"n\":\"欧美剧\",\"v\":\"oumeiju\"}]},{\"key\":\"class\",\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"古装\",\"v\":\"古装\"},{\"n\":\"战争\",\"v\":\"战争\"},{\"n\":\"青春偶像\",\"v\":\"青春偶像\"},{\"n\":\"喜剧\",\"v\":\"喜剧\"},{\"n\":\"家庭\",\"v\":\"家庭\"},{\"n\":\"犯罪\",\"v\":\"犯罪\"},{\"n\":\"动作\",\"v\":\"动作\"},{\"n\":\"奇幻\",\"v\":\"奇幻\"},{\"n\":\"剧情\",\"v\":\"剧情\"},{\"n\":\"历史\",\"v\":\"历史\"},{\"n\":\"经典\",\"v\":\"经典\"},{\"n\":\"乡村\",\"v\":\"乡村\"},{\"n\":\"情景\",\"v\":\"情景\"},{\"n\":\"商战\",\"v\":\"商战\"},{\"n\":\"网剧\",\"v\":\"网剧\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"韩国\",\"v\":\"韩国\"},{\"n\":\"香港\",\"v\":\"香港\"},{\"n\":\"台湾\",\"v\":\"台湾\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"美国\",\"v\":\"美国\"},{\"n\":\"泰国\",\"v\":\"泰国\"},{\"n\":\"英国\",\"v\":\"英国\"},{\"n\":\"新加坡\",\"v\":\"新加坡\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"zongyi\":[{\"key\":\"class\",\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"选秀\",\"v\":\"选秀\"},{\"n\":\"情感\",\"v\":\"情感\"},{\"n\":\"访谈\",\"v\":\"访谈\"},{\"n\":\"播报\",\"v\":\"播报\"},{\"n\":\"旅游\",\"v\":\"旅游\"},{\"n\":\"音乐\",\"v\":\"音乐\"},{\"n\":\"美食\",\"v\":\"美食\"},{\"n\":\"纪实\",\"v\":\"纪实\"},{\"n\":\"曲艺\",\"v\":\"曲艺\"},{\"n\":\"生活\",\"v\":\"生活\"},{\"n\":\"游戏互动\",\"v\":\"游戏互动\"},{\"n\":\"财经\",\"v\":\"财经\"},{\"n\":\"求职\",\"v\":\"求职\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"中国大陆\",\"v\":\"中国大陆\"},{\"n\":\"大陆\",\"v\":\"大陆\"},{\"n\":\"港台\",\"v\":\"港台\"},{\"n\":\"日韩\",\"v\":\"日韩\"},{\"n\":\"欧美\",\"v\":\"欧美\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}],\"dongman\":[{\"key\":\"class\",\"name\":\"剧情\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"情感\",\"v\":\"情感\"},{\"n\":\"科幻\",\"v\":\"科幻\"},{\"n\":\"热血\",\"v\":\"热血\"},{\"n\":\"推理\",\"v\":\"推理\"},{\"n\":\"搞笑\",\"v\":\"搞笑\"},{\"n\":\"冒险\",\"v\":\"冒险\"},{\"n\":\"萝莉\",\"v\":\"萝莉\"},{\"n\":\"校园\",\"v\":\"校园\"},{\"n\":\"动作\",\"v\":\"动作\"},{\"n\":\"机战\",\"v\":\"机战\"},{\"n\":\"运动\",\"v\":\"运动\"},{\"n\":\"战争\",\"v\":\"战争\"},{\"n\":\"少年\",\"v\":\"少年\"},{\"n\":\"少女\",\"v\":\"少女\"},{\"n\":\"社会\",\"v\":\"社会\"},{\"n\":\"原创\",\"v\":\"原创\"},{\"n\":\"亲子\",\"v\":\"亲子\"},{\"n\":\"益智\",\"v\":\"益智\"},{\"n\":\"励志\",\"v\":\"励志\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"area\",\"name\":\"地区\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"国产\",\"v\":\"国产\"},{\"n\":\"日本\",\"v\":\"日本\"},{\"n\":\"欧美\",\"v\":\"欧美\"},{\"n\":\"其他\",\"v\":\"其他\"}]},{\"key\":\"year\",\"name\":\"年份\",\"value\":[{\"n\":\"全部\",\"v\":\"\"},{\"n\":\"2021\",\"v\":\"2021\"},{\"n\":\"2020\",\"v\":\"2020\"},{\"n\":\"2019\",\"v\":\"2019\"},{\"n\":\"2018\",\"v\":\"2018\"},{\"n\":\"2017\",\"v\":\"2017\"},{\"n\":\"2016\",\"v\":\"2016\"},{\"n\":\"2015\",\"v\":\"2015\"},{\"n\":\"2014\",\"v\":\"2014\"},{\"n\":\"2013\",\"v\":\"2013\"},{\"n\":\"2012\",\"v\":\"2012\"},{\"n\":\"2011\",\"v\":\"2011\"},{\"n\":\"2010\",\"v\":\"2010\"},{\"n\":\"2009\",\"v\":\"2009\"},{\"n\":\"2008\",\"v\":\"2008\"},{\"n\":\"2007\",\"v\":\"2007\"},{\"n\":\"2006\",\"v\":\"2006\"},{\"n\":\"2005\",\"v\":\"2005\"},{\"n\":\"2004\",\"v\":\"2004\"}]},{\"key\":\"by\",\"name\":\"排序\",\"value\":[{\"n\":\"时间\",\"v\":\"\"},{\"n\":\"人气\",\"v\":\"hits\"},{\"n\":\"评分\",\"v\":\"score\"}]}]}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + + @Override + public String homeContent(boolean filter) { + try { + String url = siteUrl + '/'; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements elements = doc.select("ul.nav-list > li a"); + JSONArray classes = new JSONArray(); + ArrayList allClass = new ArrayList<>(); + for (Element ele : elements) { + String name = ele.text(); + boolean show = !filter || (name.equals("电影") || name.equals("连续剧") || name.equals("综艺") || name.equals("动漫")); + if (allClass.contains(name)) + show = false; + if (show) { + allClass.add(name); + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + if (filter) { + result.put("filters", filterConfig); + } + result.put("class", classes); + try { + Elements list = doc.select("div.col-lg-wide-75 > ul.myui-vodlist li div.myui-vodlist__box"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = fixCover(vod.selectFirst(".myui-vodlist__thumb").attr("data-original")); + String remark = vod.selectFirst("span.pic-text").text(); + + Matcher matcher = regexVid.matcher(vod.selectFirst(".myui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String url = siteUrl + "/vodshow/"; + if (extend != null && extend.size() > 0 && extend.containsKey("tid") && extend.get("tid").length() > 0) { + url += extend.get("tid"); + } else { + url += tid; + } + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + if (value.length() > 0) { + url += "/" + key + "/" + URLEncoder.encode(value); + } + } + } + url += "/page/" + pg + ".html"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + Elements pageInfo = doc.select(".myui-page li a"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element a = pageInfo.get(i); + String name = a.text(); + if (page == -1 && a.hasClass("btn-warm")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1).trim()); + } else { + page = 0; + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1).trim()); + } else { + pageCount = 0; + } + break; + } + } + } + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + Elements list = doc.select("ul.myui-vodlist li div.myui-vodlist__box"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = fixCover(vod.selectFirst(".myui-vodlist__thumb").attr("data-original")); + String remark = vod.selectFirst("span.pic-text").text(); + + Matcher matcher = regexVid.matcher(vod.selectFirst(".myui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 48); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 48); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String detailContent(List ids) { + try { + String url = siteUrl + "/voddetail/" + ids.get(0) + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + String vid = doc.selectFirst("span.mac_hits").attr("data-id"); + + String cover = fixCover(doc.selectFirst("a.myui-vodlist__thumb img").attr("data-original")); + String title = doc.selectFirst("div.myui-content__detail h1.title").text(); + String desc = Jsoup.parse(doc.selectFirst("meta[name=description]").attr("content")).text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.myui-content__detail span.text-muted"); + for (int i = 0; i < span_text_muted.size(); i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.equals("分类:")) { + category = text.nextElementSibling().text(); + } else if (info.equals("年份:")) { + year = text.nextElementSibling().text(); + } else if (info.equals("地区:")) { + area = text.nextElementSibling().text(); + } else if (info.equals("更新:")) { + remark = text.nextElementSibling().text(); + } else if (info.equals("导演:")) { + List directors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + directors.add(aa.get(j).text()); + } + director = TextUtils.join(",", directors); + } else if (info.equals("主演:")) { + List actors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + actors.add(aa.get(j).text()); + } + actor = TextUtils.join(",", actors); + } + } + + vodList.put("vod_id", vid); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + Elements sources = doc.select("a[data-toggle=tab]"); + + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + String sourceName = source.text(); + String tabHref = source.attr("href"); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = doc.select("div" + tabHref + " ul > li a"); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + String url = siteUrl + "/vodplay/" + id + ".html"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + if (player.has("encrypt")) { + int encrypt = player.getInt("encrypt"); + if (encrypt == 1) { + videoUrl = URLDecoder.decode(videoUrl); + } else if (encrypt == 2) { + videoUrl = new String(Base64.decode(videoUrl, Base64.DEFAULT)); + videoUrl = URLDecoder.decode(videoUrl); + } + } + String playUrl = pCfg.getString("pu"); + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + if (flag.equals("9ekk")) { + JSONObject headers = new JSONObject(); + headers.put("User-Agent", " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"); + headers.put("Accept", " */*"); + headers.put("Connection", " keep-alive"); + result.put("header", headers.toString()); + } else { + result.put("header", ""); + } + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + long currentTime = System.currentTimeMillis(); + String url = siteUrl + "/index.php/ajax/suggest?mid=1&wd=" + URLEncoder.encode(key) + "&limit=10×tamp=" + currentTime; + JSONObject searchResult = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + if (searchResult.getInt("total") > 0) { + JSONArray lists = new JSONArray(searchResult.getString("list")); + for (int i = 0; i < lists.length(); i++) { + JSONObject vod = lists.getJSONObject(i); + String id = vod.getString("id"); + String title = vod.getString("name"); + String cover = fixCover(vod.getString("pic")); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", ""); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + protected String fixCover(String cover) { + try { + return "proxy://do=nekk&pic=" + Base64.encodeToString(cover.getBytes("UTF-8"), Base64.DEFAULT | Base64.URL_SAFE | Base64.NO_WRAP); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return cover; + } + + private static HashMap nekkPicHeader = null; + + public static Object[] loadPic(String pic) { + try { + pic = new String(Base64.decode(pic, Base64.DEFAULT | Base64.URL_SAFE | Base64.NO_WRAP), "UTF-8"); + + if (nekkPicHeader == null) { + nekkPicHeader = new HashMap<>(); + nekkPicHeader.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"); + nekkPicHeader.put("referer", siteUrl + "/"); + } + OKCallBack.OKCallBackDefault callBack = new OKCallBack.OKCallBackDefault() { + @Override + protected void onFailure(Call call, Exception e) { + + } + + @Override + protected void onResponse(Response response) { + + } + }; + OkHttpUtil.get(OkHttpUtil.defaultClient(), pic, null, nekkPicHeader, callBack); + if (callBack.getResult().code() == 200) { + Headers headers = callBack.getResult().headers(); + String type = headers.get("Content-Type"); + if (type == null) { + type = "application/octet-stream"; + } + Object[] result = new Object[3]; + result[0] = 200; + result[1] = type; + System.out.println(pic); + System.out.println(type); + result[2] = callBack.getResult().body().byteStream(); + return result; + } + } catch (Throwable th) { + th.printStackTrace(); + } + return null; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Nfx.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Nfx.java new file mode 100644 index 0000000..ed70d51 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Nfx.java @@ -0,0 +1,449 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Demo for self study + *

+ * Source from Author: CatVod + */ + +public class Nfx extends Spider { + + private static final String siteUrl = "https://nfxhd.com/"; + private static final String siteHost = "nfxhd.com"; + + protected JSONObject playerConfig; + + protected Pattern regexCategory = Pattern.compile("/vodtype/(\\w+)/"); + protected Pattern regexVid = Pattern.compile("/voddetail/(\\w+)/"); + protected Pattern regexPlay = Pattern.compile("/vodplay/(\\w+)-(\\d+)-(\\d+)/"); + protected Pattern regexPage = Pattern.compile("/vodshow/(\\S+)/"); + + @Override + public void init(Context context) { + super.init(context); + try { + playerConfig = new JSONObject("{\"adaplay\":{\"sh\":\"菜逼播放器\",\"sn\":\"1\",\"pu\":\"https://nfxhd.com/dp.php/?url=\",\"or\":999},\"xigua\":{\"sh\":\"手动点播放才能播放的播放器\",\"sn\":\"1\",\"pu\":\"https://nfxhd.com/dp.php/?url=\",\"or\":999},\"danmuku\":{\"sh\":\"海王播放器\",\"sn\":\"1\",\"pu\":\"\",\"or\":999},\"dplayer\":{\"sh\":\"冥王星播放器\",\"sn\":\"0\",\"pu\":\"\",\"or\":999},\"bdxm3u8\":{\"sh\":\"北斗星m3u8\",\"sn\":\"1\",\"pu\":\"https://player.bdxzym3u8.com/dplayer/?url=\",\"or\":999},\"bdxyun\":{\"sh\":\"北斗星云播放\",\"sn\":\"1\",\"pu\":\"\",\"or\":999},\"gangtiexia\":{\"sh\":\"土星播放器\",\"sn\":\"1\",\"pu\":\"https://wy.bigmao.top/api/ShowVideoWy/3bb24322f78b47dfb8723c13d46d45ee/\",\"or\":999},\"tt10\":{\"sh\":\"木星播放器\",\"sn\":\"1\",\"pu\":\"https://wy.bigmao.top/api/ShowVideoMu/3bb24322f78b47dfb8723c13d46d45ee/\",\"or\":999},\"docker\":{\"sh\":\"彗星播放器\",\"sn\":\"1\",\"pu\":\"https://wy.bigmao.top/api/ShowVideoDoc/3bb24322f78b47dfb8723c13d46d45ee/\",\"or\":999},\"qq\":{\"sh\":\"腾讯视频\",\"sn\":\"1\",\"pu\":\"\",\"or\":999},\"youku\":{\"sh\":\"优酷\",\"sn\":\"1\",\"pu\":\"\",\"or\":999},\"qiyi\":{\"sh\":\"爱奇艺\",\"sn\":\"1\",\"pu\":\"\",\"or\":999},\"mgtv\":{\"sh\":\"仅限APP播放\",\"sn\":\"1\",\"pu\":\"\",\"or\":999},\"m3u8\":{\"sh\":\"m3u8\",\"sn\":\"1\",\"pu\":\"\",\"or\":999},\"mx771\":{\"sh\":\"站长采集的播放器\",\"sn\":\"0\",\"pu\":\"\",\"or\":999},\"ppayun2\":{\"sh\":\"白鸟座播放器\",\"sn\":\"1\",\"pu\":\"https://wy.bigmao.top/api/ShowVideoMu/3bb24322f78b47dfb8723c13d46d45ee/\",\"or\":999},\"sohu\":{\"sh\":\"搜狐\",\"sn\":\"1\",\"pu\":\"\",\"or\":999},\"iframe\":{\"sh\":\"iframe外链数据\",\"sn\":\"0\",\"pu\":\"\",\"or\":999}}"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + /** + * 爬虫headers + * + * @param url + * @return + */ + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("method", "GET"); + headers.put("Host", siteHost); + headers.put("Upgrade-Insecure-Requests", "1"); + headers.put("DNT", "1"); + headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + return headers; + } + + /** + * 获取分类数据 + 首页最近更新视频列表数据 + * + * @param filter 是否开启筛选 关联的是 软件设置中 首页数据源里的筛选开关 + * @return + */ + @Override + public String homeContent(boolean filter) { + try { + String url = siteUrl; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements elements = doc.select("ul.nav-menu>li>a"); + JSONArray classes = new JSONArray(); + ArrayList allClass = new ArrayList<>(); + for (Element ele : elements) { + String name = ele.text(); + boolean show = !filter || (name.equals("电影") || name.equals("美剧") || name.equals("日韩") || name.equals("动漫")); + if (allClass.contains(name)) + show = false; + if (show) { + allClass.add(name); + Matcher mather = regexCategory.matcher(ele.attr("href")); + if (!mather.find()) + continue; + // 把分类的id和名称取出来加到列表里 + String id = mather.group(1).trim(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + JSONObject result = new JSONObject(); + result.put("class", classes); + try { + // 取首页推荐视频列表 + Elements list = doc.select("ul.myui-vodlist>li>div"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".myui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + + Matcher matcher = regexVid.matcher(vod.selectFirst(".myui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + /** + * 获取分类信息数据 + * + * @param tid 分类id + * @param pg 页数 + * @param filter 同homeContent方法中的filter + * @param extend 筛选参数{k:v, k1:v1} + * @return + */ + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + // 获取分类数据的url + String url = siteUrl + "/vodtype/" + tid + "-" + pg + "/"; + String html = OkHttpUtil.string(url, getHeaders(url)); + Document doc = Jsoup.parse(html); + JSONObject result = new JSONObject(); + int pageCount = 0; + int page = -1; + + // 取页码相关信息 + Elements pageInfo = doc.select("ul.myui-page>li"); + if (pageInfo.size() == 0) { + page = Integer.parseInt(pg); + pageCount = page; + } else { + for (int i = 0; i < pageInfo.size(); i++) { + Element li = pageInfo.get(i); + Element a = li.selectFirst("a"); + if (a == null) + continue; + String name = a.text(); + if (page == -1 && a.hasClass("btn-warm")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + page = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + page = 0; + } + } + if (name.equals("尾页")) { + Matcher matcher = regexPage.matcher(a.attr("href")); + if (matcher.find()) { + pageCount = Integer.parseInt(matcher.group(1).split("-")[8]); + } else { + pageCount = 0; + } + break; + } + } + } + + + JSONArray videos = new JSONArray(); + if (!html.contains("没有找到您想要的结果哦")) { + // 取当前分类页的视频列表 + Elements list = doc.select("div.myui-panel_bd>ul>li>div"); + for (int i = 0; i < list.size(); i++) { + Element vod = list.get(i); + String title = vod.selectFirst(".title").text(); + String cover = vod.selectFirst(".myui-vodlist__thumb").attr("data-original"); + String remark = vod.selectFirst("span.pic-text").text(); + + Matcher matcher = regexVid.matcher(vod.selectFirst(".myui-vodlist__thumb").attr("href")); + if (!matcher.find()) + continue; + String id = matcher.group(1); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", remark); + videos.put(v); + } + } + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", 48); + result.put("total", pageCount <= 1 ? videos.length() : pageCount * 48); + + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 视频详情信息 + * + * @param ids 视频id + * @return + */ + @Override + public String detailContent(List ids) { + try { + // 视频详情url + String url = siteUrl + "/voddetail/" + ids.get(0) + "/"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONObject vodList = new JSONObject(); + + // 取基本数据 + String vid = doc.selectFirst("span.mac_hits").attr("data-id"); + + String cover = doc.selectFirst("a.myui-vodlist__thumb img").attr("data-original"); + String title = doc.selectFirst("div.myui-content__detail h1.title").text(); + String desc = doc.selectFirst("div.col-pd>span.sketch").text(); + String category = "", area = "", year = "", remark = "", director = "", actor = ""; + Elements span_text_muted = doc.select("div.myui-content__detail span.text-muted"); + for (int i = 0; i < span_text_muted.size(); i++) { + Element text = span_text_muted.get(i); + String info = text.text(); + if (info.equals("分类:")) { + category = text.nextElementSibling().text(); + } else if (info.equals("年份:")) { + year = text.nextElementSibling().text(); + } else if (info.equals("地区:")) { + area = text.nextElementSibling().text(); + } else if (info.equals("更新:")) { + remark = text.nextElementSibling().text(); + } else if (info.equals("导演:")) { + List directors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + directors.add(aa.get(j).text()); + } + } else if (info.equals("主演:")) { + List actors = new ArrayList<>(); + Elements aa = text.parent().select("a"); + for (int j = 0; j < aa.size(); j++) { + actors.add(aa.get(j).text()); + } + actor = TextUtils.join(",", actors); + } + } + + vodList.put("vod_id", vid); + vodList.put("vod_name", title); + vodList.put("vod_pic", cover); + vodList.put("type_name", category); + vodList.put("vod_year", year); + vodList.put("vod_area", area); + vodList.put("vod_remarks", remark); + vodList.put("vod_actor", actor); + vodList.put("vod_director", director); + vodList.put("vod_content", desc); + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + try { + int sort1 = playerConfig.getJSONObject(o1).getInt("or"); + int sort2 = playerConfig.getJSONObject(o2).getInt("or"); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } catch (JSONException e) { + SpiderDebug.log(e); + } + return 1; + } + }); + + // 取播放列表数据 + Elements sources = doc.select("div.myui-panel__head").get(1).select("h3"); + Elements sourceList = doc.select("ul.myui-content__list"); + + for (int i = 0; i < sources.size(); i++) { + Element source = sources.get(i); + String sourceName = source.text(); + boolean found = false; + for (Iterator it = playerConfig.keys(); it.hasNext(); ) { + String flag = it.next(); + if (playerConfig.getJSONObject(flag).getString("sh").equals(sourceName)) { + sourceName = flag; + found = true; + break; + } + } + if (!found) + continue; + String playList = ""; + Elements playListA = sourceList.get(i).select("li>a"); + List vodItems = new ArrayList<>(); + + for (int j = 0; j < playListA.size(); j++) { + Element vod = playListA.get(j); + Matcher matcher = regexPlay.matcher(vod.attr("href")); + if (!matcher.find()) + continue; + String playURL = matcher.group(1) + "-" + matcher.group(2) + "-" + matcher.group(3); + vodItems.add(vod.text() + "$" + playURL); + } + if (vodItems.size() > 0) + playList = TextUtils.join("#", vodItems); + + if (playList.length() == 0) + continue; + + vod_play.put(sourceName, playList); + } + + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + /** + * 获取视频播放信息 + * + * @param flag 播放源 + * @param id 视频id + * @param vipFlags 所有可能需要vip解析的源 + * @return + */ + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + //定义播放用的headers + JSONObject headers = new JSONObject(); + //headers.put("Host", " cokemv.co"); + headers.put("origin", " https://nfxhd.com"); + headers.put("User-Agent", " Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"); + headers.put("Accept", " */*"); + headers.put("Accept-Language", " zh-CN,zh;q=0.9,en-US;q=0.3,en;q=0.7"); + headers.put("Accept-Encoding", " gzip, deflate"); + + + // 播放页 url + String url = siteUrl + "/vodplay/" + id + "/"; + Document doc = Jsoup.parse(OkHttpUtil.string(url, getHeaders(url))); + Elements allScript = doc.select("script"); + JSONObject result = new JSONObject(); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { // 取直链 + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + if (playerConfig.has(player.getString("from"))) { + JSONObject pCfg = playerConfig.getJSONObject(player.getString("from")); + String videoUrl = player.getString("url"); + String playUrl = pCfg.getString("pu"); + result.put("parse", pCfg.getInt("sn")); + result.put("playUrl", playUrl); + result.put("url", videoUrl); + result.put("header", headers.toString()); + } + break; + } + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + long currentTime = System.currentTimeMillis(); + String url = siteUrl + "/index.php/ajax/suggest?mid=1&wd=" + URLEncoder.encode(key) + "&limit=10×tamp=" + currentTime; + JSONObject searchResult = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + if (searchResult.getInt("total") > 0) { + JSONArray lists = new JSONArray(searchResult.getString("list")); + for (int i = 0; i < lists.length(); i++) { + JSONObject vod = lists.getJSONObject(i); + String id = vod.getString("id"); + String title = vod.getString("name"); + String cover = vod.getString("pic"); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", title); + v.put("vod_pic", cover); + v.put("vod_remarks", ""); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Proxy.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Proxy.java new file mode 100644 index 0000000..6a636a2 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Proxy.java @@ -0,0 +1,32 @@ +package com.github.catvod.spider; + +import android.util.Base64; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.live.TxtSubscribe; + +import java.util.Map; + +public class Proxy extends Spider { + + public static Object[] proxy(Map params) { + try { + String what = params.get("do"); + if (what.equals("nekk")) { + String pic = params.get("pic"); + return Nekk.loadPic(pic); + } else if (what.equals("live")) { + String type = params.get("type"); + if (type.equals("txt")) { + String ext = params.get("ext"); + ext = new String(Base64.decode(ext, Base64.DEFAULT | Base64.URL_SAFE | Base64.NO_WRAP), "UTF-8"); + return TxtSubscribe.load(ext); + } + } + } catch (Throwable th) { + + } + return null; + } + +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/PushAgent.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/PushAgent.java new file mode 100644 index 0000000..377210c --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/PushAgent.java @@ -0,0 +1,98 @@ +package com.github.catvod.spider; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.utils.Misc; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.List; + +/** + * Author: @SDL + */ +public class PushAgent extends Spider { + @Override + public String detailContent(List ids) { + try { + String url = ids.get(0); + if (Misc.isVip(url)) { + JSONObject result = new JSONObject(); + JSONArray list = new JSONArray(); + JSONObject vodAtom = new JSONObject(); + vodAtom.put("vod_id", url); + vodAtom.put("vod_name", url); + vodAtom.put("vod_pic", "https://pic.rmb.bdstatic.com/bjh/1d0b02d0f57f0a42201f92caba5107ed.jpeg"); + vodAtom.put("type_name", "官源"); + vodAtom.put("vod_year", ""); + vodAtom.put("vod_area", ""); + vodAtom.put("vod_remarks", ""); + vodAtom.put("vod_actor", ""); + vodAtom.put("vod_director", ""); + vodAtom.put("vod_content", ""); + vodAtom.put("vod_play_from", "jx"); + vodAtom.put("vod_play_url", "立即播放$" + url); + list.put(vodAtom); + result.put("list", list); + return result.toString(); + } else if (Misc.isVideoFormat(url)) { + JSONObject result = new JSONObject(); + JSONArray list = new JSONArray(); + JSONObject vodAtom = new JSONObject(); + vodAtom.put("vod_id", url); + vodAtom.put("vod_name", url); + vodAtom.put("vod_pic", "https://pic.rmb.bdstatic.com/bjh/1d0b02d0f57f0a42201f92caba5107ed.jpeg"); + vodAtom.put("type_name", "直连"); + vodAtom.put("vod_play_from", "player"); + vodAtom.put("vod_play_url", "立即播放$" + url); + list.put(vodAtom); + result.put("list", list); + return result.toString(); + } else if (url.startsWith("http://") || url.startsWith("https://")) { + JSONObject result = new JSONObject(); + JSONArray list = new JSONArray(); + JSONObject vodAtom = new JSONObject(); + vodAtom.put("vod_id", url); + vodAtom.put("vod_name", url); + vodAtom.put("vod_pic", "https://pic.rmb.bdstatic.com/bjh/1d0b02d0f57f0a42201f92caba5107ed.jpeg"); + vodAtom.put("type_name", "网页"); + vodAtom.put("vod_play_from", "parse"); + vodAtom.put("vod_play_url", "立即播放$" + url); + list.put(vodAtom); + result.put("list", list); + return result.toString(); + } + } catch (Throwable throwable) { + + } + return ""; + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + if (flag.equals("jx")) { + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("jx", "1"); + result.put("url", id); + return result.toString(); + } else if (flag.equals("parse")) { + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", id); + return result.toString(); + } else if (flag.equals("player")) { + JSONObject result = new JSONObject(); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", id); + return result.toString(); + } + } catch (Throwable throwable) { + + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPath.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPath.java new file mode 100644 index 0000000..049df82 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPath.java @@ -0,0 +1,474 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; +import com.github.catvod.utils.okhttp.OkHttpUtil; +import com.github.catvod.xpath.XPathRule; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.seimicrawler.xpath.JXDocument; +import org.seimicrawler.xpath.JXNode; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +public class XPath extends Spider { + + @Override + public void init(Context context) { + super.init(context); + } + + public void init(Context context, String extend) { + super.init(context, extend); + this.ext = extend; + } + + @Override + public String homeContent(boolean filter) { + try { + fetchRule(); + JSONObject result = new JSONObject(); + JSONArray classes = new JSONArray(); + if (rule.getCateManual().size() > 0) { + Set keys = rule.getCateManual().keySet(); + for (String k : keys) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_name", k); + jsonObject.put("type_id", rule.getCateManual().get(k)); + classes.put(jsonObject); + } + } + try { + String webUrl = rule.getHomeUrl(); + JXDocument doc = JXDocument.create(fetch(webUrl)); + if (rule.getCateManual().size() == 0) { + List navNodes = doc.selN(rule.getCateNode()); + for (int i = 0; i < navNodes.size(); i++) { + String name = navNodes.get(i).selOne(rule.getCateName()).asString().trim(); + name = rule.getCateNameR(name); + String id = navNodes.get(i).selOne(rule.getCateId()).asString().trim(); + id = rule.getCateIdR(id); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("type_id", id); + jsonObject.put("type_name", name); + classes.put(jsonObject); + } + } + if (!rule.getHomeVodNode().isEmpty()) { + try { + JSONArray videos = new JSONArray(); + List vodNodes = doc.selN(rule.getHomeVodNode()); + for (int i = 0; i < vodNodes.size(); i++) { + String name = vodNodes.get(i).selOne(rule.getHomeVodName()).asString().trim(); + name = rule.getHomeVodNameR(name); + String id = vodNodes.get(i).selOne(rule.getHomeVodId()).asString().trim(); + id = rule.getHomeVodIdR(id); + String pic = vodNodes.get(i).selOne(rule.getHomeVodImg()).asString().trim(); + pic = rule.getHomeVodImgR(pic); + pic = Misc.fixUrl(webUrl, pic); + String mark = ""; + if (!rule.getHomeVodMark().isEmpty()) { + try { + mark = vodNodes.get(i).selOne(rule.getHomeVodMark()).asString().trim(); + mark = rule.getHomeVodMarkR(mark); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", name); + v.put("vod_pic", pic); + v.put("vod_remarks", mark); + videos.put(v); + } + result.put("list", videos); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + } catch (Exception e) { + SpiderDebug.log(e); + } + result.put("class", classes); + if (filter && rule.getFilter() != null) { + result.put("filters", rule.getFilter()); + } + return result.toString(); + } catch ( + Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("User-Agent", rule.getUa().isEmpty() + ? "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36" + : rule.getUa()); + return headers; + } + + @Override + public String homeVideoContent() { + try { + fetchRule(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + protected String categoryUrl(String tid, String pg, boolean filter, HashMap extend) { + return rule.getCateUrl().replace("{cateId}", tid).replace("{catePg}", pg); + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + fetchRule(); + String webUrl = categoryUrl(tid, pg, filter, extend); + JSONArray videos = new JSONArray(); + JXDocument doc = JXDocument.create(fetch(webUrl)); + List vodNodes = doc.selN(rule.getCateVodNode()); + for (int i = 0; i < vodNodes.size(); i++) { + String name = vodNodes.get(i).selOne(rule.getCateVodName()).asString().trim(); + name = rule.getCateVodNameR(name); + String id = vodNodes.get(i).selOne(rule.getCateVodId()).asString().trim(); + id = rule.getCateVodIdR(id); + String pic = vodNodes.get(i).selOne(rule.getCateVodImg()).asString().trim(); + pic = rule.getCateVodImgR(pic); + pic = Misc.fixUrl(webUrl, pic); + String mark = ""; + if (!rule.getCateVodMark().isEmpty()) { + try { + mark = vodNodes.get(i).selOne(rule.getCateVodMark()).asString().trim(); + mark = rule.getCateVodMarkR(mark); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", name); + v.put("vod_pic", pic); + v.put("vod_remarks", mark); + videos.put(v); + } + JSONObject result = new JSONObject(); + result.put("page", pg); + result.put("pagecount", Integer.MAX_VALUE); + result.put("limit", 90); + result.put("total", Integer.MAX_VALUE); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + protected void detailContentExt(String content, JSONObject vod) { + + } + + @Override + public String detailContent(List ids) { + try { + fetchRule(); + String webUrl = rule.getDetailUrl().replace("{vid}", ids.get(0)); + String webContent = fetch(webUrl); + JXDocument doc = JXDocument.create(webContent); + JXNode vodNode = doc.selNOne(rule.getDetailNode()); + + String cover = "", title = "", desc = "", category = "", area = "", year = "", remark = "", director = "", actor = ""; + + title = vodNode.selOne(rule.getDetailName()).asString().trim(); + title = rule.getDetailNameR(title); + + cover = vodNode.selOne(rule.getDetailImg()).asString().trim(); + cover = rule.getDetailImgR(cover); + cover = Misc.fixUrl(webUrl, cover); + + if (!rule.getDetailCate().isEmpty()) { + try { + category = vodNode.selOne(rule.getDetailCate()).asString().trim(); + category = rule.getDetailCateR(category); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + if (!rule.getDetailYear().isEmpty()) { + try { + year = vodNode.selOne(rule.getDetailYear()).asString().trim(); + year = rule.getDetailYearR(year); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + if (!rule.getDetailArea().isEmpty()) { + try { + area = vodNode.selOne(rule.getDetailArea()).asString().trim(); + area = rule.getDetailAreaR(area); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + if (!rule.getDetailMark().isEmpty()) { + try { + remark = vodNode.selOne(rule.getDetailMark()).asString().trim(); + remark = rule.getDetailMarkR(remark); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + if (!rule.getDetailActor().isEmpty()) { + try { + actor = vodNode.selOne(rule.getDetailActor()).asString().trim(); + actor = rule.getDetailActorR(actor); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + if (!rule.getDetailDirector().isEmpty()) { + try { + director = vodNode.selOne(rule.getDetailDirector()).asString().trim(); + director = rule.getDetailDirectorR(director); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + if (!rule.getDetailDesc().isEmpty()) { + try { + desc = vodNode.selOne(rule.getDetailDesc()).asString().trim(); + desc = rule.getDetailDescR(desc); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + + JSONObject vod = new JSONObject(); + vod.put("vod_id", ids.get(0)); + vod.put("vod_name", title); + vod.put("vod_pic", cover); + vod.put("type_name", category); + vod.put("vod_year", year); + vod.put("vod_area", area); + vod.put("vod_remarks", remark); + vod.put("vod_actor", actor); + vod.put("vod_director", director); + vod.put("vod_content", desc); + + ArrayList playFrom = new ArrayList<>(); + + List fromNodes = doc.selN(rule.getDetailFromNode()); + for (int i = 0; i < fromNodes.size(); i++) { + String name = fromNodes.get(i).selOne(rule.getDetailFromName()).asString().trim(); + name = rule.getDetailFromNameR(name); + playFrom.add(name); + } + + ArrayList playList = new ArrayList<>(); + List urlListNodes = doc.selN(rule.getDetailUrlNode()); + for (int i = 0; i < urlListNodes.size(); i++) { + List urlNodes = urlListNodes.get(i).sel(rule.getDetailUrlSubNode()); + List vodItems = new ArrayList<>(); + for (int j = 0; j < urlNodes.size(); j++) { + String name = urlNodes.get(j).selOne(rule.getDetailUrlName()).asString().trim(); + name = rule.getDetailUrlNameR(name); + String id = urlNodes.get(j).selOne(rule.getDetailUrlId()).asString().trim(); + id = rule.getDetailUrlIdR(id); + vodItems.add(name + "$" + id); + } + // 排除播放列表为空的播放源 + if (vodItems.size() == 0 && playFrom.size() > i) { + playFrom.set(i, ""); + } + playList.add(TextUtils.join("#", vodItems)); + } + // 排除播放列表为空的播放源 + for (int i = playFrom.size() - 1; i >= 0; i--) { + if (playFrom.get(i).isEmpty()) + playFrom.remove(i); + } + for (int i = playList.size() - 1; i >= 0; i--) { + if (playList.get(i).isEmpty()) + playList.remove(i); + } + for (int i = playList.size() - 1; i >= 0; i--) { + if (i >= playFrom.size()) + playList.remove(i); + } + String vod_play_from = TextUtils.join("$$$", playFrom); + String vod_play_url = TextUtils.join("$$$", playList); + vod.put("vod_play_from", vod_play_from); + vod.put("vod_play_url", vod_play_url); + + detailContentExt(webContent, vod); + + JSONObject result = new JSONObject(); + JSONArray list = new JSONArray(); + list.put(vod); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + fetchRule(); + String webUrl = rule.getPlayUrl().isEmpty() ? id : rule.getPlayUrl().replace("{playUrl}", id); + SpiderDebug.log(webUrl); + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("playUrl", ""); + if (!rule.getPlayUa().isEmpty()) { + result.put("ua", rule.getPlayUa()); + } + result.put("url", webUrl); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String searchContent(String key, boolean quick) { + try { + fetchRule(); + if (rule.getSearchUrl().isEmpty()) { + return ""; + } + String webUrl = rule.getSearchUrl().replace("{wd}", URLEncoder.encode(key)); + String webContent = fetch(webUrl); + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + // add maccms suggest search api support + if (rule.getSearchVodNode().startsWith("json:")) { + String[] node = rule.getSearchVodNode().substring(5).split(">"); + JSONObject data = new JSONObject(webContent); + for (int i = 0; i < node.length; i++) { + if (i == node.length - 1) { + JSONArray vodArray = data.getJSONArray(node[i]); + for (int j = 0; j < vodArray.length(); j++) { + JSONObject vod = vodArray.getJSONObject(j); + String name = vod.optString(rule.getSearchVodName()).trim(); + name = rule.getSearchVodNameR(name); + String id = vod.optString(rule.getSearchVodId()).trim(); + id = rule.getSearchVodIdR(id); + String pic = vod.optString(rule.getSearchVodImg()).trim(); + pic = rule.getSearchVodImgR(pic); + pic = Misc.fixUrl(webUrl, pic); + String mark = vod.optString(rule.getSearchVodMark()).trim(); + mark = rule.getSearchVodMarkR(mark); + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", name); + v.put("vod_pic", pic); + v.put("vod_remarks", mark); + videos.put(v); + } + } else { + data = data.getJSONObject(node[i]); + } + } + } else { + JXDocument doc = JXDocument.create(webContent); + List vodNodes = doc.selN(rule.getSearchVodNode()); + for (int i = 0; i < vodNodes.size(); i++) { + String name = vodNodes.get(i).selOne(rule.getSearchVodName()).asString().trim(); + name = rule.getSearchVodNameR(name); + String id = vodNodes.get(i).selOne(rule.getSearchVodId()).asString().trim(); + id = rule.getSearchVodIdR(id); + String pic = vodNodes.get(i).selOne(rule.getSearchVodImg()).asString().trim(); + pic = rule.getSearchVodImgR(pic); + pic = Misc.fixUrl(webUrl, pic); + String mark = ""; + if (!rule.getCateVodMark().isEmpty()) { + try { + mark = vodNodes.get(i).selOne(rule.getSearchVodMark()).asString().trim(); + mark = rule.getSearchVodMarkR(mark); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + JSONObject v = new JSONObject(); + v.put("vod_id", id); + v.put("vod_name", name); + v.put("vod_pic", pic); + v.put("vod_remarks", mark); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } catch ( + Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public boolean manualVideoCheck() { + return false; + } + + private String[] videoFormatList = new String[]{".m3u8", ".mp4", ".mpeg", ".flv"}; + + @Override + public boolean isVideoFormat(String url) { + url = url.toLowerCase(); + if (url.contains("=http") || url.contains("=https") || url.contains("=https%3a%2f") || url.contains("=http%3a%2f")) { + return false; + } + for (String format : videoFormatList) { + if (url.contains(format)) { + return true; + } + } + return false; + } + + protected String ext = null; + protected XPathRule rule = null; + + protected void fetchRule() { + if (rule == null) { + if (ext != null) { + if (ext.startsWith("http")) { + String json = OkHttpUtil.string(ext, null); + rule = XPathRule.fromJson(json); + loadRuleExt(json); + } else { + rule = XPathRule.fromJson(ext); + loadRuleExt(ext); + } + } + } + } + + protected void loadRuleExt(String json) { + + } + + protected String fetch(String webUrl) { + SpiderDebug.log(webUrl); + return OkHttpUtil.string(webUrl, getHeaders(webUrl)); + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathBde4.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathBde4.java new file mode 100644 index 0000000..799cd8d --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathBde4.java @@ -0,0 +1,135 @@ +package com.github.catvod.spider; + +import android.util.Base64; + +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OKCallBack; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import okhttp3.Call; +import okhttp3.Response; + +/** + * 需配合猫大的解密接口使用,没有接口不用尝试。 + */ + +public class XPathBde4 extends XPath { + + String tk = ""; + + @Override + protected void loadRuleExt(String json) { + try { + JSONObject jsonObj = new JSONObject(json); + tk = jsonObj.optString("decodeTk").trim(); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + @Override + protected String categoryUrl(String tid, String pg, boolean filter, HashMap extend) { + String url = rule.getCateUrl().replace("{catePg}", pg); + String s = "all"; + if (extend != null) { + if (extend.containsKey("s")) { + s = extend.get("s"); + } + } + url = url.replace("{cateId}", s); + url += "?"; + if (extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + if (key.equals("s")) + continue; + String value = extend.get(key); + if (value.length() > 0) { + url += (key + "=" + URLEncoder.encode(value) + "&"); + } + } + } + if (!tid.equals("*")) { + url += ("type=" + tid); + } + return url; + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + fetchRule(); + String webUrl = rule.getPlayUrl().isEmpty() ? id : rule.getPlayUrl().replace("{playUrl}", id); + SpiderDebug.log(webUrl); + String content = OkHttpUtil.string(webUrl, getHeaders(webUrl)); + String startFlag = "var m3u8 = \""; + int start = content.indexOf(startFlag); + start = start + startFlag.length(); + int end = content.indexOf("\";", start); + String m3u8 = content.substring(start, end).replace("\\", "").replace("https", "http"); + SpiderDebug.log(m3u8); + HashMap headers = getHeaders(m3u8); + + OKCallBack m3u8Callback = new OKCallBack() { + @Override + public String onParseResponse(Call call, Response response) { + try { + return new String(Base64.encode(response.body().bytes(), Base64.DEFAULT)); + } catch (IOException e) { + return ""; + } + } + + @Override + public void onFailure(Call call, Exception e) { + setResult(""); + SpiderDebug.log(e); + } + + @Override + public void onResponse(String response) { + } + }; + OkHttpUtil.get(OkHttpUtil.defaultClient(), m3u8, null, headers, m3u8Callback); + String m3u8Data = m3u8Callback.getResult(); + if (m3u8Data != null && !m3u8Data.isEmpty()) { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(); + json.put("data", m3u8Data); + json.put("t", tk); + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), "https://cat.idontcare.top/ssr/bde4", json.toString(), new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + } + + @Override + public void onResponse(String response) { + try { + result.put("url", response); + } catch (JSONException e) { + } + } + }); + result.put("parse", 0); + result.put("playUrl", ""); + if (!rule.getPlayUa().isEmpty()) { + result.put("ua", rule.getPlayUa()); + } + return result.toString(); + + } + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathEgg.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathEgg.java new file mode 100644 index 0000000..4a8ec16 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathEgg.java @@ -0,0 +1,113 @@ +package com.github.catvod.spider; + +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OKCallBack; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.List; + +import okhttp3.Call; + +public class XPathEgg extends XPath { + + String tk = ""; + + @Override + protected void loadRuleExt(String json) { + try { + JSONObject jsonObj = new JSONObject(json); + tk = jsonObj.optString("decodeTk").trim(); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + @Override + protected String categoryUrl(String tid, String pg, boolean filter, HashMap extend) { + if (Integer.parseInt(pg) <= 1) { + return rule.getCateUrl().replace("{cateId}", tid); + } + return (rule.getCateUrl() + "index_{catePg}.html").replace("{cateId}", tid).replace("{catePg}", pg); + } + + @Override + protected void detailContentExt(String content, JSONObject vod) { + try { + String startFlag = "infoid="; + int start = content.indexOf(startFlag); + start = start + startFlag.length(); + int end = content.indexOf(";", start); + String infoid = content.substring(start, end); + + startFlag = "link5='"; + start = content.indexOf(startFlag); + start = start + startFlag.length(); + end = content.indexOf("';", start); + String link5 = content.substring(start, end); + + JSONObject json = new JSONObject(); + json.put("infoid", infoid); + json.put("link5", link5); + json.put("t", tk); + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), "https://cat.idontcare.top/ssr/dandan", json.toString(), new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + } + + @Override + public void onResponse(String response) { + try { + JSONObject obj = new JSONObject(response); + vod.put("vod_play_from", obj.getString("vod_play_from")); + vod.put("vod_play_url", obj.getString("vod_play_url")); + } catch (JSONException e) { + } + } + }); + } catch (Exception e) { + + } + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + fetchRule(); + HashMap json = new HashMap<>(); + json.put("url", id); + HashMap headers = new HashMap<>(); + headers.put("accept", "*/*"); + headers.put("x-requested-with", "XMLHttpRequest"); + headers.put("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"); + headers.put("origin", "https://www.dandanzan.cc"); + headers.put("accept-language", "zh-CN,zh;q=0.9"); + JSONObject result = new JSONObject(); + OkHttpUtil.post(OkHttpUtil.defaultClient(), rule.getPlayUrl(), json, headers, new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + } + + @Override + public void onResponse(String response) { + try { + result.put("url", response); + } catch (JSONException e) { + } + } + }); + result.put("parse", 0); + result.put("playUrl", ""); + if (!rule.getPlayUa().isEmpty()) { + result.put("ua", rule.getPlayUa()); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathFilter.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathFilter.java new file mode 100644 index 0000000..7b2f0c2 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathFilter.java @@ -0,0 +1,36 @@ +package com.github.catvod.spider; + +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class XPathFilter extends XPath { + + @Override + protected void loadRuleExt(String json) { + super.loadRuleExt(json); + } + + @Override + protected String categoryUrl(String tid, String pg, boolean filter, HashMap extend) { + String cateUrl = rule.getCateUrl(); + if (filter && extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + if (value.length() > 0) { + cateUrl = cateUrl.replace("{" + key + "}", URLEncoder.encode(value)); + } + } + } + cateUrl = cateUrl.replace("{cateId}", tid).replace("{catePg}", pg); + Matcher m = Pattern.compile("\\{(.*?)\\}").matcher(cateUrl); + while (m.find()) { + String n = m.group(0).replace("{", "").replace("}", ""); + cateUrl = cateUrl.replace(m.group(0), "").replace("/" + n + "/", ""); + } + return cateUrl; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathMac.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathMac.java new file mode 100644 index 0000000..bdfa562 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathMac.java @@ -0,0 +1,196 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Base64; + +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; + +import org.json.JSONException; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; + +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class XPathMac extends XPath { + // 尝试分析直连 + private boolean decodePlayUrl; + // 尝试匹配官源标识以调用应用配置中的解析列表 + private boolean decodeVipFlag; + // 播放器配置js + private String playerConfigJs = ""; + // 播放器配置js取值正则 + private String playerConfigJsRegex = "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list"; + // 站点里播放源对应的真实官源 + private HashMap show2VipFlag = new HashMap<>(); + + /** + * mac cms 直连和官源调用应用内播放列表支持 + * + * @param context + * @param extend + */ + public void init(Context context, String extend) { + super.init(context, extend); + + } + + @Override + protected void loadRuleExt(String json) { + try { + JSONObject jsonObj = new JSONObject(json); + decodePlayUrl = jsonObj.optBoolean("dcPlayUrl", false); + decodeVipFlag = jsonObj.optBoolean("dcVipFlag", false); + JSONObject dcShow2Vip = jsonObj.optJSONObject("dcShow2Vip"); + if (dcShow2Vip != null) { + Iterator keys = dcShow2Vip.keys(); + while (keys.hasNext()) { + String name = keys.next(); + show2VipFlag.put(name.trim(), dcShow2Vip.getString(name).trim()); + } + } + playerConfigJs = jsonObj.optString("pCfgJs").trim(); + playerConfigJsRegex = jsonObj.optString("pCfgJsR", playerConfigJsRegex).trim(); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + + @Override + public String homeContent(boolean filter) { + String result = super.homeContent(filter); + if (result.length() > 0 && playerConfigJs.length() > 0) { // 尝试通过playerConfigJs获取展示和flag匹配关系 + String webContent = fetch(playerConfigJs); + Matcher matcher = Pattern.compile(playerConfigJsRegex).matcher(webContent); + if (matcher.find()) { + try { + JSONObject jsonObject = new JSONObject(matcher.group(1)); + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + JSONObject keyObj = jsonObject.optJSONObject(key); + if (keyObj == null) + continue; + String show = keyObj.optString("show").trim(); + if (show.isEmpty()) + continue; + show2VipFlag.put(show, key); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + } + // SpiderDebug.log(webContent); + } + return result; + } + + @Override + public String detailContent(List ids) { + String result = super.detailContent(ids); + if (decodeVipFlag && result.length() > 0) { + try { + JSONObject jsonObject = new JSONObject(result); + String playFrom[] = jsonObject.optJSONArray("list").getJSONObject(0).optString("vod_play_from").split("\\$\\$\\$"); + if (playFrom.length > 0) { + for (int i = 0; i < playFrom.length; i++) { + if (show2VipFlag.containsKey(playFrom[i])) { + playFrom[i] = show2VipFlag.get(playFrom[i]); + } + } + jsonObject.optJSONArray("list").getJSONObject(0).put("vod_play_from", TextUtils.join("$$$", playFrom)); + result = jsonObject.toString(); + } + } catch (Throwable th) { + SpiderDebug.log(th); + } + } + return result; + } + + + @Override + public String playerContent(String flag, String id, List vipFlags) { + fetchRule(); + String webUrl = rule.getPlayUrl().isEmpty() ? id : rule.getPlayUrl().replace("{playUrl}", id); + String videoUrl = null; + // 尝试分析直连 + if (decodePlayUrl) { + try { + Document doc = Jsoup.parse(fetch(webUrl)); + Elements allScript = doc.select("script"); + for (int i = 0; i < allScript.size(); i++) { + String scContent = allScript.get(i).html().trim(); + if (scContent.startsWith("var player_")) { + int start = scContent.indexOf('{'); + int end = scContent.lastIndexOf('}') + 1; + String json = scContent.substring(start, end); + JSONObject player = new JSONObject(json); + String videoUrlTmp = player.getString("url"); + if (player.has("encrypt")) { + int encrypt = player.getInt("encrypt"); + if (encrypt == 1) { + videoUrlTmp = URLDecoder.decode(videoUrlTmp); + } else if (encrypt == 2) { + videoUrlTmp = new String(Base64.decode(videoUrlTmp, Base64.DEFAULT)); + videoUrlTmp = URLDecoder.decode(videoUrlTmp); + } + } + videoUrl = videoUrlTmp; + break; + } + } + } catch (Exception e) { + SpiderDebug.log(e); + } + } + if (videoUrl != null) { + // 适配2.0.6的调用应用内解析列表的支持, 需要配合直连分析和匹配官源解析一起使用,参考cjt影视和极品直连 + if (decodeVipFlag && Misc.isVip(videoUrl)) { // 使用jx:1 + try { + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("jx", "1"); + result.put("url", videoUrl); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + } else if (decodeVipFlag && vipFlags.contains(flag)) { // 是否使用应用内解析列表解析官源 + try { + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", videoUrl); + result.put("header", ""); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + // 如果是视频直连 直接返回免解 + else if (isVideoFormat(videoUrl)) { + try { + JSONObject result = new JSONObject(); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", videoUrl); + result.put("header", ""); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + } + // 上述都失败了就按默认模式走 + return super.playerContent(flag, id, vipFlags); + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathMacFilter.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathMacFilter.java new file mode 100644 index 0000000..056f80f --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathMacFilter.java @@ -0,0 +1,30 @@ +package com.github.catvod.spider; + +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class XPathMacFilter extends XPathMac { + @Override + protected String categoryUrl(String tid, String pg, boolean filter, HashMap extend) { + String cateUrl = rule.getCateUrl(); + if (filter && extend != null && extend.size() > 0) { + for (Iterator it = extend.keySet().iterator(); it.hasNext(); ) { + String key = it.next(); + String value = extend.get(key); + if (value.length() > 0) { + cateUrl = cateUrl.replace("{" + key + "}", URLEncoder.encode(value)); + } + } + } + cateUrl = cateUrl.replace("{cateId}", tid).replace("{catePg}", pg); + Matcher m = Pattern.compile("\\{(.*?)\\}").matcher(cateUrl); + while (m.find()) { + String n = m.group(0).replace("{", "").replace("}", ""); + cateUrl = cateUrl.replace(m.group(0), "").replace("/" + n + "/", ""); + } + return cateUrl; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathNfMov.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathNfMov.java new file mode 100644 index 0000000..918f74b --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/XPathNfMov.java @@ -0,0 +1,36 @@ +package com.github.catvod.spider; + +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class XPathNfMov extends XPath { + + private String cookies = ""; + + @Override + protected HashMap getHeaders(String url) { + HashMap hashMap = super.getHeaders(url); + if (cookies.length() > 0) + hashMap.put("Cookie", cookies); + return hashMap; + } + + @Override + protected String fetch(String webUrl) { + SpiderDebug.log(webUrl); + Map> respHeaders = new TreeMap<>(); + String content = OkHttpUtil.string(webUrl, getHeaders(webUrl), respHeaders); + if (content.contains("http-equiv=\"refresh\"")) { + if (respHeaders.containsKey("set-cookie")) { + cookies = respHeaders.get("set-cookie").get(0); + } + content = OkHttpUtil.string(webUrl, getHeaders(webUrl)); + } + return content; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Ysdq.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Ysdq.java new file mode 100644 index 0000000..1153366 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Ysdq.java @@ -0,0 +1,275 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class Ysdq extends Spider { + private static HashMap sites = new HashMap<>(); + + protected void fetchRule() { + if (sites.size() == 0) { + try { + String json = OkHttpUtil.string("https://pj567.coding.net/p/source/d/source/git/raw/master/mobile/config.json", null); + JSONArray sources = new JSONObject(json).optJSONArray("source"); + for (int i = 0; i < sources.length(); i++) { + JSONObject obj = sources.getJSONObject(i); + String type = obj.optString("type"); + if (type.equals("AppV0") || type.equals("AppTv") || type.equals("aiKanTv")) { + String scName = obj.optString("sourceName"); + sites.put(scName, obj); + SpiderDebug.log("{\"key\":\"csp_ysdq_" + scName + "\", \"name\":\"" + scName + "(搜)\", \"type\":3, \"api\":\"csp_Ysdq\",\"searchable\":1,\"quickSearch\":0, \"ext\":\"" + scName + "\"},"); + } + } + } catch (Exception e) { + + } + } + } + + private JSONObject getJson() { + if (sites.containsKey(sourceName)) { + return sites.get(sourceName); + } + return null; + } + + private String sourceName = ""; + + @Override + public void init(Context context, String extend) { + super.init(context, extend); + this.sourceName = extend; + } + + private String jsonArr2Str(JSONArray array) { + try { + ArrayList strings = new ArrayList<>(); + for (int i = 0; i < array.length(); i++) { + strings.add(array.getString(i)); + } + return TextUtils.join(",", strings); + } catch (JSONException e) { + } + return ""; + } + + @Override + public String detailContent(List ids) { + try { + fetchRule(); + JSONObject cfg = getJson(); + if (cfg != null) { + String type = cfg.optString("type"); + JSONObject result = new JSONObject(); + JSONObject vod = new JSONObject(); + if (type.equals("AppV0")) { + String url = cfg.optString("detailUrl").replace("%s", ids.get(0)); + String json = OkHttpUtil.string(url, null); + JSONObject obj = new JSONObject(json).optJSONObject("data"); + vod.put("vod_id", obj.getString("vod_id")); + vod.put("vod_name", obj.getString("vod_name")); + vod.put("vod_pic", obj.getString("vod_pic")); + vod.put("type_name", obj.optString("vod_class")); + vod.put("vod_year", obj.optString("vod_year")); + vod.put("vod_lang", obj.optString("vod_lang")); + vod.put("vod_area", obj.optString("vod_area")); + vod.put("vod_remarks", obj.optString("vod_remarks")); + vod.put("vod_actor", obj.optString("vod_actor")); + vod.put("vod_director", obj.optString("vod_director")); + vod.put("vod_content", obj.optString("vod_content")); + vod.put("vod_play_from", obj.getString("vod_play_from")); + vod.put("vod_play_url", obj.getString("vod_play_url")); + } else if (type.equals("AppTV")) { + String url = ids.get(0); + String json = OkHttpUtil.string(url, null); + JSONObject obj = new JSONObject(json); + vod.put("vod_id", obj.getString("id")); + vod.put("vod_name", obj.getString("title")); + vod.put("vod_pic", obj.getString("img_url")); + vod.put("type_name", jsonArr2Str(obj.optJSONArray("type"))); + vod.put("vod_year", obj.optString("pubtime")); + vod.put("vod_area", jsonArr2Str(obj.optJSONArray("area"))); + vod.put("vod_remarks", obj.optString("trunk")); + vod.put("vod_actor", jsonArr2Str(obj.optJSONArray("actor"))); + vod.put("vod_director", jsonArr2Str(obj.optJSONArray("director"))); + vod.put("vod_content", obj.optString("intro")); + Map vod_play = new LinkedHashMap<>(); + JSONObject playList = obj.getJSONObject("videolist"); + Iterator playListKeys = playList.keys(); + while (playListKeys.hasNext()) { + String from = playListKeys.next(); + JSONArray playListUrls = playList.getJSONArray(from); + ArrayList urls = new ArrayList<>(); + for (int j = 0; j < playListUrls.length(); j++) { + JSONObject urlObj = playListUrls.getJSONObject(j); + urls.add(urlObj.getString("title") + "$" + urlObj.getString("url")); + } + vod_play.put(from, TextUtils.join("#", urls)); + } + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vod.put("vod_play_from", vod_play_from); + vod.put("vod_play_url", vod_play_url); + } else if (type.equals("aiKanTv")) { + String url = cfg.optString("detailUrl").replace("%s", ids.get(0)); + String json = OkHttpUtil.string(url, null); + JSONObject obj = new JSONObject(json).optJSONObject("data"); + vod.put("vod_id", obj.getString("vod_id")); + vod.put("vod_name", obj.getString("vod_name")); + vod.put("vod_pic", obj.getString("vod_pic")); + vod.put("type_name", obj.optString("vod_class")); + vod.put("vod_year", obj.optString("vod_year")); + vod.put("vod_lang", obj.optString("vod_lang")); + vod.put("vod_area", obj.optString("vod_area")); + vod.put("vod_remarks", obj.optString("vod_remarks")); + vod.put("vod_actor", obj.optString("vod_actor")); + vod.put("vod_director", obj.optString("vod_director")); + vod.put("vod_content", obj.optString("vod_content")); + + Map vod_play = new LinkedHashMap<>(); + JSONArray playList = obj.getJSONArray("vod_play_list"); + for (int i = 0; i < playList.length(); i++) { + JSONObject playListObj = playList.getJSONObject(i); + String from = playListObj.getString("from"); + String playUrls = playListObj.getString("url"); + if (playUrls.length() > 0) { + vod_play.put(from, playUrls); + } + } + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vod.put("vod_play_from", vod_play_from); + vod.put("vod_play_url", vod_play_url); + } + JSONArray list = new JSONArray(); + list.put(vod); + result.put("list", list); + return result.toString(); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + fetchRule(); + JSONObject cfg = getJson(); + if (cfg != null) { + String type = cfg.optString("type"); + String url = cfg.optString("searchUrl"); + if (!url.isEmpty()) { + JSONObject result = new JSONObject(); + JSONArray videos = new JSONArray(); + url = url.replace("%s", URLEncoder.encode(key)); + String json = OkHttpUtil.string(url, null); + if (type.equals("AppV0")) { + JSONArray list = new JSONObject(json).optJSONArray("list"); + for (int i = 0; i < list.length(); i++) { + JSONObject obj = list.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", obj.getString("vod_id")); + v.put("vod_name", obj.getString("vod_name")); + v.put("vod_pic", obj.getString("vod_pic")); + v.put("vod_remarks", obj.getString("vod_remarks")); + videos.put(v); + } + } else if (type.equals("AppTV")) { + JSONArray list = new JSONObject(json).optJSONArray("data"); + for (int i = 0; i < list.length(); i++) { + JSONObject obj = list.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", obj.getString("nextlink")); + v.put("vod_name", obj.getString("title")); + v.put("vod_pic", obj.getString("pic")); + v.put("vod_remarks", obj.getString("state")); + videos.put(v); + } + } else if (type.equals("aiKanTv")) { + JSONArray list = new JSONObject(json).optJSONObject("data").optJSONArray("list"); + for (int i = 0; i < list.length(); i++) { + JSONObject obj = list.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", obj.getString("vod_id")); + v.put("vod_name", obj.getString("vod_name")); + v.put("vod_pic", obj.getString("vod_pic")); + v.put("vod_remarks", obj.getString("vod_remarks")); + videos.put(v); + } + } + result.put("list", videos); + return result.toString(); + } + } + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + fetchRule(); + if (vipFlags.contains(flag)) { + try { + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", id); + result.put("header", ""); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + // 如果是视频直连 直接返回免解 + else if (isVideoFormat(id)) { + try { + JSONObject result = new JSONObject(); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", id); + result.put("header", ""); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + } catch (Exception e) { + SpiderDebug.log(e); + } + // 尝试解析 + try { + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", id); + result.put("header", ""); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Ysgc.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Ysgc.java new file mode 100644 index 0000000..ffc446a --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/Ysgc.java @@ -0,0 +1,394 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.Misc; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 影视工厂 + *

+ * Author: 小黄瓜 + */ +public class Ysgc extends Spider { + + private static final String siteUrl = "https://www.ik4.cc"; + private static final String siteHost = "www.ik4.cc"; + + protected JSONObject playerConfig = new JSONObject(); + + @Override + public void init(Context context) { + super.init(context); + } + + protected HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("user-agent", "Dart/2.14 (dart:io)"); + headers.put("version", "1.9.8"); + headers.put("copyright", "xiaogui"); + headers.put("host", siteHost); + headers.put("platform", "android"); + return headers; + } + + @Override + public String homeContent(boolean filter) { + try { + String url = siteUrl + "/xgapp.php/v1/nav?token="; + JSONObject jsonObject = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONArray jsonArray = jsonObject.getJSONArray("data"); + JSONArray classes = new JSONArray(); + JSONObject filterConfig = new JSONObject(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jObj = jsonArray.getJSONObject(i); + String typeName = jObj.getString("type_name"); + if (typeName.equals("电视直播")) + continue; + String typeId = jObj.getString("type_id"); + JSONObject newCls = new JSONObject(); + newCls.put("type_id", typeId); + newCls.put("type_name", typeName); + classes.put(newCls); + try { + JSONObject typeExtend = jObj.getJSONObject("type_extend"); + Iterator typeExtendKeys = typeExtend.keys(); + JSONArray extendsAll = new JSONArray(); + while (typeExtendKeys.hasNext()) { + String typeExtendKey = typeExtendKeys.next(); + String typeExtendName = null; + switch (typeExtendKey) { + case "class": + typeExtendName = "类型"; + break; + case "area": + typeExtendName = "地区"; + break; + case "lang": + typeExtendName = "语言"; + break; + case "year": + typeExtendName = "年代"; + break; + } + if (typeExtendName == null) { + SpiderDebug.log(typeExtendKey); + continue; + } + String typeExtendStr = typeExtend.getString(typeExtendKey); + if (typeExtendStr.trim().length() == 0) + continue; + String[] newTypeExtendKeys = typeExtendStr.split(","); + JSONObject newTypeExtend = new JSONObject(); + newTypeExtend.put("key", typeExtendKey); + newTypeExtend.put("name", typeExtendName); + JSONArray newTypeExtendKV = new JSONArray(); + { + JSONObject kvAll = new JSONObject(); + kvAll.put("n", "全部"); + kvAll.put("v", ""); + newTypeExtendKV.put(kvAll); + } + for (String k : newTypeExtendKeys) { + if (typeExtendName.equals("伦理")) + continue; + JSONObject kv = new JSONObject(); + kv.put("n", k); + kv.put("v", k); + newTypeExtendKV.put(kv); + } + newTypeExtend.put("value", newTypeExtendKV); + extendsAll.put(newTypeExtend); + } + filterConfig.put(typeId, extendsAll); + } catch (Exception e) { + SpiderDebug.log(e); + } + + } + JSONObject result = new JSONObject(); + result.put("class", classes); + if (filter) { + result.put("filters", filterConfig); + } + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String homeVideoContent() { + try { + String url = siteUrl + "/xgapp.php/v1/index_video?token="; + JSONObject jsonObject = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONArray jsonArray = jsonObject.getJSONArray("data"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jObj = jsonArray.getJSONObject(i); + JSONArray videoList = jObj.getJSONArray("vlist"); + for (int j = 0; j < videoList.length() && j < 6; j++) { + JSONObject vObj = videoList.getJSONObject(j); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("vod_id")); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + String url = siteUrl + "/xgapp.php/v1/video?tid=" + tid + "&pg=" + pg + "&token="; + Set keys = extend.keySet(); + for (String key : keys) { + url += "&" + key + "=" + URLEncoder.encode(extend.get(key)); + } + JSONObject dataObject = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONArray jsonArray = dataObject.getJSONArray("data"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("vod_id")); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } + JSONObject result = new JSONObject(); + int limit = 20; + int page = dataObject.getInt("page"); + int total = dataObject.getInt("total"); + int pageCount = dataObject.getInt("pagecount"); + result.put("page", page); + result.put("pagecount", pageCount); + result.put("limit", limit); + result.put("total", total); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String detailContent(List ids) { + try { + String url = siteUrl + "/xgapp.php/v1/video_detail?id=" + ids.get(0) + "&token="; + JSONObject jsonObject = new JSONObject(OkHttpUtil.string(url, getHeaders(url))); + JSONObject dataObject = jsonObject.getJSONObject("data"); + if (dataObject.has("vod_info")) { + try { + dataObject = dataObject.getJSONObject("vod_info"); + } catch (Exception e) { + + } + } + JSONObject vodList = new JSONObject(); + vodList.put("vod_id", dataObject.getString("vod_id")); + vodList.put("vod_name", dataObject.getString("vod_name")); + vodList.put("vod_pic", dataObject.getString("vod_pic")); + vodList.put("type_name", dataObject.getString("vod_class")); + vodList.put("vod_year", dataObject.getString("vod_year")); + vodList.put("vod_area", dataObject.getString("vod_area")); + vodList.put("vod_remarks", dataObject.getString("vod_remarks")); + vodList.put("vod_actor", dataObject.getString("vod_actor")); + vodList.put("vod_director", dataObject.getString("vod_director")); + vodList.put("vod_content", dataObject.getString("vod_content")); + JSONArray playerList = dataObject.getJSONArray("vod_url_with_player"); + List playFlags = new ArrayList<>(); + HashMap playUrls = new HashMap<>(); + for (int i = 0; i < playerList.length(); i++) { + JSONObject playerListObj = playerList.getJSONObject(i); + String from = playerListObj.getString("code"); + playUrls.put(from, playerListObj.getString("url")); + playerListObj.remove("url"); + playerConfig.put(from, playerListObj); + playFlags.add(from); + } + + Map vod_play = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + int sort1 = playFlags.indexOf(o1); + int sort2 = playFlags.indexOf(o2); + + if (sort1 == sort2) { + return 1; + } + return sort1 - sort2 > 0 ? 1 : -1; + } + }); + + String[] vod_play_from_list = dataObject.getString("vod_play_from").split("\\$\\$\\$"); + String[] vod_play_url_list = dataObject.getString("vod_play_url").split("\\$\\$\\$"); + + for (int i = 0; i < vod_play_from_list.length; i++) { + String from = vod_play_from_list[i]; + if (i >= vod_play_url_list.length || vod_play_url_list[i].trim().length() == 0) { + if (playUrls.containsKey(from) && playUrls.get(from).trim().length() > 0) { + vod_play.put(vod_play_from_list[i], playUrls.get(from)); + } + continue; + } + vod_play.put(vod_play_from_list[i], vod_play_url_list[i]); + } + + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodList.put("vod_play_from", vod_play_from); + vodList.put("vod_play_url", vod_play_url); + + JSONObject result = new JSONObject(); + JSONArray list = new JSONArray(); + list.put(vodList); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + private HashMap getHeaderJxs(String url) { + HashMap headers = new HashMap<>(); + headers.put("platform_version", "LMY47I"); + headers.put("user-agent", "Dart/2.12 (dart:io)"); + headers.put("version", "1.6.4"); + headers.put("copyright", "xiaogui"); + headers.put("platform", "android"); + headers.put("client_name", "6L+95Ymn6L6+5Lq6"); + return headers; + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + JSONObject result = new JSONObject(); + if (id.contains("hsl.ysgc.xyz")) { + String uuu = "https://www.ysgc.cc/static/player/dplayer.php?url=" + id; + HashMap headers = new HashMap(); + headers.put("referer", "https://www.ysgc.cc/"); + String content = OkHttpUtil.string(uuu, headers); + Matcher matcher = Pattern.compile("url: url\\+'(.+?)',").matcher(content); + if (matcher.find()) { + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", id + matcher.group(1)); + result.put("header", "{\"Referer\":\" https://www.ysgc.cc\"}"); + } else { + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", uuu); + result.put("header", "{\"Referer\":\"https://www.ysgc.cc/\"}"); + } + return result.toString(); + } else if (id.contains("duoduozy.com") || id.contains("m3u8.cache.suoyo.cc")) { + String uuu = "https://www.6080kan.cc/app.php?url=" + id; + String content = OkHttpUtil.string(uuu, null); + return Misc.jsonParse(id, content).toString(); + } + if (vipFlags.contains(flag)) { + try { + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", id); + return result.toString(); + } catch (Exception ee) { + SpiderDebug.log(ee); + } + } + JSONObject playerObj = playerConfig.getJSONObject(flag); + String parseUrl = playerObj.getString("parse_api") + id; + String content = OkHttpUtil.string(parseUrl, getHeaderJxs(parseUrl)); + JSONObject playerData = new JSONObject(content); + JSONObject headers = new JSONObject(); + String ua = playerData.optString("user-agent", ""); + if (ua.trim().length() > 0) { + headers.put("User-Agent", " " + ua); + } + String referer = playerData.optString("referer", ""); + if (referer.trim().length() > 0) { + headers.put("Referer", " " + referer); + } + result.put("parse", 0); + result.put("header", headers.toString()); + result.put("playUrl", ""); + result.put("url", playerData.getString("url")); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + if (vipFlags.contains(flag)) { + try { + JSONObject result = new JSONObject(); + result.put("parse", 1); + result.put("playUrl", ""); + result.put("url", id); + return result.toString(); + } catch (Exception ee) { + SpiderDebug.log(ee); + } + } + } + return ""; + } + + @Override + public String searchContent(String key, boolean quick) { + if (quick) + return ""; + try { + String url = siteUrl + "/xgapp.php/v1/search?text=" + URLEncoder.encode(key) + "&pg=1"; + String content = OkHttpUtil.string(url, getHeaders(url)); + JSONObject dataObject = new JSONObject(content); + JSONArray jsonArray = dataObject.getJSONArray("data"); + JSONArray videos = new JSONArray(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("vod_id")); + v.put("vod_name", vObj.getString("vod_name")); + v.put("vod_pic", vObj.getString("vod_pic")); + v.put("vod_remarks", vObj.getString("vod_remarks")); + videos.put(v); + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/YydsAli1.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/YydsAli1.java new file mode 100644 index 0000000..f3df733 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/spider/YydsAli1.java @@ -0,0 +1,517 @@ +package com.github.catvod.spider; + +import android.content.Context; +import android.text.TextUtils; + +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.utils.okhttp.OKCallBack; +import com.github.catvod.utils.okhttp.OkHttpUtil; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import okhttp3.Call; + +public class YydsAli1 extends Spider { + private static final String siteUrl = "https://cmn.yyds.fans"; + + @Override + public void init(Context context) { + super.init(context); + } + + private HashMap getHeaders(String url) { + HashMap headers = new HashMap<>(); + headers.put("accept", "application/json, text/plain, */*"); + headers.put("origin", "https://yyds.fans"); + headers.put("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"); + return headers; + } + + @Override + public String homeContent(boolean filter) { + try { + String url = siteUrl + "/api/categories"; + String content = OkHttpUtil.string(url, getHeaders(url)); + JSONObject jsonObject = new JSONObject(content); + JSONArray jsonArray = jsonObject.getJSONArray("data"); + JSONArray classes = new JSONArray(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jObj = jsonArray.getJSONObject(i); + String typeName = jObj.getString("title"); + if (typeName.equals("首页") || typeName.equals("热门精选")) { + continue; + } + String typeId = jObj.getString("id"); + JSONObject newCls = new JSONObject(); + newCls.put("type_id", typeId); + newCls.put("type_name", typeName); + classes.put(newCls); + } + JSONObject result = new JSONObject(); + result.put("class", classes); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String homeVideoContent() { + try { + JSONArray videos = new JSONArray(); + try { + String url = siteUrl + "/api/posts"; + JSONObject json = new JSONObject(); + json.put("category_id", "0"); + json.put("skip", "0"); + json.put("limit", "30"); + json.put("keyword", ""); + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), url, json.toString(), getHeaders(url), new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + } + + @Override + public void onResponse(String response) { + try { + JSONObject jsonObject = new JSONObject(response); + JSONArray jsonArray = jsonObject.getJSONArray("data"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("id")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.optString("cover")); + String mark = vObj.optString("subtitle"); + if (mark.equals("null")) + mark = ""; + v.put("vod_remarks", mark); + videos.put(v); + } + } catch (JSONException e) { + } + } + }); + } catch (Exception e) { + + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + try { + int limit = 30; + int page = Integer.parseInt(pg); + if (page == 0) { + page = 1; + } + String url = siteUrl + "/api/posts"; + JSONObject json = new JSONObject(); + json.put("category_id", tid); + json.put("skip", ((page - 1) * limit) + ""); + json.put("limit", limit + ""); + json.put("keyword", ""); + JSONArray videos = new JSONArray(); + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), url, json.toString(), getHeaders(url), new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + } + + @Override + public void onResponse(String response) { + try { + JSONObject jsonObject = new JSONObject(response); + JSONArray jsonArray = jsonObject.getJSONArray("data"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("id")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.optString("cover")); + String mark = vObj.optString("subtitle"); + if (mark.equals("null")) + mark = ""; + v.put("vod_remarks", mark); + videos.put(v); + } + } catch (JSONException e) { + } + } + }); + JSONObject result = new JSONObject(); + result.put("page", page); + int pageCount = videos.length() == limit ? page + 1 : page; + result.put("pagecount", pageCount); + result.put("limit", limit); + result.put("total", Integer.MAX_VALUE); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + Pattern aliyun = Pattern.compile(".*?aliyundrive.com/s/(\\w+)[^\\w]?"); + Pattern aliyunShort = Pattern.compile(".*?alywp.net/(\\w+)[^\\w]?"); + + @Override + public String detailContent(List ids) { + try { + String url = siteUrl + "/api/post-info"; + JSONObject json = new JSONObject(); + json.put("id", ids.get(0)); + JSONObject vodAtom = new JSONObject(); + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), url, json.toString(), getHeaders(url), new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + } + + @Override + public void onResponse(String response) { + try { + JSONObject dataObject = new JSONObject(response); + JSONObject vObj = dataObject.getJSONObject("data"); + + vodAtom.put("vod_id", vObj.getString("id")); + vodAtom.put("vod_name", vObj.getString("title")); + vodAtom.put("vod_pic", vObj.getString("cover")); + vodAtom.put("type_name", ""); + vodAtom.put("vod_year", vObj.getString("year")); + vodAtom.put("vod_area", vObj.getString("region")); + String mark = vObj.optString("subtitle"); + if (mark.equals("null")) + mark = ""; + vodAtom.put("vod_remarks", mark); + vodAtom.put("vod_actor", vObj.getString("actors")); + vodAtom.put("vod_director", vObj.getString("director")); + String desc = vObj.optString("desc"); + if (desc.equals("null")) + desc = ""; + vodAtom.put("vod_content", desc); + + Map vod_play = new LinkedHashMap<>(); + JSONArray jsonArray = vObj.getJSONArray("links"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject obj = jsonArray.getJSONObject(i); + String link = obj.optString("link", ""); + updatePlaylist(link, vod_play); + } + if (vod_play.size() == 0 && vObj.optString("content").length() > 0) { + updatePlaylist(vObj.optString("content"), vod_play); + } + if (vod_play.size() > 0) { + String vod_play_from = TextUtils.join("$$$", vod_play.keySet()); + String vod_play_url = TextUtils.join("$$$", vod_play.values()); + vodAtom.put("vod_play_from", vod_play_from); + vodAtom.put("vod_play_url", vod_play_url); + } + } catch (JSONException e) { + } + } + }); + JSONObject result = new JSONObject(); + JSONArray list = new JSONArray(); + list.put(vodAtom); + result.put("list", list); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + private void updatePlaylist(String link, Map vod_play) { + String shareId = null; + Matcher matcher = aliyun.matcher(link); + if (matcher.find()) { + shareId = matcher.group(1); + } else { + matcher = aliyunShort.matcher(link); + if (matcher.find()) { + shareId = matcher.group(1); + Map> respHeaders = new TreeMap<>(); + OkHttpUtil.stringNoRedirect("https://alywp.net/" + shareId, null, respHeaders); + shareId = OkHttpUtil.getRedirectLocation(respHeaders); + if (shareId != null) { + matcher = aliyun.matcher(shareId); + if (matcher.find()) { + shareId = matcher.group(1); + } + } + } + } + if (shareId != null) { + String shareToken = getShareTk(shareId, ""); + ArrayList vodItems = new ArrayList<>(); + getFileList(shareToken, shareId, "", "root", vodItems); + vod_play.put("阿里云盘" + (vod_play.size() > 0 ? vod_play.size() : ""), TextUtils.join("#", vodItems)); + } + } + + @Override + public String playerContent(String flag, String id, List vipFlags) { + try { + String[] infos = id.split("\\+"); + String shareTk = getShareTk(infos[0], ""); + refreshTk(); + if (!accessTk.isEmpty()) { + JSONObject json = new JSONObject(); + json.put("share_id", infos[0]); + json.put("category", "live_transcoding"); + json.put("file_id", infos[1]); + json.put("template_id", ""); + HashMap headers = new HashMap<>(); + headers.put("x-share-token", shareTk); + headers.put("authorization", accessTk); + OKCallBack.OKCallBackString callback = new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + } + + @Override + public void onResponse(String response) { + String videoUrl = ""; + try { + JSONArray playList = new JSONObject(response).getJSONObject("video_preview_play_info").getJSONArray("live_transcoding_task_list"); + String[] orders = new String[]{"FHD", "HD", "SD"}; + for (String or : orders) { + for (int i = 0; i < playList.length(); i++) { + JSONObject obj = playList.getJSONObject(i); + if (obj.optString("template_id").equals(or)) { + videoUrl = obj.getString("url"); + break; + } + } + if (!videoUrl.isEmpty()) + break; + } + if (videoUrl.isEmpty() && playList.length() > 0) { + videoUrl = playList.getJSONObject(0).getString("url"); + } + } catch (JSONException e) { + } finally { + setResult(videoUrl); + } + } + }; + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), "https://api.aliyundrive.com/v2/file/get_share_link_video_preview_play_info", json.toString(), headers, callback); + JSONArray playList = new JSONObject(callback.getResult()).getJSONObject("video_preview_play_info").getJSONArray("live_transcoding_task_list"); + String videoUrl = ""; + String[] orders = new String[]{"FHD", "HD", "SD"}; + for (String or : orders) { + for (int i = 0; i < playList.length(); i++) { + JSONObject obj = playList.getJSONObject(i); + if (obj.optString("template_id").equals(or)) { + videoUrl = obj.getString("url"); + break; + } + } + if (!videoUrl.isEmpty()) + break; + } + if (videoUrl.isEmpty() && playList.length() > 0) { + videoUrl = playList.getJSONObject(0).getString("url"); + } + JSONObject headerObj = new JSONObject(); + headerObj.put("user-agent", " Dalvik/2.1.0 (Linux; U; Android 7.0; ZTE BA520 Build/MRA58K)"); + headerObj.put("referer", " https://www.aliyundrive.com/"); + JSONObject result = new JSONObject(); + result.put("parse", 0); + result.put("playUrl", ""); + result.put("url", videoUrl); + result.put("header", headerObj.toString()); + return result.toString(); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + @Override + public String searchContent(String key, boolean quick) { + try { + if (quick) + return ""; + JSONArray videos = new JSONArray(); + try { + String url = siteUrl + "/api/posts"; + JSONObject json = new JSONObject(); + json.put("category_id", "-1"); + json.put("skip", "0"); + json.put("limit", "30"); + json.put("keyword", key); + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), url, json.toString(), getHeaders(url), new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + } + + @Override + public void onResponse(String response) { + try { + JSONObject jsonObject = new JSONObject(response); + JSONArray jsonArray = jsonObject.getJSONArray("data"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject vObj = jsonArray.getJSONObject(i); + JSONObject v = new JSONObject(); + v.put("vod_id", vObj.getString("id")); + v.put("vod_name", vObj.getString("title")); + v.put("vod_pic", vObj.getString("cover")); + v.put("vod_remarks", vObj.getString("subtitle")); + videos.put(v); + } + } catch (JSONException e) { + } + } + }); + + } catch (Exception e) { + + } + JSONObject result = new JSONObject(); + result.put("list", videos); + return result.toString(); + } catch (Exception e) { + SpiderDebug.log(e); + } + return ""; + } + + private String aliTk = ""; + + private void fetchAliTk() { + if (aliTk.isEmpty()) { + try { + HashMap headers = new HashMap<>(); + headers.put("User-Agent", "okhttp/4.5.0"); + aliTk = new JSONObject(OkHttpUtil.string("http://81.68.244.5/tv/alitk", headers)).optString("alitk"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + } + + private String accessTk = ""; + + private void refreshTk() { + fetchAliTk(); + if (!aliTk.isEmpty() && accessTk.isEmpty()) { + try { + JSONObject json = new JSONObject(); + json.put("refresh_token", aliTk); + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), "https://api.aliyundrive.com/token/refresh", json.toString(), new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + + } + + @Override + public void onResponse(String response) { + try { + JSONObject obj = new JSONObject(response); + accessTk = obj.getString("token_type") + " " + obj.getString("access_token"); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + }); + } catch (JSONException e) { + SpiderDebug.log(e); + } + } + } + + private String getShareTk(String shareId, String sharePwd) { + try { + JSONObject json = new JSONObject(); + json.put("share_id", shareId); + json.put("share_pwd", ""); + OKCallBack.OKCallBackString callback = new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + + } + + @Override + public void onResponse(String response) { + + } + }; + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), "https://api.aliyundrive.com/v2/share_link/get_share_token", json.toString(), callback); + return new JSONObject(callback.getResult()).optString("share_token"); + } catch (JSONException e) { + SpiderDebug.log(e); + } + return ""; + } + + + private void getFileList(String shareTk, String shareId, String sharePwd, String root, ArrayList vodItems) { + try { + // 取文件夹 + JSONObject json = new JSONObject(); + json.put("share_id", shareId); + json.put("parent_file_id", root); + json.put("limit", 100); + json.put("image_thumbnail_process", "image/resize,w_160/format,jpeg"); + json.put("image_url_process", "image/resize,w_1920/format,jpeg"); + json.put("video_thumbnail_process", "video/snapshot,t_1000,f_jpg,ar_auto,w_300"); + json.put("order_by", "name"); + json.put("order_direction", "ASC"); + HashMap headers = new HashMap<>(); + headers.put("x-share-token", shareTk); + OKCallBack.OKCallBackString callback = new OKCallBack.OKCallBackString() { + @Override + public void onFailure(Call call, Exception e) { + + } + + @Override + public void onResponse(String response) { + + } + }; + OkHttpUtil.postJson(OkHttpUtil.defaultClient(), "https://api.aliyundrive.com/adrive/v3/file/list", json.toString(), headers, callback); + JSONArray rootList = new JSONObject(callback.getResult()).optJSONArray("items"); + if (rootList != null && rootList.length() > 0) { + for (int i = 0; i < rootList.length(); i++) { + JSONObject item = rootList.getJSONObject(i); + if (item.getString("type").equals("folder")) { + String dirId = item.getString("file_id"); + getFileList(shareTk, shareId, sharePwd, dirId, vodItems); + } else { + if (item.getString("type").equals("file") && !item.getString("file_extension").equals("txt")) { + String fileId = item.getString("file_id"); + String fileName = item.getString("name"); + vodItems.add(fileName + "$" + shareId + "+" + fileId); + } + } + } + } + // 取文件列表 + } catch (JSONException e) { + SpiderDebug.log(e); + } + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/Misc.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/Misc.java new file mode 100644 index 0000000..ac26ad1 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/Misc.java @@ -0,0 +1,122 @@ +package com.github.catvod.utils; + +import android.net.Uri; + +import com.github.catvod.crawler.SpiderDebug; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.regex.Pattern; + +public class Misc { + public static boolean isVip(String url) { + // 适配2.0.6的调用应用内解析列表的支持, 需要配合直连分析一起使用,参考cjt影视和极品直连 + try { + boolean isVip = false; + String host = Uri.parse(url).getHost(); + String[] vipWebsites = new String[]{"iqiyi.com", "v.qq.com", "youku.com", "le.com", "tudou.com", "mgtv.com", "sohu.com", "acfun.cn", "bilibili.com", "baofeng.com", "pptv.com"}; + for (int b = 0; b < vipWebsites.length; b++) { + if (host.contains(vipWebsites[b])) { + if ("iqiyi.com".equals(vipWebsites[b])) { + //爱奇艺需要特殊处理 + if (url.contains("iqiyi.com/a_") || url.contains("iqiyi.com/w_") || url.contains("iqiyi.com/v_")) { + isVip = true; + break; + } + } else { + isVip = true; + break; + } + } + } + return isVip; + } catch (Exception e) { + } + return false; + } + + private static final Pattern snifferMatch = Pattern.compile("http((?!http).){26,}?\\.(m3u8|mp4)\\?.*|http((?!http).){26,}\\.(m3u8|mp4)|http((?!http).){26,}?/m3u8\\?pt=m3u8.*|http((?!http).)*?default\\.ixigua\\.com/.*|http((?!http).)*?cdn-tos[^\\?]*|http((?!http).)*?/obj/tos[^\\?]*|http.*?/player/m3u8play\\.php\\?url=.*|http.*?/player/.*?[pP]lay\\.php\\?url=.*|http.*?/playlist/m3u8/\\?vid=.*|http.*?\\.php\\?type=m3u8&.*|http.*?/download.aspx\\?.*|http.*?/api/up_api.php\\?.*|https.*?\\.66yk\\.cn.*|http((?!http).)*?netease\\.com/file/.*"); + + public static boolean isVideoFormat(String url) { + if (snifferMatch.matcher(url).find()) { + if (url.contains("cdn-tos") && url.contains(".js")) { + return false; + } + return true; + } + return false; + } + + public static String fixUrl(String base, String src) { + try { + if (src.startsWith("//")) { + Uri parse = Uri.parse(base); + src = parse.getScheme() + ":" + src; + } else if (!src.contains("://")) { + Uri parse = Uri.parse(base); + src = parse.getScheme() + "://" + parse.getHost() + src; + } + } catch (Exception e) { + SpiderDebug.log(e); + } + return src; + } + + public static boolean isBlackVodUrl(String input, String url) { + if (url.contains("973973.xyz") || url.contains(".fit:")) + return true; + return false; + } + + public static final String UaWinChrome = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"; + + public static JSONObject fixJsonVodHeader(JSONObject headers, String input, String url) throws JSONException { + if (headers == null) + headers = new JSONObject(); + if (input.contains("www.mgtv.com")) { + headers.put("Referer", " "); + headers.put("User-Agent", " Mozilla/5.0"); + } else if (url.contains("titan.mgtv")) { + headers.put("Referer", " "); + headers.put("User-Agent", " Mozilla/5.0"); + } else if (input.contains("bilibili")) { + headers.put("Referer", " https://www.bilibili.com/"); + headers.put("User-Agent", " " + Misc.UaWinChrome); + } + return headers; + } + + public static JSONObject jsonParse(String input, String json) throws JSONException { + JSONObject jsonPlayData = new JSONObject(json); + String url = jsonPlayData.getString("url"); + if (url.startsWith("//")) { + url = "https:" + url; + } + if (!url.startsWith("http")) { + return null; + } + if (url.equals(input)) { + if (isVip(url) || !isVideoFormat(url)) { + return null; + } + } + if (Misc.isBlackVodUrl(input, url)) { + return null; + } + JSONObject headers = new JSONObject(); + String ua = jsonPlayData.optString("user-agent", ""); + if (ua.trim().length() > 0) { + headers.put("User-Agent", " " + ua); + } + String referer = jsonPlayData.optString("referer", ""); + if (referer.trim().length() > 0) { + headers.put("Referer", " " + referer); + } + headers = Misc.fixJsonVodHeader(headers, input, url); + JSONObject taskResult = new JSONObject(); + taskResult.put("header", headers); + taskResult.put("url", url); + return taskResult; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/SSLSocketFactoryCompat.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/SSLSocketFactoryCompat.java new file mode 100644 index 0000000..81104e4 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/SSLSocketFactoryCompat.java @@ -0,0 +1,167 @@ +package com.github.catvod.utils; + +import android.os.Build; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.GeneralSecurityException; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + +public class SSLSocketFactoryCompat extends SSLSocketFactory { + private SSLSocketFactory defaultFactory; + // Android 5.0+ (API level21) provides reasonable default settings + // but it still allows SSLv3 + // https://developer.android.com/about/versions/android-5.0-changes.html#ssl + static String protocols[] = null, cipherSuites[] = null; + + static { + try { + SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); + if (socket != null) { + /* set reasonable protocol versions */ + // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0) + // - remove all SSL versions (especially SSLv3) because they're insecure now + List protocols = new LinkedList<>(); + for (String protocol : socket.getSupportedProtocols()) + if (!protocol.toUpperCase().contains("SSL")) + protocols.add(protocol); + SSLSocketFactoryCompat.protocols = protocols.toArray(new String[protocols.size()]); + /* set up reasonable cipher suites */ + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // choose known secure cipher suites + List allowedCiphers = Arrays.asList( + // TLS 1.2 + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256", + // maximum interoperability + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + // additionally + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); + List availableCiphers = Arrays.asList(socket.getSupportedCipherSuites()); + // take all allowed ciphers that are available and put them into preferredCiphers + HashSet preferredCiphers = new HashSet<>(allowedCiphers); + preferredCiphers.retainAll(availableCiphers); + /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling + * ciphers which are enabled by default, but have become unsecure), but I guess for + * the security level of DAVdroid and maximum compatibility, disabling of insecure + * ciphers should be a server-side task */ + // add preferred ciphers to enabled ciphers + HashSet enabledCiphers = preferredCiphers; + enabledCiphers.addAll(new HashSet<>(Arrays.asList(socket.getEnabledCipherSuites()))); + SSLSocketFactoryCompat.cipherSuites = enabledCiphers.toArray(new String[enabledCiphers.size()]); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public SSLSocketFactoryCompat(X509TrustManager tm) { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, (tm != null) ? new X509TrustManager[]{tm} : null, null); + defaultFactory = sslContext.getSocketFactory(); + } catch (GeneralSecurityException e) { + throw new AssertionError(); // The system has no TLS. Just give up. + } + } + + private void upgradeTLS(SSLSocket ssl) { + // Android 5.0+ (API level21) provides reasonable default settings + // but it still allows SSLv3 + // https://developer.android.com/about/versions/android-5.0-changes.html#ssl + if (protocols != null) { + ssl.setEnabledProtocols(protocols); + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && cipherSuites != null) { + ssl.setEnabledCipherSuites(cipherSuites); + } + } + + @Override + public String[] getDefaultCipherSuites() { + return cipherSuites; + } + + @Override + public String[] getSupportedCipherSuites() { + return cipherSuites; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + Socket ssl = defaultFactory.createSocket(s, host, port, autoClose); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + Socket ssl = defaultFactory.createSocket(host, port); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + Socket ssl = defaultFactory.createSocket(host, port, localHost, localPort); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + Socket ssl = defaultFactory.createSocket(host, port); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + Socket ssl = defaultFactory.createSocket(address, port, localAddress, localPort); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + //定义一个信任所有证书的TrustManager + public static final X509TrustManager trustAllCert = new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + }; +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/SpiderOKClient.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/SpiderOKClient.java new file mode 100644 index 0000000..5fba1e9 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/SpiderOKClient.java @@ -0,0 +1,36 @@ +package com.github.catvod.utils; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; + +public class SpiderOKClient { + private static OkHttpClient noRedirectClient = null; + + public static OkHttpClient noRedirectClient() { + if (noRedirectClient == null) { + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + .connectTimeout(15, TimeUnit.SECONDS) + .followRedirects(false) + .followSslRedirects(false) + .retryOnConnectionFailure(true) + .sslSocketFactory(new SSLSocketFactoryCompat(SSLSocketFactoryCompat.trustAllCert), SSLSocketFactoryCompat.trustAllCert); + noRedirectClient = builder.build(); + } + return noRedirectClient; + } + + public static String getRedirectLocation(Map> headers) { + if (headers == null) + return null; + if (headers.containsKey("location")) + return headers.get("location").get(0); + if (headers.containsKey("Location")) + return headers.get("Location").get(0); + return null; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OKCallBack.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OKCallBack.java new file mode 100644 index 0000000..9bd5b7c --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OKCallBack.java @@ -0,0 +1,60 @@ +package com.github.catvod.utils.okhttp; + +import java.io.IOException; + +import okhttp3.Call; +import okhttp3.Response; + + +public abstract class OKCallBack { + + private T result = null; + + public T getResult() { + return result; + } + + protected void setResult(T val) { + result = val; + } + + protected void onError(final Call call, final Exception e) { + onFailure(call, e); + } + + protected void onSuccess(Call call, Response response) { + T obj = onParseResponse(call, response); + setResult(obj); + onResponse(obj); + } + + protected abstract T onParseResponse(Call call, Response response); + + protected abstract void onFailure(Call call, Exception e); + + protected abstract void onResponse(T response); + + public static abstract class OKCallBackDefault extends OKCallBack { + @Override + public Response onParseResponse(Call call, Response response) { + return response; + } + } + + public static abstract class OKCallBackString extends OKCallBack { + @Override + public void onError(Call call, Exception e) { + setResult(""); + super.onError(call, e); + } + + @Override + public String onParseResponse(Call call, Response response) { + try { + return response.body().string(); + } catch (IOException e) { + return ""; + } + } + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OKRequest.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OKRequest.java new file mode 100644 index 0000000..c24d5ba --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OKRequest.java @@ -0,0 +1,132 @@ +package com.github.catvod.utils.okhttp; + +import android.text.TextUtils; + +import java.io.IOException; +import java.util.Map; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.FormBody; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.Response; + +class OKRequest { + private final String mMethodType; + private String mUrl; + private Object mTag = null; + private final Map mParamsMap; + private final String mJsonStr; + private final Map mHeaderMap; + private final OKCallBack mCallBack; + private okhttp3.Request mOkHttpRequest; + private okhttp3.Request.Builder mRequestBuilder; + + + OKRequest(String methodType, String url, Map paramsMap, Map headerMap, OKCallBack callBack) { + this(methodType, url, null, paramsMap, headerMap, callBack); + } + + OKRequest(String methodType, String url, String jsonStr, Map headerMap, OKCallBack callBack) { + this(methodType, url, jsonStr, null, headerMap, callBack); + } + + private OKRequest(String methodType, String url, String jsonStr, Map paramsMap, Map headerMap, OKCallBack callBack) { + mMethodType = methodType; + mUrl = url; + mJsonStr = jsonStr; + mParamsMap = paramsMap; + mHeaderMap = headerMap; + mCallBack = callBack; + getInstance(); + } + + public void setTag(Object tag) { + mTag = tag; + } + + private void getInstance() { + mRequestBuilder = new okhttp3.Request.Builder(); + switch (mMethodType) { + case OkHttpUtil.METHOD_GET: + setGetParams(); + break; + case OkHttpUtil.METHOD_POST: + mRequestBuilder.post(getRequestBody()); + break; + } + mRequestBuilder.url(mUrl); + if (mTag != null) + mRequestBuilder.tag(mTag); + if (mHeaderMap != null) { + setHeader(); + } + mOkHttpRequest = mRequestBuilder.build(); + } + + private RequestBody getRequestBody() { + if (!TextUtils.isEmpty(mJsonStr)) { + MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + return RequestBody.create(JSON, mJsonStr); + } + FormBody.Builder formBody = new FormBody.Builder(); + if (mParamsMap != null) { + for (String key : mParamsMap.keySet()) { + formBody.add(key, mParamsMap.get(key)); + } + } + return formBody.build(); + } + + private void setGetParams() { + if (mParamsMap != null) { + mUrl = mUrl + "?"; + for (String key : mParamsMap.keySet()) { + mUrl = mUrl + key + "=" + mParamsMap.get(key) + "&"; + } + mUrl = mUrl.substring(0, mUrl.length() - 1); + } + } + + private void setHeader() { + if (mHeaderMap != null) { + for (String key : mHeaderMap.keySet()) { + mRequestBuilder.addHeader(key, mHeaderMap.get(key)); + } + } + } + + void execute(OkHttpClient client) { + Call call = client.newCall(mOkHttpRequest); + try { + Response response = call.execute(); + if (mCallBack != null) { + mCallBack.onSuccess(call, response); + } + } catch (IOException e) { + if (mCallBack != null) { + mCallBack.onError(call, e); + } + } + } + + void call(OkHttpClient client) { + client.newCall(mOkHttpRequest).enqueue(new Callback() { + @Override + public void onFailure(final Call call, final IOException e) { + if (mCallBack != null) { + mCallBack.onError(call, e); + } + } + + @Override + public void onResponse(final Call call, final Response response) throws IOException { + if (mCallBack != null) { + mCallBack.onSuccess(call, response); + } + } + }); + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OkHttpUtil.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OkHttpUtil.java new file mode 100644 index 0000000..e161c40 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/OkHttpUtil.java @@ -0,0 +1,200 @@ +package com.github.catvod.utils.okhttp; + +import com.github.catvod.crawler.SpiderDebug; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Response; + +public class OkHttpUtil { + + public static final String METHOD_GET = "GET"; + public static final String METHOD_POST = "POST"; + + private static final int DEFAULT_TIMEOUT = 15; + + private static final Object lockO = new Object(); + + private static OkHttpClient defaultClient = null; + + /** + * 不自动重定向 + */ + private static OkHttpClient noRedirectClient = null; + + public static OkHttpClient defaultClient() { + synchronized (lockO) { + if (defaultClient == null) { + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .retryOnConnectionFailure(true) + .sslSocketFactory(new SSLSocketFactoryCompat(SSLSocketFactoryCompat.trustAllCert), SSLSocketFactoryCompat.trustAllCert); + defaultClient = builder.build(); + } + return defaultClient; + } + } + + public static OkHttpClient noRedirectClient() { + synchronized (lockO) { + if (noRedirectClient == null) { + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .followRedirects(false) + .followSslRedirects(false) + .retryOnConnectionFailure(true) + .sslSocketFactory(new SSLSocketFactoryCompat(SSLSocketFactoryCompat.trustAllCert), SSLSocketFactoryCompat.trustAllCert); + noRedirectClient = builder.build(); + } + return noRedirectClient; + } + } + + public static String string(OkHttpClient client, String url, String tag, Map paramsMap, Map headerMap, Map> respHeaderMap) { + OKCallBack stringCallback = new OKCallBack() { + @Override + public String onParseResponse(Call call, Response response) { + try { + if (respHeaderMap != null) { + respHeaderMap.clear(); + respHeaderMap.putAll(response.headers().toMultimap()); + } + return response.body().string(); + } catch (IOException e) { + return ""; + } + } + + @Override + public void onFailure(Call call, Exception e) { + setResult(""); + SpiderDebug.log(e); + } + + @Override + public void onResponse(String response) { + } + }; + OKRequest req = new OKRequest(METHOD_GET, url, paramsMap, headerMap, stringCallback); + req.setTag(tag); + req.execute(client); + return stringCallback.getResult(); + } + + public static String stringNoRedirect(String url, Map headerMap, Map> respHeaderMap) { + return string(noRedirectClient(), url, null, null, headerMap, respHeaderMap); + } + + public static String string(String url, Map headerMap, Map> respHeaderMap) { + return string(defaultClient(), url, null, null, headerMap, respHeaderMap); + } + + public static String string(String url, Map headerMap) { + return string(defaultClient(), url, null, null, headerMap, null); + } + + public static String string(String url, String tag, Map headerMap) { + return string(defaultClient(), url, tag, null, headerMap, null); + } + + public static void get(OkHttpClient client, String url, OKCallBack callBack) { + get(client, url, null, null, callBack); + } + + public static void get(OkHttpClient client, String url, Map paramsMap, OKCallBack callBack) { + get(client, url, paramsMap, null, callBack); + } + + public static void get(OkHttpClient client, String url, Map paramsMap, Map headerMap, OKCallBack callBack) { + new OKRequest(METHOD_GET, url, paramsMap, headerMap, callBack).execute(client); + } + + public static void post(OkHttpClient client, String url, OKCallBack callBack) { + post(client, url, null, callBack); + } + + public static void post(OkHttpClient client, String url, Map paramsMap, OKCallBack callBack) { + post(client, url, paramsMap, null, callBack); + } + + public static void post(OkHttpClient client, String url, Map paramsMap, Map headerMap, OKCallBack callBack) { + new OKRequest(METHOD_POST, url, paramsMap, headerMap, callBack).execute(client); + } + + public static void post(OkHttpClient client, String url, String tag, Map paramsMap, Map headerMap, OKCallBack callBack) { + OKRequest req = new OKRequest(METHOD_POST, url, paramsMap, headerMap, callBack); + req.setTag(tag); + req.execute(client); + } + + public static void postJson(OkHttpClient client, String url, String jsonStr, OKCallBack callBack) { + postJson(client, url, jsonStr, null, callBack); + } + + public static void postJson(OkHttpClient client, String url, String jsonStr, Map headerMap, OKCallBack callBack) { + new OKRequest(METHOD_POST, url, jsonStr, headerMap, callBack).execute(client); + } + + /** + * 根据Tag取消请求 + */ + public static void cancel(OkHttpClient client, Object tag) { + if (client == null || tag == null) return; + for (Call call : client.dispatcher().queuedCalls()) { + if (tag.equals(call.request().tag())) { + call.cancel(); + } + } + for (Call call : client.dispatcher().runningCalls()) { + if (tag.equals(call.request().tag())) { + call.cancel(); + } + } + } + + public static void cancel(Object tag) { + cancel(defaultClient(), tag); + } + + public static void cancelAll() { + cancelAll(defaultClient()); + } + + /** + * 取消所有请求请求 + */ + public static void cancelAll(OkHttpClient client) { + if (client == null) return; + for (Call call : client.dispatcher().queuedCalls()) { + call.cancel(); + } + for (Call call : client.dispatcher().runningCalls()) { + call.cancel(); + } + } + + /** + * 获取重定向地址 + * + * @param headers + * @return + */ + public static String getRedirectLocation(Map> headers) { + if (headers == null) + return null; + if (headers.containsKey("location")) + return headers.get("location").get(0); + if (headers.containsKey("Location")) + return headers.get("Location").get(0); + return null; + } +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/SSLSocketFactoryCompat.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/SSLSocketFactoryCompat.java new file mode 100644 index 0000000..f0395e0 --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/utils/okhttp/SSLSocketFactoryCompat.java @@ -0,0 +1,167 @@ +package com.github.catvod.utils.okhttp; + +import android.os.Build; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.security.GeneralSecurityException; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + +public class SSLSocketFactoryCompat extends SSLSocketFactory { + private final SSLSocketFactory defaultFactory; + // Android 5.0+ (API level21) provides reasonable default settings + // but it still allows SSLv3 + // https://developer.android.com/about/versions/android-5.0-changes.html#ssl + static String[] protocols = null; + static String[] cipherSuites = null; + + static { + try { + SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); + if (socket != null) { + /* set reasonable protocol versions */ + // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0) + // - remove all SSL versions (especially SSLv3) because they're insecure now + List protocols = new LinkedList<>(); + for (String protocol : socket.getSupportedProtocols()) + if (!protocol.toUpperCase().contains("SSL")) + protocols.add(protocol); + SSLSocketFactoryCompat.protocols = protocols.toArray(new String[protocols.size()]); + /* set up reasonable cipher suites */ + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // choose known secure cipher suites + List allowedCiphers = Arrays.asList( + // TLS 1.2 + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256", + // maximum interoperability + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + // additionally + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); + List availableCiphers = Arrays.asList(socket.getSupportedCipherSuites()); + // take all allowed ciphers that are available and put them into preferredCiphers + HashSet preferredCiphers = new HashSet<>(allowedCiphers); + preferredCiphers.retainAll(availableCiphers); + /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling + * ciphers which are enabled by default, but have become unsecure), but I guess for + * the security level of DAVdroid and maximum compatibility, disabling of insecure + * ciphers should be a server-side task */ + // add preferred ciphers to enabled ciphers + HashSet enabledCiphers = preferredCiphers; + enabledCiphers.addAll(new HashSet<>(Arrays.asList(socket.getEnabledCipherSuites()))); + SSLSocketFactoryCompat.cipherSuites = enabledCiphers.toArray(new String[enabledCiphers.size()]); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public SSLSocketFactoryCompat(X509TrustManager tm) { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, (tm != null) ? new X509TrustManager[]{tm} : null, null); + defaultFactory = sslContext.getSocketFactory(); + } catch (GeneralSecurityException e) { + throw new AssertionError(); // The system has no TLS. Just give up. + } + } + + private void upgradeTLS(SSLSocket ssl) { + // Android 5.0+ (API level21) provides reasonable default settings + // but it still allows SSLv3 + // https://developer.android.com/about/versions/android-5.0-changes.html#ssl + if (protocols != null) { + ssl.setEnabledProtocols(protocols); + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && cipherSuites != null) { + ssl.setEnabledCipherSuites(cipherSuites); + } + } + + @Override + public String[] getDefaultCipherSuites() { + return cipherSuites; + } + + @Override + public String[] getSupportedCipherSuites() { + return cipherSuites; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + Socket ssl = defaultFactory.createSocket(s, host, port, autoClose); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + Socket ssl = defaultFactory.createSocket(host, port); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + Socket ssl = defaultFactory.createSocket(host, port, localHost, localPort); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + Socket ssl = defaultFactory.createSocket(host, port); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + Socket ssl = defaultFactory.createSocket(address, port, localAddress, localPort); + if (ssl instanceof SSLSocket) + upgradeTLS((SSLSocket) ssl); + return ssl; + } + + //定义一个信任所有证书的TrustManager + public static final X509TrustManager trustAllCert = new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + }; +} diff --git a/CatVodTVSpider/app/src/main/java/com/github/catvod/xpath/XPathRule.java b/CatVodTVSpider/app/src/main/java/com/github/catvod/xpath/XPathRule.java new file mode 100644 index 0000000..7238fdc --- /dev/null +++ b/CatVodTVSpider/app/src/main/java/com/github/catvod/xpath/XPathRule.java @@ -0,0 +1,687 @@ +package com.github.catvod.xpath; + +import com.github.catvod.crawler.SpiderDebug; + +import org.json.JSONObject; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class XPathRule { + /** + * user-agent + */ + private String ua; + /** + * 获取分类和首页推荐的Url + */ + private String homeUrl; + /** + * 分类节点 xpath + */ + private String cateNode; + /** + * 分类节点名 xpath + */ + private String cateName; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern cateNameR; + /** + * 分类节点id xpath + */ + private String cateId; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern cateIdR; + /** + * 手动指定 分类 如果有则不从homeUrl中获取分类 + */ + private LinkedHashMap cateManual = new LinkedHashMap<>(); + + /** + * 筛选 + */ + private JSONObject filter; + + /** + * 更新推荐视频节点 xpath + */ + private String homeVodNode; + /** + * 更新推荐视频名称 xpath + */ + private String homeVodName; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern homeVodNameR; + /** + * 更新推荐视频id xpath + */ + private String homeVodId; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern homeVodIdR; + /** + * 更新推荐视频图片 xpath + */ + private String homeVodImg; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern homeVodImgR; + /** + * 更新推荐视频简介 xpath + */ + private String homeVodMark; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern homeVodMarkR; + /** + * 分类页地址 + */ + private String cateUrl; + /** + * 分类叶视频节点 xpath + */ + private String cateVodNode; + /** + * 分类叶视频名称 xpath + */ + private String cateVodName; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern cateVodNameR; + /** + * 分类叶视频视频id xpath + */ + private String cateVodId; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern cateVodIdR; + /** + * 分类叶视频视频图片 xpath + */ + private String cateVodImg; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern cateVodImgR; + /** + * 分类叶视频视频简介 xpath + */ + private String cateVodMark; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern cateVodMarkR; + + /** + * 详情页面 + */ + private String dtUrl; + /** + * 详情节点 xpath + */ + private String dtNode; + /** + * 详情 视频名 xpath + */ + private String dtName; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtNameR; + /** + * 详情视频图片 xpath + */ + private String dtImg; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtImgR; + /** + * 详情视频分类 xpath + */ + private String dtCate; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtCateR; + /** + * 详情视频年份 xpath + */ + private String dtYear; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtYearR; + /** + * 详情视频地区 xpath + */ + private String dtArea; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtAreaR; + /** + * 详情视频简介 xpath + */ + private String dtMark; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtMarkR; + /** + * 详情演员 xpath + */ + private String dtActor; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtActorR; + /** + * 详情导演 xpath + */ + private String dtDirector; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtDirectorR; + /** + * 详情 说明 长 xpath + */ + private String dtDesc; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern dtDescR; + + /** + * 详情播放来源节点 + */ + private String dtFromNode; + /** + * 详情播放来源名称 xpath + */ + private String dtFromName; + /** + * 详情 + */ + private Pattern dtFromNameR; + /** + * 详情播放地址列表节点 xpath + */ + private String dtUrlNode; + /** + * 详情播放地址节点 xpath + */ + private String dtUrlSubNode; + /** + * 详情播放地址id xpath + */ + private String dtUrlId; + /** + * 详情 + */ + private Pattern dtUrlIdR; + /** + * 详情播放地址名称 xpath + */ + private String dtUrlName; + /** + * 详情 + */ + private Pattern dtUrlNameR; + /** + * 播放页面url + */ + private String playUrl; + /** + * 播放解析调用ua + */ + private String playUa; + + /** + * 搜索页地址 + */ + private String searchUrl; + + /** + * 搜索页视频节点 xpath + */ + private String scVodNode; + /** + * 搜索页视频名称 xpath + */ + private String scVodName; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern scVodNameR; + /** + * 搜索页视频id xpath + */ + private String scVodId; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern scVodIdR; + /** + * 搜索页视频图片 xpath + */ + private String scVodImg; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern scVodImgR; + /** + * 搜索页视频简介 xpath + */ + private String scVodMark; + /** + * 正则对取到的数据进行二次处理 + */ + private Pattern scVodMarkR; + + private static Pattern getPattern(JSONObject json, String key) { + String v = json.optString(key).trim(); + if (v.isEmpty()) + return null; + else { + try { + return Pattern.compile(v); + } catch (Exception e) { + SpiderDebug.log(e); + } + } + return null; + } + + private static String doReplaceRegex(Pattern pattern, String src) { + if (pattern == null) + return src; + try { + Matcher matcher = pattern.matcher(src); + if (matcher.find()) { + return matcher.group(1).trim(); + } + } catch (Exception e) { + SpiderDebug.log(e); + } + return src; + } + + public static XPathRule fromJson(String json) { + try { + JSONObject jsonObj = new JSONObject(json); + XPathRule rule = new XPathRule(); + rule.ua = jsonObj.optString("ua"); + rule.homeUrl = jsonObj.optString("homeUrl").trim(); + rule.cateNode = jsonObj.optString("cateNode").trim(); + rule.cateName = jsonObj.optString("cateName").trim(); + rule.cateNameR = getPattern(jsonObj, "cateNameR"); + rule.cateId = jsonObj.optString("cateId").trim(); + rule.cateIdR = getPattern(jsonObj, "cateIdR"); + JSONObject navs = jsonObj.optJSONObject("cateManual"); + if (navs != null) { + Iterator keys = navs.keys(); + while (keys.hasNext()) { + String name = keys.next(); + rule.cateManual.put(name.trim(), navs.getString(name).trim()); + } + } + rule.filter = jsonObj.optJSONObject("filter"); + rule.homeVodNode = jsonObj.optString("homeVodNode").trim(); + rule.homeVodName = jsonObj.optString("homeVodName").trim(); + rule.homeVodNameR = getPattern(jsonObj, "homeVodNameR"); + rule.homeVodId = jsonObj.optString("homeVodId").trim(); + rule.homeVodIdR = getPattern(jsonObj, "homeVodIdR"); + rule.homeVodImg = jsonObj.optString("homeVodImg").trim(); + rule.homeVodImgR = getPattern(jsonObj, "homeVodImgR"); + rule.homeVodMark = jsonObj.optString("homeVodMark").trim(); + rule.homeVodMarkR = getPattern(jsonObj, "homeVodMarkR"); + rule.cateUrl = jsonObj.optString("cateUrl").trim(); + rule.cateVodNode = jsonObj.optString("cateVodNode").trim(); + rule.cateVodName = jsonObj.optString("cateVodName").trim(); + rule.cateVodNameR = getPattern(jsonObj, "cateVodNameR"); + rule.cateVodId = jsonObj.optString("cateVodId").trim(); + rule.cateVodIdR = getPattern(jsonObj, "cateVodIdR"); + rule.cateVodImg = jsonObj.optString("cateVodImg").trim(); + rule.cateVodImgR = getPattern(jsonObj, "cateVodImgR"); + rule.cateVodMark = jsonObj.optString("cateVodMark").trim(); + rule.cateVodMarkR = getPattern(jsonObj, "cateVodMarkR"); + rule.dtUrl = jsonObj.optString("dtUrl"); + rule.dtNode = jsonObj.optString("dtNode"); + rule.dtName = jsonObj.optString("dtName"); + rule.dtNameR = getPattern(jsonObj, "dtNameR"); + rule.dtImg = jsonObj.optString("dtImg"); + rule.dtImgR = getPattern(jsonObj, "dtImgR"); + rule.dtCate = jsonObj.optString("dtCate"); + rule.dtCateR = getPattern(jsonObj, "dtCateR"); + rule.dtYear = jsonObj.optString("dtYear"); + rule.dtYearR = getPattern(jsonObj, "dtYearR"); + rule.dtArea = jsonObj.optString("dtArea"); + rule.dtAreaR = getPattern(jsonObj, "dtAreaR"); + rule.dtMark = jsonObj.optString("dtMark"); + rule.dtMarkR = getPattern(jsonObj, "dtMarkR"); + rule.dtActor = jsonObj.optString("dtActor"); + rule.dtActorR = getPattern(jsonObj, "dtActorR"); + rule.dtDirector = jsonObj.optString("dtDirector"); + rule.dtDirectorR = getPattern(jsonObj, "dtDirectorR"); + rule.dtDesc = jsonObj.optString("dtDesc"); + rule.dtDescR = getPattern(jsonObj, "dtDescR"); + rule.dtFromNode = jsonObj.optString("dtFromNode"); + rule.dtFromName = jsonObj.optString("dtFromName"); + rule.dtFromNameR = getPattern(jsonObj, "dtFromNameR"); + rule.dtUrlNode = jsonObj.optString("dtUrlNode"); + rule.dtUrlSubNode = jsonObj.optString("dtUrlSubNode"); + rule.dtUrlId = jsonObj.optString("dtUrlId"); + rule.dtUrlIdR = getPattern(jsonObj, "dtUrlIdR"); + rule.dtUrlName = jsonObj.optString("dtUrlName"); + rule.dtUrlNameR = getPattern(jsonObj, "dtUrlNameR"); + rule.playUrl = jsonObj.optString("playUrl"); + rule.playUa = jsonObj.optString("playUa"); + rule.searchUrl = jsonObj.optString("searchUrl"); + rule.scVodNode = jsonObj.optString("scVodNode").trim(); + rule.scVodName = jsonObj.optString("scVodName").trim(); + rule.scVodNameR = getPattern(jsonObj, "scVodNameR"); + rule.scVodId = jsonObj.optString("scVodId").trim(); + rule.scVodIdR = getPattern(jsonObj, "scVodIdR"); + rule.scVodImg = jsonObj.optString("scVodImg").trim(); + rule.scVodImgR = getPattern(jsonObj, "scVodImgR"); + rule.scVodMark = jsonObj.optString("scVodMark").trim(); + rule.scVodMarkR = getPattern(jsonObj, "scVodMarkR"); + return rule; + } catch (Exception e) { + SpiderDebug.log(e); + } + return null; + } + + public String getUa() { + return ua; + } + + public String getHomeUrl() { + return homeUrl; + } + + public String getCateNode() { + return cateNode; + } + + public String getCateName() { + return cateName; + } + + public String getCateNameR(String src) { + return doReplaceRegex(cateNameR, src); + } + + public String getCateId() { + return cateId; + } + + public String getCateIdR(String src) { + return doReplaceRegex(cateIdR, src); + } + + public LinkedHashMap getCateManual() { + return cateManual; + } + + public JSONObject getFilter() { + return filter; + } + + public String getHomeVodNode() { + return homeVodNode; + } + + public String getHomeVodName() { + return homeVodName; + } + + public String getHomeVodNameR(String src) { + return doReplaceRegex(homeVodNameR, src); + } + + public String getHomeVodId() { + return homeVodId; + } + + public String getHomeVodIdR(String src) { + return doReplaceRegex(homeVodIdR, src); + } + + public String getHomeVodImg() { + return homeVodImg; + } + + public String getHomeVodImgR(String src) { + return doReplaceRegex(homeVodImgR, src); + } + + public String getHomeVodMark() { + return homeVodMark; + } + + public String getHomeVodMarkR(String src) { + return doReplaceRegex(homeVodMarkR, src); + } + + public String getCateUrl() { + return cateUrl; + } + + public String getCateVodNode() { + return cateVodNode; + } + + public String getCateVodName() { + return cateVodName; + } + + public String getCateVodNameR(String src) { + return doReplaceRegex(cateVodNameR, src); + } + + public String getCateVodId() { + return cateVodId; + } + + public String getCateVodIdR(String src) { + return doReplaceRegex(cateVodIdR, src); + } + + public String getCateVodImg() { + return cateVodImg; + } + + public String getCateVodImgR(String src) { + return doReplaceRegex(cateVodImgR, src); + } + + public String getCateVodMark() { + return cateVodMark; + } + + public String getCateVodMarkR(String src) { + return doReplaceRegex(cateVodNameR, src); + } + + public String getDetailUrl() { + return dtUrl; + } + + public String getDetailNode() { + return dtNode; + } + + public String getDetailName() { + return dtName; + } + + public String getDetailNameR(String src) { + return doReplaceRegex(dtNameR, src); + } + + public String getDetailImg() { + return dtImg; + } + + public String getDetailImgR(String src) { + return doReplaceRegex(dtImgR, src); + } + + public String getDetailCate() { + return dtCate; + } + + public String getDetailCateR(String src) { + return doReplaceRegex(dtCateR, src); + } + + public String getDetailYear() { + return dtYear; + } + + public String getDetailYearR(String src) { + return doReplaceRegex(dtYearR, src); + } + + public String getDetailArea() { + return dtArea; + } + + public String getDetailAreaR(String src) { + return doReplaceRegex(dtAreaR, src); + } + + public String getDetailMark() { + return dtMark; + } + + public String getDetailMarkR(String src) { + return doReplaceRegex(dtMarkR, src); + } + + public String getDetailActor() { + return dtActor; + } + + public String getDetailActorR(String src) { + return doReplaceRegex(dtActorR, src); + } + + public String getDetailDirector() { + return dtDirector; + } + + public String getDetailDirectorR(String src) { + return doReplaceRegex(dtDirectorR, src); + } + + public String getDetailDesc() { + return dtDesc; + } + + public String getDetailDescR(String src) { + return doReplaceRegex(dtDescR, src); + } + + public String getDetailFromNode() { + return dtFromNode; + } + + public String getDetailFromName() { + return dtFromName; + } + + public String getDetailFromNameR(String src) { + return doReplaceRegex(dtFromNameR, src); + } + + public String getDetailUrlNode() { + return dtUrlNode; + } + + public String getDetailUrlSubNode() { + return dtUrlSubNode; + } + + public String getDetailUrlId() { + return dtUrlId; + } + + public String getDetailUrlIdR(String src) { + return doReplaceRegex(dtUrlIdR, src); + } + + public String getDetailUrlName() { + return dtUrlName; + } + + public String getDetailUrlNameR(String src) { + return doReplaceRegex(dtUrlNameR, src); + } + + public String getPlayUrl() { + return playUrl; + } + + public String getPlayUa() { + return playUa; + } + + public String getSearchUrl() { + return searchUrl; + } + + public String getSearchVodNode() { + return scVodNode; + } + + public String getSearchVodName() { + return scVodName; + } + + public String getSearchVodNameR(String src) { + return doReplaceRegex(scVodNameR, src); + } + + public String getSearchVodId() { + return scVodId; + } + + public String getSearchVodIdR(String src) { + return doReplaceRegex(scVodIdR, src); + } + + public String getSearchVodImg() { + return scVodImg; + } + + public String getSearchVodImgR(String src) { + return doReplaceRegex(scVodImgR, src); + } + + public String getSearchVodMark() { + return scVodMark; + } + + public String getSearchVodMarkR(String src) { + return doReplaceRegex(scVodMarkR, src); + } +} diff --git a/CatVodTVSpider/app/src/main/res/drawable-hdpi/app_icon.png b/CatVodTVSpider/app/src/main/res/drawable-hdpi/app_icon.png new file mode 100644 index 0000000..81491fc Binary files /dev/null and b/CatVodTVSpider/app/src/main/res/drawable-hdpi/app_icon.png differ diff --git a/CatVodTVSpider/app/src/main/res/drawable-xhdpi/app_icon.png b/CatVodTVSpider/app/src/main/res/drawable-xhdpi/app_icon.png new file mode 100644 index 0000000..edbfc76 Binary files /dev/null and b/CatVodTVSpider/app/src/main/res/drawable-xhdpi/app_icon.png differ diff --git a/CatVodTVSpider/app/src/main/res/drawable-xxhdpi/app_icon.png b/CatVodTVSpider/app/src/main/res/drawable-xxhdpi/app_icon.png new file mode 100644 index 0000000..0cec2e2 Binary files /dev/null and b/CatVodTVSpider/app/src/main/res/drawable-xxhdpi/app_icon.png differ diff --git a/CatVodTVSpider/app/src/main/res/drawable-xxxhdpi/app_icon.png b/CatVodTVSpider/app/src/main/res/drawable-xxxhdpi/app_icon.png new file mode 100644 index 0000000..00e83b9 Binary files /dev/null and b/CatVodTVSpider/app/src/main/res/drawable-xxxhdpi/app_icon.png differ diff --git a/CatVodTVSpider/app/src/main/res/values/strings.xml b/CatVodTVSpider/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..10cabcc --- /dev/null +++ b/CatVodTVSpider/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + CatVodSpider + \ No newline at end of file diff --git a/CatVodTVSpider/build.gradle b/CatVodTVSpider/build.gradle new file mode 100644 index 0000000..e6f435a --- /dev/null +++ b/CatVodTVSpider/build.gradle @@ -0,0 +1,48 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext { + appVersionCode = 1 + appVersionName = "1.0.0" + + miniSdkVersion = 16 + compileSdkVersion = 30 + targetSdkVersion = 26 + + jsoup_version = "1.14.1" + jsoupxpath_version = "2.5.0" + okhttp_version = "3.12.13" + rxhttp_version = "2.6.5" + } + + repositories { + mavenCentral() + google() + jcenter() + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } + maven { url 'https://jitpack.io' } + } + dependencies { + classpath "com.android.tools.build:gradle:4.2.1" + classpath "gradle.plugin.ru.cleverpumpkin.proguard-dictionaries-generator:plugin:1.0.8" + } +} + +allprojects { + repositories { + mavenCentral() + google() + jcenter() + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } + maven { url 'https://jitpack.io' } + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/CatVodTVSpider/buildAndGenJar.bat b/CatVodTVSpider/buildAndGenJar.bat new file mode 100644 index 0000000..72e0b5c --- /dev/null +++ b/CatVodTVSpider/buildAndGenJar.bat @@ -0,0 +1,7 @@ +@echo off + +call "%~dp0\gradlew" assembleRelease --no-daemon + +call "%~dp0\jar\genJar.bat" %1 + +pause \ No newline at end of file diff --git a/CatVodTVSpider/gradle.properties b/CatVodTVSpider/gradle.properties new file mode 100644 index 0000000..6826e61 --- /dev/null +++ b/CatVodTVSpider/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true \ No newline at end of file diff --git a/CatVodTVSpider/gradle/wrapper/gradle-wrapper.jar b/CatVodTVSpider/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/CatVodTVSpider/gradle/wrapper/gradle-wrapper.jar differ diff --git a/CatVodTVSpider/gradle/wrapper/gradle-wrapper.properties b/CatVodTVSpider/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..070478b --- /dev/null +++ b/CatVodTVSpider/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Sep 21 09:53:05 CST 2021 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/CatVodTVSpider/gradlew b/CatVodTVSpider/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/CatVodTVSpider/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/CatVodTVSpider/gradlew.bat b/CatVodTVSpider/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/CatVodTVSpider/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/CatVodTVSpider/jar/3rd/apktool_2.4.1.jar b/CatVodTVSpider/jar/3rd/apktool_2.4.1.jar new file mode 100644 index 0000000..28ce1e5 Binary files /dev/null and b/CatVodTVSpider/jar/3rd/apktool_2.4.1.jar differ diff --git a/CatVodTVSpider/jar/3rd/baksmali-2.5.2.jar b/CatVodTVSpider/jar/3rd/baksmali-2.5.2.jar new file mode 100644 index 0000000..b548bfc Binary files /dev/null and b/CatVodTVSpider/jar/3rd/baksmali-2.5.2.jar differ diff --git a/CatVodTVSpider/jar/3rd/dex.jar b/CatVodTVSpider/jar/3rd/dex.jar new file mode 100644 index 0000000..46c5f98 Binary files /dev/null and b/CatVodTVSpider/jar/3rd/dex.jar differ diff --git a/CatVodTVSpider/jar/3rd/dx-1.16.jar b/CatVodTVSpider/jar/3rd/dx-1.16.jar new file mode 100644 index 0000000..6e468ce Binary files /dev/null and b/CatVodTVSpider/jar/3rd/dx-1.16.jar differ diff --git a/CatVodTVSpider/jar/3rd/oss.jar b/CatVodTVSpider/jar/3rd/oss.jar new file mode 100644 index 0000000..2949336 Binary files /dev/null and b/CatVodTVSpider/jar/3rd/oss.jar differ diff --git a/CatVodTVSpider/jar/README.md b/CatVodTVSpider/jar/README.md new file mode 100644 index 0000000..712dd9b --- /dev/null +++ b/CatVodTVSpider/jar/README.md @@ -0,0 +1,143 @@ +## 共享包 + +### 这里的jar包由群友玩家共享的爬虫代码生成,不定期更新jar包和配置 + +### **注意:由于防火墙的问题,你可能无法直接使用这里的jar包,请自己想办法解决** + +```json + +"spider": "https://github.com/catvod/CatVodTVSpider/blob/master/jar/custom_spider.jar?raw=true", + +国内镜像 + +"spider": "https://litecucumber.coding.net/p/cat/d/config/git/raw/master/custom_spider.txt", + +...... +"sites": [ +...... + { + "key": "csp_Aidi", + "name": "爱迪", + "type": 3, + "api": "csp_Aidi", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_Enlienli", + "name": "嗯哩嗯哩", + "type": 3, + "api": "csp_Enlienli", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_Auete", + "name": "Auete", + "type": 3, + "api": "csp_Auete", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_Cokemv", + "name": "Cokemv", + "type": 3, + "api": "csp_Cokemv", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_Buka", + "name": "真不卡(测试)", + "type": 3, + "api": "csp_Buka", + "searchable": 0, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_Ysgc", + "name": "影视工厂", + "type": 3, + "api": "csp_Ysgc", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_N0ys", + "name": "90影视", + "type": 3, + "api": "csp_N0ys", + "searchable": 1, + "quickSearch": 1, + "filterable": 0 + }, + { + "key": "csp_Nekk", + "name": "9E看看", + "type": 3, + "api": "csp_Nekk", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_Imaple", + "name": "枫林网(爬虫)", + "type": 3, + "api": "csp_Imaple", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_Jumi", + "name": "剧迷(爬虫)", + "type": 3, + "api": "csp_Jumi", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_Nfx", + "name": "NFXHD(爬虫)", + "type": 3, + "api": "csp_Nfx", + "searchable": 1, + "quickSearch": 0, + "filterable": 1 + }, + { + "key": "csp_YydsAli1", + "name": "YYDS阿里(爬虫)", + "type": 3, + "api": "csp_YydsAli1", + "searchable": 1, + "quickSearch": 0, + "filterable": 0 + } +...... +], +...... +"parses": [ +...... + { + "name": "Json并发", + "type": 2, + "url": "Parallel" + }, + { + "name": "Json轮询", + "type": 2, + "url": "Sequence" + } +...... +] +``` diff --git a/CatVodTVSpider/jar/custom_spider.jar b/CatVodTVSpider/jar/custom_spider.jar new file mode 100644 index 0000000..152a94b Binary files /dev/null and b/CatVodTVSpider/jar/custom_spider.jar differ diff --git a/CatVodTVSpider/jar/custom_spider.jar.md5 b/CatVodTVSpider/jar/custom_spider.jar.md5 new file mode 100644 index 0000000..9735dd0 --- /dev/null +++ b/CatVodTVSpider/jar/custom_spider.jar.md5 @@ -0,0 +1 @@ +e43a84197bf0dcac50990fbc7f2ca470 diff --git a/CatVodTVSpider/jar/genJar.bat b/CatVodTVSpider/jar/genJar.bat new file mode 100644 index 0000000..cdb3600 --- /dev/null +++ b/CatVodTVSpider/jar/genJar.bat @@ -0,0 +1,32 @@ +@echo off + +del "%~dp0\custom_spider.jar" +rd /s/q "%~dp0\Smali_classes" + +java -jar "%~dp0\3rd\baksmali-2.5.2.jar" d "%~dp0\..\app\build\intermediates\dex\release\minifyReleaseWithR8\classes.dex" -o "%~dp0\Smali_classes" + +rd /s/q "%~dp0\spider.jar\smali\com\github\catvod\spider" +rd /s/q "%~dp0\spider.jar\smali\com\github\catvod\parser" + +if not exist "%~dp0\spider.jar\smali\com\github\catvod\" md "%~dp0\spider.jar\smali\com\github\catvod\" + +if "%1" == "ec" ( + java -Dfile.encoding=utf-8 -jar "%~dp0\3rd\oss.jar" "%~dp0\Smali_classes" +) + +move "%~dp0\Smali_classes\com\github\catvod\spider" "%~dp0\spider.jar\smali\com\github\catvod\" +move "%~dp0\Smali_classes\com\github\catvod\parser" "%~dp0\spider.jar\smali\com\github\catvod\" + +rd /s/q "%~dp0\Smali_classes" + +java -jar "%~dp0\3rd\apktool_2.4.1.jar" b "%~dp0\spider.jar" -c + +move "%~dp0\spider.jar\dist\dex.jar" "%~dp0\custom_spider.jar" + +certUtil -hashfile "%~dp0\custom_spider.jar" MD5 | find /i /v "md5" | find /i /v "certutil" > "%~dp0\custom_spider.jar.md5" + +rd /s/q "%~dp0\spider.jar\smali\com\github\catvod\spider" +rd /s/q "%~dp0\spider.jar\smali\com\github\catvod\parser" + +rd /s/q "%~dp0\spider.jar\build" +rd /s/q "%~dp0\spider.jar\dist" diff --git a/CatVodTVSpider/jar/spider.jar/apktool.yml b/CatVodTVSpider/jar/spider.jar/apktool.yml new file mode 100644 index 0000000..975be9f --- /dev/null +++ b/CatVodTVSpider/jar/spider.jar/apktool.yml @@ -0,0 +1,13 @@ +!!brut.androlib.meta.MetaInfo +apkFileName: dex.jar +compressionType: false +doNotCompress: null +isFrameworkApk: false +packageInfo: null +sdkInfo: null +sharedLibrary: false +sparseResources: false +unknownFiles: +usesFramework: null +version: 2.4.1_0303 +versionInfo: null diff --git a/CatVodTVSpider/jar/spider.jar/original/META-INF/MANIFEST.MF b/CatVodTVSpider/jar/spider.jar/original/META-INF/MANIFEST.MF new file mode 100644 index 0000000..2b41730 --- /dev/null +++ b/CatVodTVSpider/jar/spider.jar/original/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Dex-Location: classes.dex +Created-By: dx 1.11 + diff --git a/CatVodTVSpider/settings.gradle b/CatVodTVSpider/settings.gradle new file mode 100644 index 0000000..eea76c7 --- /dev/null +++ b/CatVodTVSpider/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = "CatVodSpider" +include ':app' diff --git a/CatVodTVSpider/xpath/4kyu.json b/CatVodTVSpider/xpath/4kyu.json new file mode 100644 index 0000000..3e15084 --- /dev/null +++ b/CatVodTVSpider/xpath/4kyu.json @@ -0,0 +1,66 @@ +{ + "author": "zhao xiaoyun", + "ua": "", + "homeUrl": "https://4kyu.cc", + "dcVipFlag": "true", + "pCfgJs": "https://4kyu.cc/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class,'myui-header__menu')]/li/a[contains(@href, 'vodtype') and not(contains(@href,'label'))]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/vodtype/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//li[contains(@class, 'col-lg-8')]/div/a[contains(@class,'myui-vodlist__thumb')]", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/voddetail/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "cateUrl": "https://4kyu.cc/vodtype/{cateId}-{catePg}.html", + "cateVodNode": "//li[contains(@class, 'col-lg-7')]/div/a[contains(@class,'myui-vodlist__thumb')]", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/voddetail/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "dtUrl": "https://4kyu.cc/voddetail/{vid}.html", + "dtNode": "//body", + "dtName": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/@title", + "dtNameR": "", + "dtImg": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/img/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '分类')]/following-sibling::*/text()", + "dtCateR": "", + "dtArea": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '地区')]/following-sibling::*/text()", + "dtAreaR": "", + "dtYear": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtMark": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '更新')]/following-sibling::*/text()", + "dtMarkR": "", + "dtActor": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '主演')]/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '导演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '简介')]/parent::text()", + "dtDescR": "", + "dtFromNode": "//a[@data-toggle='tab' and contains(@href, 'playlist')]", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//div[contains(@class,'tab-content')]/div[contains(@id, 'playlist')]", + "dtUrlSubNode": "//li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/vodplay/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://4kyu.cc/vodplay/{playUrl}.html", + "playUa": "", + "searchUrl": "https://4kyu.cc/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "" +} diff --git a/CatVodTVSpider/xpath/555.json b/CatVodTVSpider/xpath/555.json new file mode 100644 index 0000000..04d26d8 --- /dev/null +++ b/CatVodTVSpider/xpath/555.json @@ -0,0 +1,74 @@ +{ + "author": "zhao xiaoyun", + "ua": "", + "homeUrl": "https://www.o8tv.com", + "dcVipFlag": "true", + "pCfgJs": "https://www.o8tv.com/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class, 'myui-header__menu')]/li/a[contains(@href, 'vodtype')]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/vodtype(\\S+).html", + "cateManual": { + "电影": "1", + "连续剧": "2", + "综艺": "3", + "动漫": "4", + "纪录片": "91" + }, + "homeVodNode": "//ul[contains(@class, 'myui-vodlist')]/li/div[contains(@class, 'myui-vodlist__box')]", + "homeVodName": "/a/@title", + "homeVodId": "/a/@href", + "homeVodIdR": "/vodplay(\\S+).html", + "homeVodImg": "/a/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span[contains(@class,'pic-text')]/text()", + "cateUrl": "https://www.o8tv.com/vodshow/{cateId}--------{catePg}---2021.html", + "cateVodNode": "//ul[contains(@class, 'myui-vodlist')]/li/div[contains(@class, 'myui-vodlist__box')]", + "cateVodName": "/div/h4/a/@title", + "cateVodId": "/div/h4/a//@href", + "cateVodIdR": "/vodplay(\\S+).html", + "cateVodImg": "/a/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span[contains(@class,'pic-text')]/text()", + "dtUrl": "https://www.o8tv.com/voddetail/{vid}.html", + "dtNode": "//div[contains(@class,'col-lg-wide-75')]", + "dtName": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/@title", + "dtNameR": "", + "dtImg": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/img/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '分类')]/following-sibling::*/text()", + "dtCateR": "", + "dtYear": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '地区')]/following-sibling::*/text()", + "dtAreaR": "", + "dtMark": "", + "dtMarkR": "", + "dtActor": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '主演')]/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '导演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '简介')]/parent::text()", + "dtDescR": "", + "dtFromNode": "//a[@data-toggle='tab' and contains(@href, 'playlist')]", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//div[contains(@class,'tab-content')]/div[contains(@id, 'playlist')]", + "dtUrlSubNode": "//li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/vodplay/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://www.o8tv.com/vodplay/{playUrl}.html", + "playUa": "", + "searchUrl": "https://www.o8tv.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "" +} diff --git a/CatVodTVSpider/xpath/94sm.json b/CatVodTVSpider/xpath/94sm.json new file mode 100644 index 0000000..b387739 --- /dev/null +++ b/CatVodTVSpider/xpath/94sm.json @@ -0,0 +1,61 @@ +{ + "ua": "", + "homeUrl": "https://www.40mdd.com", + "cateNode": "//ul[contains(@class,'navbar-nav')]/li/a[contains(@href, '.html') and not(contains(@href, '6'))]", + "cateName": "/text()", + "cateNameR": "热门(.+)", + "cateId": "/@href", + "cateIdR": "/type/(\\d+).html", + "cateManual": {}, + "homeVodNode": "//div[@class='col-md-12 movie-item-out']//a[not(contains(@href, '6'))]/parent::*/parent::*/parent::*/div[contains(@class, 'movie-item-out') and position()<10]/div[@class='movie-item']/a", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/show/(\\w+).html", + "homeVodImg": "/img/@src", + "homeVodMark": "/button/text()", + "cateUrl": "https://www.40mdd.com/type/{cateId}/{catePg}.html", + "cateVodNode": "//div[@class='movie-item']/a", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/show/(\\w+).html", + "cateVodImg": "/img/@src", + "cateVodMark": "/button/text()", + "dtUrl": "https://www.40mdd.com/show/{vid}.html", + "dtNode": "//div[@class='container-fluid']", + "dtName": "//div[@class='col-md-9']//div[@class='col-md-4']//img/@alt", + "dtNameR": "", + "dtImg": "//div[@class='col-md-9']//div[@class='col-md-4']//img/@src", + "dtImgR": "", + "dtCate": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '类型')]/parent::*/following-sibling::*/text()", + "dtCateR": "", + "dtYear": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '日期')]/parent::*/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '国家')]/parent::*/following-sibling::*/text()", + "dtAreaR": "", + "dtMark": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '状态')]/parent::*/following-sibling::*/text()", + "dtMarkR": "", + "dtActor": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '主演')]/parent::*/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//div[@class='col-md-8']//span[@class='info-label' and contains(text(), '导演')]/parent::*/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "//p[@class='summary']/text()", + "dtDescR": "", + "dtFromNode": "//div[contains(@class,'resource-list')]/div[@class='panel-heading']/strong", + "dtFromName": "/text()", + "dtFromNameR": "\\S+\\.(\\S+) \\(", + "dtUrlNode": "//div[contains(@class,'resource-list')]/ul[@class='dslist-group']", + "dtUrlSubNode": "/li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/play/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://www.40mdd.com/play/{playUrl}.html", + "playUa": "", + "searchUrl": "https://www.40mdd.com/search?wd={wd}", + "scVodNode": "//div[@class='movie-item']/a", + "scVodName": "/@title", + "scVodId": "/@href", + "scVodIdR": "/show/(\\w+).html", + "scVodImg": "/img/@src", + "scVodMark": "/button/text()" +} diff --git a/CatVodTVSpider/xpath/Aixixi.json b/CatVodTVSpider/xpath/Aixixi.json new file mode 100644 index 0000000..56c2425 --- /dev/null +++ b/CatVodTVSpider/xpath/Aixixi.json @@ -0,0 +1,227 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://aixixi.vip/", + "cateNode": "//ul[contains(@class,'mo-navs-boxs')]/li/a[contains(@href, 'vod')]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/index.php/vod/show/id/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//ul[@class='mo-cols-rows']/li", + "homeVodName": "/a[2]/text()", + "homeVodId": "/a/@href", + "homeVodIdR": "/index.php/vod/detail/id/(\\w+).html", + "homeVodImg": "/a/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "//span[contains(@class,'mo-situ-rema')]/text()", + "cateUrl": "https://aixixi.vip/index.php/vod/show/area/{area}/by/{by}/year/{year}/id/{cateId}/page/{catePg}.html", + "cateVodNode": "//ul[@class='mo-cols-rows']/li", + "cateVodName": "/a[2]/text()", + "cateVodId": "/a/@href", + "cateVodIdR": "/index.php/vod/detail/id/(\\w+).html", + "cateVodImg": "/a/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "//span[contains(@class,'mo-situ-rema')]/text()", + "dtUrl": "https://aixixi.vip/index.php/vod/detail/id/{vid}.html", + "dtNode": "//dl[contains(@class,'mo-deta-info')]", + "dtName": "/dd/h1/a/text()", + "dtNameR": "", + "dtImg": "/dt/a/@data-original", + "dtImgR": "", + "dtCate": "//ul[contains(@class,'mo-cols-rows')]/li/span[contains(text(), '分类')]/following-sibling::a/text()", + "dtYear": "//ul[contains(@class,'mo-cols-rows')]/li/span[contains(text(), '年份')]/following-sibling::a/text()", + "dtArea": "//ul[contains(@class,'mo-cols-rows')]/li/span[contains(text(), '地区')]/following-sibling::a/text()", + "dtMark": "", + "dtDirector": "//ul[contains(@class,'mo-cols-rows')]/li/span[contains(text(), '导演')]/parent::*/a/text()", + "dtActor": "//ul[contains(@class,'mo-cols-rows')]/li/span[contains(text(), '主演')]/parent::*/a/text()", + "dtDesc": "//ul[contains(@class,'mo-cols-rows')]/li[contains(text(),'短评')]/text()", + "dtFromNode": "//div[contains(@class,'mo-sort-head')]/h2/a", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//ul[contains(@class,'mo-movs-item')]", + "dtUrlSubNode": "/li/a", + "dtUrlId": "/@href", + "dtUrlIdR": "/index.php/vod/play/id/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://aixixi.vip/index.php/vod/play/id/{playUrl}.html", + "playUa": "", + "searchUrl": "https://aixixi.vip/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "", + "filter": { + "1": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"}, + {"n": "德国","v": "德国"}, + {"n": "泰国","v": "泰国"}, + {"n": "印度","v": "印度"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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": "2008","v": "2008"}, + {"n": "2000","v": "2000"}, + {"n": "1997","v": "1997"}, + {"n": "1980","v": "1980"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "时间","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "2": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "内地","v": "内地"}, + {"n": "韩国","v": "韩国"}, + {"n": "香港","v": "香港"}, + {"n": "日本","v": "日本"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "泰国","v": "泰国"}, + {"n": "英国","v": "英国"}, + {"n": "新加坡","v": "新加坡"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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": "2008","v": "2008"}, + {"n": "2000","v": "2000"}, + {"n": "1997","v": "1997"}, + {"n": "1980","v": "1980"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "时间","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "4": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "国产","v": "国产"}, + {"n": "日本","v": "日本"}, + {"n": "欧美","v": "欧美"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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": "2008","v": "2008"}, + {"n": "2000","v": "2000"}, + {"n": "1997","v": "1997"}, + {"n": "1980","v": "1980"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "时间","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "3": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "内地","v": "内地"}, + {"n": "港台","v": "港台"}, + {"n": "日韩","v": "日韩"}, + {"n": "欧美","v": "欧美"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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": "2008","v": "2008"}, + {"n": "2000","v": "2000"}, + {"n": "1997","v": "1997"}, + {"n": "1980","v": "1980"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "时间","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ] + } +} diff --git a/CatVodTVSpider/xpath/Djx.json b/CatVodTVSpider/xpath/Djx.json new file mode 100644 index 0000000..522115e --- /dev/null +++ b/CatVodTVSpider/xpath/Djx.json @@ -0,0 +1,337 @@ +{ + "ua": "", + "homeUrl": "https://www.dijiaxia.com/", + "cateNode": "//ul[contains(@class,'nav-menu')]/li/a[contains(@href, 'vod')]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/index.php/vod/type/id/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//ul[contains(@class,'myui-vodlist')]/li/div/a", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/voddetail/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "//span[contains(@class,'pic-text')]/text()", + "cateUrl": "https://www.dijiaxia.com/index.php/vod/show/area/{area}/id/{cateId}/by/{by}/year/{year}/page/{catePg}.html", + "cateVodNode": "//ul[contains(@class,'myui-vodlist')]/li/div/a", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/index.php/vod/detail/id/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "//span[contains(@class,'pic-text')]/text()", + "dtUrl": "https://www.dijiaxia.com/index.php/vod/detail/id/{vid}.html", + "dtNode": "//body", + "dtName": "//div[contains(@class,'myui-content__thumb')]/a/@title", + "dtNameR": "", + "dtImg": "//div[contains(@class,'myui-content__thumb')]/a/img/@data-original", + "dtImgR": "", + "dtCate": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '分类')]/following-sibling::a/text()", + "dtYear": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '年份')]/following-sibling::a/text()", + "dtArea": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '地区')]/following-sibling::a/text()", + "dtMark": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '更新')]/following-sibling::a/text()", + "dtDirector": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '导演')]/following-sibling::a/text()", + "dtActor": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '主演')]/following-sibling::a/text()", + "dtDesc": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '简介')]/parent::*/text()[2]", + "dtFromNode": "//ul[contains(@class,'nav-tabs')]/li/a", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//ul[contains(@class,'myui-content__list')]", + "dtUrlSubNode": "/li/a", + "dtUrlId": "/@href", + "dtUrlIdR": "/index.php/vod/play/id/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://www.dijiaxia.com/index.php/vod/play/id/{playUrl}.html", + "playUa": "", + "searchUrl": "https://www.dijiaxia.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "", + "filter": { + "1": [ + { + "key": "cateId", + "name": "類型", + "value": [ + {"n": "全部","v": ""}, + {"n": "4K专区","v": "43"}, + {"n": "动作片","v": "6"}, + {"n": "喜剧片","v": "7"}, + {"n": "爱情片","v": "8"}, + {"n": "科幻片","v": "9"}, + {"n": "恐怖片","v": "10"}, + {"n": "剧情片","v": "11"}, + {"n": "战争片","v": "12"}, + {"n": "犯罪片","v": "24"}, + {"n": "战争片","v": "25"}, + {"n": "悬疑片","v": "26"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ], + "2": [ + { + "key": "cateId", + "name": "類型", + "value": [ + {"n": "全部","v": ""}, + {"n": "国产剧","v": "13"}, + {"n": "高评分独家资源","v": "42"}, + {"n": "港台剧","v": "14"}, + {"n": "日韩剧","v": "15"}, + {"n": "欧美剧","v": "16"}, + {"n": "泰国剧","v": "31"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "内地","v": "内地"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ], + "3": [ + { + "key": "cateId", + "name": "類型", + "value": [ + {"n": "全部","v": ""}, + {"n": "国内综艺","v": "33"}, + {"n": "国外综艺","v": "34"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "内地","v": "内地"}, + {"n": "港台","v": "港台"}, + {"n": "日韩","v": "日韩"}, + {"n": "欧美","v": "欧美"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ], + "4": [ + { + "key": "cateId", + "name": "類型", + "value": [ + {"n": "全部","v": ""}, + {"n": "国产动漫","v": "35"}, + {"n": "日韩动漫","v": "38"}, + {"n": "欧美动漫","v": "37"}, + {"n": "其他动漫","v": "39"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "国产","v": "国产"}, + {"n": "日本","v": "日本"}, + {"n": "欧美","v": "欧美"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ], + "20": [ + { + "key": "cateId", + "name": "類型", + "value": [ + {"n": "全部","v": ""}, + {"n": "国漫","v": "22"}, + {"n": "番剧","v": "23"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ], + "44": [ + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ] + } +} diff --git a/CatVodTVSpider/xpath/Duboku.json b/CatVodTVSpider/xpath/Duboku.json new file mode 100644 index 0000000..0f792ed --- /dev/null +++ b/CatVodTVSpider/xpath/Duboku.json @@ -0,0 +1,177 @@ +{ + "ua": "", + "homeUrl": "https://www.duboku.tv/", + "dcVipFlag": "true", + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class,'nav-menu')]/li/a[contains(@href, 'vodtype')and not(contains(@href,'20'))]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/vodtype/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//ul[contains(@class,'myui-vodlist')]/li/div/a", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/voddetail/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "//span[contains(@class,'pic-text')]/text()", + "cateUrl": "https://www.duboku.tv/vodshow/{cateId}-{area}-{by}------{catePg}---{year}.html", + "cateVodNode": "//ul[contains(@class,'myui-vodlist')]/li/div/a", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/voddetail/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "//span[contains(@class,'pic-text')]/text()", + "dtUrl": "https://my.duboku.vip/voddetail/{vid}.html", + "dtNode": "//body", + "dtName": "//div[contains(@class,'myui-content__thumb')]/a/@title", + "dtNameR": "", + "dtImg": "//div[contains(@class,'myui-content__thumb')]/a/img/@data-original", + "dtImgR": "", + "dtCate": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '分类')]/following-sibling::a/text()", + "dtYear": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '年份')]/following-sibling::a/text()", + "dtArea": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '地区')]/following-sibling::a/text()", + "dtMark": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '更新')]/following-sibling::a/text()", + "dtDirector": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '导演')]/following-sibling::a/text()", + "dtActor": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '主演')]/following-sibling::a/text()", + "dtDesc": "//div[contains(@class,'myui-content__detail')]/p/span[contains(text(), '简介')]/following-sibling::a/text()", + "dtFromNode": "//ul[contains(@class,'nav-tabs')]/li/a", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//ul[contains(@class,'myui-content__list')]", + "dtUrlSubNode": "/li/a", + "dtUrlId": "/@href", + "dtUrlIdR": "/vodplay/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://my.duboku.vip/vodplay/{playUrl}.html", + "playUa": "", + "searchUrl": "https://my.duboku.vip/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "", + "filter": { + "2": [ + { + "key": "cateId", + "name": "類型", + "value": [ + {"n": "全部","v": ""}, + {"n": "陸劇","v": "13"}, + {"n": "日韓劇","v": "15"}, + {"n": "英美剧","v": "16"}, + {"n": "台泰剧","v": "14"}, + {"n": "港剧","v": "20"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "美国","v": "美国"}, + {"n": "英国","v": "英国"}, + {"n": "内地","v": "内地"}, + {"n": "泰国","v": "泰国"}, + {"n": "韩国","v": "韩国"}, + {"n": "香港","v": "香港"}, + {"n": "日本","v": "日本"}, + {"n": "台湾","v": "台湾"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "2021","v": "2021"}, + {"n": "2020","v": "2020"}, + {"n": "2019","v": "2019"}, + {"n": "2018","v": "2018"}, + {"n": "2017","v": "2017"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "按更新","v": "time"}, + {"n": "周人气","v": "hits_week"}, + {"n": "月人气","v": "hits_month"} + ] + } + ], + "3": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "内地","v": "内地"}, + {"n": "港台","v": "港台"}, + {"n": "日韩","v": "日韩"}, + {"n": "欧美","v": "欧美"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "2021","v": "2021"}, + {"n": "2020","v": "2020"}, + {"n": "2019","v": "2019"}, + {"n": "2018","v": "2018"}, + {"n": "2017","v": "2017"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "按更新","v": "time"}, + {"n": "周人气","v": "hits_week"}, + {"n": "月人气","v": "hits_month"} + ] + } + ], + "4": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "国产","v": "国产"}, + {"n": "日本","v": "日本"}, + {"n": "欧美","v": "欧美"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "2021","v": "2021"}, + {"n": "2020","v": "2020"}, + {"n": "2019","v": "2019"}, + {"n": "2018","v": "2018"}, + {"n": "2017","v": "2017"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "按更新","v": "time"}, + {"n": "周人气","v": "hits_week"}, + {"n": "月人气","v": "hits_month"} + ] + } + ] + } + } diff --git a/CatVodTVSpider/xpath/Gimy.json b/CatVodTVSpider/xpath/Gimy.json new file mode 100644 index 0000000..d8ca706 --- /dev/null +++ b/CatVodTVSpider/xpath/Gimy.json @@ -0,0 +1,264 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://gimytv.com/", + "dcVipFlag": "true", + "pCfgJs": "https://gimytv.com/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[@class='myui-header__menu']/li/a[contains(@href, 'browse')]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/browse/(\\w+).html", + "cateManual": { + "电影": "movies", + "电视剧": "tvseries", + "综艺": "tv_show", + "动漫": "anime" + }, + "homeVodNode": "//ul[@class='myui-vodlist clearfix']/li/div/a", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodMark": "/span[contains(@class, 'pic-text')]/text()", + "cateUrl": "https://gimytv.com/genre/{cateId}-{area}-{year}-{catePg}/by/{by}.html", + "cateVodNode": "//ul[@class='myui-vodlist clearfix']/li/div/a", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodMark": "/span[contains(@class, 'pic-text')]/text()", + "dtUrl": "https://gimytv.com/{vid}.html", + "dtNode": "//div[@class='container']", + "dtName": "//div[@class='myui-content__thumb']/a/@title", + "dtNameR": "", + "dtImg": "//div[@class='myui-content__thumb']/a/img/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//p[@class='data']/span[contains(text(), '分類')]/following-sibling::*/text()", + "dtCateR": "", + "dtYear": "//p[@class='data']/span[contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "", + "dtAreaR": "", + "dtMark": "//p[@class='data']/span[contains(text(), '更新')]/following-sibling::*/text()", + "dtMarkR": "", + "dtActor": "//p[@class='data']/span[contains(text(), '主演')]/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//p[@class='data']/span[contains(text(), '導演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "//div[contains(@class,'col-pd')]/p/text()", + "dtDescR": "", + "dtFromNode": "//div[@class='myui-panel_hd']/div/h3[contains(text(),'線路')]", + "dtFromName": "/text()", + "dtUrlNode": "//ul[contains(@class,'myui-content__list')]", + "dtUrlSubNode": "/li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://gimytv.com/{playUrl}.html", + "playUa": "", + "searchUrl": "https://gimytv.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "", + "filter": { + "movies": [ + { + "key": "cateId", + "name": "分类", + "value": [ + {"n": "全部","v": ""}, + {"n": "劇情片","v": "drama"}, + {"n": "動作片","v": "action"}, + {"n": "科幻片","v": "scifi"}, + {"n": "喜劇片","v": "comedymovie"}, + {"n": "愛情片","v": "romance"}, + {"n": "戰爭片","v": "war"}, + {"n": "恐怖片","v": "horror"}, + {"n": "動畫電影","v": "animation"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "美國","v": "美國"}, + {"n": "歐美","v": "歐美"}, + {"n": "大陸","v": "大陸"}, + {"n": "中國大陸","v": "中國大陸"}, + {"n": "韓國","v": "韓國"}, + {"n": "香港","v": "香港"}, + {"n": "日本","v": "日本"}, + {"n": "英國","v": "英國"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "按更新","v": "time"}, + {"n": "周人气","v": "hits_week"}, + {"n": "月人气","v": "hits_month"} + ] + } + ], + "tvseries": [ + { + "key": "cateId", + "name": "分类", + "value": [ + {"n": "全部","v": ""}, + {"n": "陸劇","v": "cn"}, + {"n": "韓劇","v": "kr"}, + {"n": "美劇","v": "us"}, + {"n": "日劇","v": "jp"}, + {"n": "台劇","v": "tw"}, + {"n": "港劇","v": "hks"}, + {"n": "海外劇","v": "ot"}, + {"n": "紀錄片","v": "documentary"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "按更新","v": "time"}, + {"n": "周人气","v": "hits_week"}, + {"n": "月人气","v": "hits_month"} + ] + } + ], + "anime": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "日本","v": "日本"}, + {"n": "美國","v": "美國"}, + {"n": "歐美","v": "歐美"}, + {"n": "大陸","v": "大陸"}, + {"n": "臺灣","v": "臺灣"}, + {"n": "香港","v": "香港"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "按更新","v": "time"}, + {"n": "周人气","v": "hits_week"}, + {"n": "月人气","v": "hits_month"} + ] + } + ], + "tv_show": [ + { + "key": "cateId", + "name": "分类", + "value": [ + {"n": "全部","v": ""}, + {"n": "纪录片","v": "28"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陸","v": "大陸"}, + {"n": "中國大陸","v": "中國大陸"}, + {"n": "韓國","v": "韓國"}, + {"n": "臺灣","v": "臺灣"}, + {"n": "美國","v": "美國"}, + {"n": "歐美","v": "歐美"}, + {"n": "日本","v": "日本"}, + {"n": "香港","v": "香港"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "按更新","v": "time"}, + {"n": "周人气","v": "hits_week"}, + {"n": "月人气","v": "hits_month"} + ] + } + ] + } +} diff --git a/CatVodTVSpider/xpath/Jumi.json b/CatVodTVSpider/xpath/Jumi.json new file mode 100644 index 0000000..407cd4e --- /dev/null +++ b/CatVodTVSpider/xpath/Jumi.json @@ -0,0 +1,255 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://jumi.tv", + "dcVipFlag": "true", + "pCfgJs": "https://jumi.tv/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class,'myui-header__menu')]/li/a[contains(@href, 'type') and not(contains(@href, 'label'))]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/type/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//div[contains(@class, 'col-lg-wide-75')]//ul[contains(@class,'myui-vodlist')]/li//a[contains(@class,'myui-vodlist__thumb')]", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/vod/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span[contains(@class,'pic-text')]/text()", + "cateUrl": "https://jumi.tv/show/{cateId}/area/{area}/by/{by}/page/{catePg}/year/{year}.html", + "cateVodNode": "//ul[contains(@class,'myui-vodlist')]//li//a[contains(@class,'myui-vodlist__thumb')]", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/vod/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span[contains(@class,'pic-text')]/text()", + "dtUrl": "https://jumi.tv/vod/{vid}.html", + "dtNode": "//div[contains(@class,'col-lg-wide-75')]", + "dtName": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/@title", + "dtNameR": "", + "dtImg": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/img/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '分類')]/following-sibling::*/text()", + "dtCateR": "", + "dtYear": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '地區')]/following-sibling::*/text()", + "dtAreaR": "", + "dtMark": "", + "dtMarkR": "", + "dtActor": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '主演')]/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '導演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '簡介')]/parent::text()", + "dtDescR": "", + "dtFromNode": "//a[@data-toggle='tab' and contains(@href, 'playlist')]", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//div[contains(@class,'tab-content')]/div[contains(@id, 'playlist')]", + "dtUrlSubNode": "//li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/play/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://jumi.tv/play/{playUrl}.html", + "playUa": "", + "searchUrl": "https://jumi.tv/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "", + "filter": { + "1": [ + { + "key": "cateId", + "name": "類型", + "value": [ + {"n": "全部","v": ""}, + {"n": "動作片","v": "6"}, + {"n": "喜劇片","v": "7"}, + {"n": "愛情片","v": "8"}, + {"n": "科幻片","v": "9"}, + {"n": "恐怖片","v": "10"}, + {"n": "劇情片","v": "11"}, + {"n": "戰爭片","v": "12"}, + {"n": "紀錄片","v": "20"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陸","v": "大陸"}, + {"n": "香港","v": "香港"}, + {"n": "台灣","v": "台灣"}, + {"n": "美國","v": "美國"}, + {"n": "法國","v": "法國"}, + {"n": "英國","v": "英國"}, + {"n": "日本","v": "日本"}, + {"n": "韓國","v": "韓國"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ], + "2": [ + { + "key": "cateId", + "name": "分类", + "value": [ + {"n": "全部","v": ""}, + {"n": "陸劇","v": "13"}, + {"n": "韓劇","v": "23"}, + {"n": "美劇","v": "16"}, + {"n": "日劇","v": "15"}, + {"n": "台劇","v": "22"}, + {"n": "港劇","v": "14"}, + {"n": "海外劇","v": "24"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ], + "4": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陸","v": "大陸"}, + {"n": "香港","v": "香港"}, + {"n": "台灣","v": "台灣"}, + {"n": "美國","v": "美國"}, + {"n": "法國","v": "法國"}, + {"n": "英國","v": "英國"}, + {"n": "日本","v": "日本"}, + {"n": "韓國","v": "韓國"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ], + "3": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陸","v": "大陸"}, + {"n": "香港","v": "香港"}, + {"n": "台灣","v": "台灣"}, + {"n": "美國","v": "美國"}, + {"n": "法國","v": "法國"}, + {"n": "英國","v": "英國"}, + {"n": "日本","v": "日本"}, + {"n": "韓國","v": "韓國"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "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"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "時間","v": "time"}, + {"n": "人氣","v": "hits"}, + {"n": "評分","v": "score"} + ] + } + ] + } +} diff --git a/CatVodTVSpider/xpath/Nfuxs.json b/CatVodTVSpider/xpath/Nfuxs.json new file mode 100644 index 0000000..43a9aab --- /dev/null +++ b/CatVodTVSpider/xpath/Nfuxs.json @@ -0,0 +1,285 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://www.nfuxs.com/", + "cateNode": "//ul[contains(@class,'nav-menu')]/li/a[contains(@href, 'vodtype')]", + "cateName": "/span/text()", + "cateId": "/@href", + "cateIdR": "/vodtype/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//div[@class='module-item']", + "homeVodName": "//div[@class='module-item-pic']/a/@title", + "homeVodId": "//div[@class='module-item-pic']/a/@href", + "homeVodIdR": "/voddetail/(\\w+).html", + "homeVodImg": "//div[@class='module-item-pic']/img/@data-src", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "//div[@class='module-item-caption']/span[1]/text()", + "cateUrl": "https://www.nfuxs.com/vodshow/{cateId}-{area}-{by}------{catePg}---{year}.html", + "cateVodNode": "//div[@class='module-item']", + "cateVodName": "//div[@class='module-item-pic']/a/@title", + "cateVodId": "//div[@class='module-item-pic']/a/@href", + "cateVodIdR": "/voddetail/(\\w+).html", + "cateVodImg": "//div[@class='module-item-pic']/img/@data-src", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "//div[@class='module-item-caption']/span[1]/text()", + "dtUrl": "https://www.nfuxs.com/voddetail/{vid}.html", + "dtNode": "//body", + "dtName": "//div[@class='video-info-header']/h1/text()", + "dtNameR": "", + "dtImg": "//div[@class='video-cover']/div/div/img/@data-src", + "dtImgR": "", + "dtCate": "//div[@class='tag-link']/span/following-sibling::a/text()", + "dtYear": "//div[contains(@class,'video-info-aux')]/a[2]/text()", + "dtArea": "//div[contains(@class,'video-info-aux')]/a[3]/text()", + "dtMark": "", + "dtDirector": "//span[contains(text(), '导演')]/parent::*/div/span/following-sibling::*/text()", + "dtActor": "//span[contains(text(), '主演')]/parent::*/div/span/following-sibling::*/text()", + "dtDesc": "//span[contains(text(), '剧情')]/parent::*/div/span/text()", + "dtFromNode": "//div[@class='module-tab-content']/div/span", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//div[@class='scroll-content']", + "dtUrlSubNode": "/a", + "dtUrlId": "/@href", + "dtUrlIdR": "/vodplay/(\\S+).html", + "dtUrlName": "/span/text()", + "dtUrlNameR": "", + "playUrl": "https://www.nfuxs.com/vodplay/{playUrl}.html", + "playUa": "", + "searchUrl": "https://www.nfuxs.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "", + "filter": { + "1": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"}, + {"n": "泰国","v": "泰国"}, + {"n": "印度","v": "印度"}, + {"n": "意大利","v": "意大利"}, + {"n": "西班牙","v": "西班牙"}, + {"n": "加拿大","v": "加拿大"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "2021","v": "2021"}, + {"n": "2020","v": "2020"}, + {"n": "2019","v": "2019"}, + {"n": "2018","v": "2018"}, + {"n": "2017","v": "2017"}, + {"n": "2016","v": "2016"}, + {"n": "2015","v": "2015"}, + {"n": "2014","v": "2014"}, + {"n": "2013","v": "2013"}, + {"n": "2012","v": "2012"}, + {"n": "2011","v": "2011"}, + {"n": "2010","v": "2010"}, + {"n": "2009","v": "2009"}, + {"n": "2008","v": "2008"}, + {"n": "2007","v": "2007"}, + {"n": "2006","v": "2006"}, + {"n": "2005","v": "2005"}, + {"n": "2004","v": "2004"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "更新","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "2": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"}, + {"n": "泰国","v": "泰国"}, + {"n": "印度","v": "印度"}, + {"n": "意大利","v": "意大利"}, + {"n": "西班牙","v": "西班牙"}, + {"n": "加拿大","v": "加拿大"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "2021","v": "2021"}, + {"n": "2020","v": "2020"}, + {"n": "2019","v": "2019"}, + {"n": "2018","v": "2018"}, + {"n": "2017","v": "2017"}, + {"n": "2016","v": "2016"}, + {"n": "2015","v": "2015"}, + {"n": "2014","v": "2014"}, + {"n": "2013","v": "2013"}, + {"n": "2012","v": "2012"}, + {"n": "2011","v": "2011"}, + {"n": "2010","v": "2010"}, + {"n": "2009","v": "2009"}, + {"n": "2008","v": "2008"}, + {"n": "2007","v": "2007"}, + {"n": "2006","v": "2006"}, + {"n": "2005","v": "2005"}, + {"n": "2004","v": "2004"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "更新","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "3": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"}, + {"n": "泰国","v": "泰国"}, + {"n": "印度","v": "印度"}, + {"n": "意大利","v": "意大利"}, + {"n": "西班牙","v": "西班牙"}, + {"n": "加拿大","v": "加拿大"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "2021","v": "2021"}, + {"n": "2020","v": "2020"}, + {"n": "2019","v": "2019"}, + {"n": "2018","v": "2018"}, + {"n": "2017","v": "2017"}, + {"n": "2016","v": "2016"}, + {"n": "2015","v": "2015"}, + {"n": "2014","v": "2014"}, + {"n": "2013","v": "2013"}, + {"n": "2012","v": "2012"}, + {"n": "2011","v": "2011"}, + {"n": "2010","v": "2010"}, + {"n": "2009","v": "2009"}, + {"n": "2008","v": "2008"}, + {"n": "2007","v": "2007"}, + {"n": "2006","v": "2006"}, + {"n": "2005","v": "2005"}, + {"n": "2004","v": "2004"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "更新","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "1": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"}, + {"n": "泰国","v": "泰国"}, + {"n": "印度","v": "印度"}, + {"n": "意大利","v": "意大利"}, + {"n": "西班牙","v": "西班牙"}, + {"n": "加拿大","v": "加拿大"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"n": "2021","v": "2021"}, + {"n": "2020","v": "2020"}, + {"n": "2019","v": "2019"}, + {"n": "2018","v": "2018"}, + {"n": "2017","v": "2017"}, + {"n": "2016","v": "2016"}, + {"n": "2015","v": "2015"}, + {"n": "2014","v": "2014"}, + {"n": "2013","v": "2013"}, + {"n": "2012","v": "2012"}, + {"n": "2011","v": "2011"}, + {"n": "2010","v": "2010"}, + {"n": "2009","v": "2009"}, + {"n": "2008","v": "2008"}, + {"n": "2007","v": "2007"}, + {"n": "2006","v": "2006"}, + {"n": "2005","v": "2005"}, + {"n": "2004","v": "2004"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "更新","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ] + } + } diff --git a/CatVodTVSpider/xpath/Renjie.json b/CatVodTVSpider/xpath/Renjie.json new file mode 100644 index 0000000..d669153 --- /dev/null +++ b/CatVodTVSpider/xpath/Renjie.json @@ -0,0 +1,279 @@ +{ + "ua": "", + "homeUrl": "https://www.tangrenjie.tv", + "dcVipFlag": "true", + "pCfgJs": "https://www.tangrenjie.tv/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class, 'top_nav')]/li/a[contains(@href, 'type') and not(contains(@href,'5'))]", + "cateName": "/b/text()", + "cateId": "/@href", + "cateIdR": "/vod/type/id/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//div[@class='cbox1']/ul[contains(@class, 'vodlist')]/li/a", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/vod/detail/id/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span[contains(@class,'pic_text')]//b/text()", + "cateUrl": "https://www.tangrenjie.tv/vod/show/area/{area}/by/{by}/id/{cateId}/page/{catePg}/year/{year}.html", + "cateVodNode": "//ul[contains(@class, 'vodlist')]/li/a", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/vod/detail/id/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span[contains(@class,'pic_text')]//b/text()", + "dtUrl": "https://www.tangrenjie.tv/vod/detail/id/{vid}.html", + "dtNode": "//body", + "dtName": "//div[contains(@class,'content_thum')]/a/@title", + "dtNameR": "", + "dtImg": "//div[contains(@class,'content_thum')]/a/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//li[@class='data']/span[contains(text(), '类型')]/following-sibling::*/text()", + "dtCateR": "", + "dtYear": "//li[@class='data']/span[contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "//li[@class='data']/span[contains(text(), '地区')]/following-sibling::*/text()", + "dtAreaR": "", + "dtMark": "//li[@class='data']/span[contains(text(), '更新时间')]/following-sibling::*/text()", + "dtMarkR": "", + "dtActor": "//li[@class='data']/span[contains(text(), '主演')]/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//li[@class='data']/span[contains(text(), '导演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "//span[contains(text(), '简介')]/parent::*/text()[2]", + "dtDescR": "", + "dtFromNode": "//div[contains(@class,'play_source_tab')]/a/b", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//div[@class='playlist_full']", + "dtUrlSubNode": "/ul/li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/vod/play/id/(\\S+).html", + "dtUrlName": "/b/text()", + "dtUrlNameR": "", + "playUrl": "https://www.tangrenjie.tv/vod/play/id/{playUrl}.html", + "playUa": "", + "searchUrl": "https://www.tangrenjie.tv/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "", + "filter": { + "1": [ + { + "key": "cateId", + "name": "分类", + "value": [ + {"n": "全部","v": ""}, + {"n": "动作片","v": "6"}, + {"n": "喜剧片","v": "7"}, + {"n": "爱情片","v": "8"}, + {"n": "科幻片","v": "9"}, + {"n": "恐怖片","v": "10"}, + {"n": "剧情片","v": "11"}, + {"n": "战争片","v": "12"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"}, + {"n": "德国","v": "德国"}, + {"n": "泰国","v": "泰国"}, + {"n": "印度","v": "印度"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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": "2008","v": "2008"}, + {"n": "2000","v": "2000"}, + {"n": "1997","v": "1997"}, + {"n": "1980","v": "1980"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "时间","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "2": [ + { + "key": "cateId", + "name": "分类", + "value": [ + {"n": "全部","v": ""}, + {"n": "国产剧","v": "13"}, + {"n": "港台剧","v": "14"}, + {"n": "日韩剧","v": "15"}, + {"n": "欧美剧","v": "16"}, + {"n": "海外剧","v": "27"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "日本","v": "日本"}, + {"n": "欧美","v": "欧美"}, + {"n": "韩国","v": "韩国"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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": "2008","v": "2008"}, + {"n": "2000","v": "2000"}, + {"n": "1997","v": "1997"}, + {"n": "1980","v": "1980"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "时间","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "4": [ + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "日本","v": "日本"}, + {"n": "欧美","v": "欧美"}, + {"n": "韩国","v": "韩国"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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": "2008","v": "2008"}, + {"n": "2000","v": "2000"}, + {"n": "1997","v": "1997"}, + {"n": "1980","v": "1980"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "时间","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ], + "3": [ + { + "key": "cateId", + "name": "分类", + "value": [ + {"n": "全部","v": ""}, + {"n": "纪录片","v": "28"} + ] + }, + { + "key": "area", + "name": "地区", + "value": [ + {"n": "全部","v": ""}, + {"n": "大陆","v": "大陆"}, + {"n": "香港","v": "香港"}, + {"n": "台湾","v": "台湾"}, + {"n": "美国","v": "美国"}, + {"n": "法国","v": "法国"}, + {"n": "英国","v": "英国"}, + {"n": "日本","v": "日本"}, + {"n": "韩国","v": "韩国"}, + {"n": "德国","v": "德国"}, + {"n": "泰国","v": "泰国"}, + {"n": "印度","v": "印度"}, + {"n": "其他","v": "其他"} + ] + }, + { + "key": "year", + "name": "年份", + "value": [ + {"n": "全部","v": ""}, + {"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": "2008","v": "2008"}, + {"n": "2000","v": "2000"}, + {"n": "1997","v": "1997"}, + {"n": "1980","v": "1980"} + ] + }, + { + "key": "by", + "name": "排序", + "value": [ + {"n": "时间","v": "time"}, + {"n": "人气","v": "hits"}, + {"n": "评分","v": "score"} + ] + } + ] + } +} diff --git a/CatVodTVSpider/xpath/Sky4k.json b/CatVodTVSpider/xpath/Sky4k.json new file mode 100644 index 0000000..c6c0030 --- /dev/null +++ b/CatVodTVSpider/xpath/Sky4k.json @@ -0,0 +1,254 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://movie.sky4k.ga", + "dcVipFlag": "true", + "pCfgJs": "https://movie.sky4k.ga/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[@class='vi-nav swiper-wrapper clearfix']/li/a[contains(@href, '.html') and not(contains(@href, 'topic'))]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/vodshow/(\\d+)-----------.html", + "cateManual": {}, + "homeVodNode": "//ul[@class='dx-list dx-left']/li[@class='dx-vod']", + "homeVodName": "/a/@title", + "homeVodId": "/a/@href", + "homeVodIdR": "/voddetail/(\\w+).html", + "homeVodImg": "/a/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span[contains(@class,'vod_remarks')]/text()", + "cateUrl": "https://movie.sky4k.ga/vodshow/{cateId}-{area}-{by}------{catePg}---{year}.html", + "cateVodNode": "//li[@class='dx-vod']", + "cateVodName": "/a/@title", + "cateVodId": "/a/@href", + "cateVodIdR": "/voddetail/(\\w+).html", + "cateVodImg": "/a/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span[contains(@class,'vod_remarks')]/text()", + "dtUrl": "https://movie.sky4k.ga/voddetail/{vid}.html", + "dtNode": "//div[@class='container detail']", + "dtName": "/div[@class='detail-objs']/div[@class='obj-info']/h1/text()", + "dtNameR": "", + "dtImg": "/div[@class='detail-objs']/div[@class='obj-pic']/a[@class='picHover']/img/@src", + "dtImgR": "", + "dtCate": "//div[@class='tag-link']/a/text()", + "dtCateR": "", + "dtYear": "//a[@class='tag-link'][2]/text()", + "dtYearR": "", + "dtArea": "//a[@class='tag-link'][3]/text()", + "dtAreaR": "", + "dtMark": "//label[contains(text(),'Date')]/following-sibling::*/text()", + "dtMarkR": "", + "dtActor": "//label[contains(text(),'Actor')]/following-sibling::*/a/text()", + "dtActorR": "", + "dtDirector": "//label[contains(text(),'Director')]/following-sibling::*/a/text()", + "dtDirectorR": "", + "dtDesc": "//div[@class='vod_content']/text()[1]", + "dtDescR": "", + "dtFromNode": "//div[@class='playlist']/div/div[@class='play-title']/div[@class='player_info']/h2", + "dtFromName": "/text()", + "dtFromNameR": "\\S+\\.(\\S+) \\(", + "dtUrlNode": "//div[contains(@class,'play_li fn-clear')]", + "dtUrlSubNode": "/a", + "dtUrlId": "@href", + "dtUrlIdR": "/vodplay/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://movie.sky4k.ga/vodplay/{playUrl}.html", + "playUa": "", + "searchUrl": "https://movie.sky4k.ga/vodsearch/-------------.html?wd={wd}", + "scVodNode": "//li[@class='dx-vod']", + "scVodName": "/a/@title", + "scVodId": "/a/@href", + "scVodIdR": "/voddetail/(\\w+).html", + "scVodImg": "/a/@data-original", + "scVodImgR": "\\S+(http\\S+)", + "scVodMark": "/span[contains(@class,'vod_remarks')]/text()", + "filter": { + "1": [ + { + "key": "cateId", + "name": "Sort", + "value": [ + {"n": "All","v": ""}, + {"n": "BiliBili","v": "21"}, + {"n": "Action","v": "6"}, + {"n": "Comendy","v": "7"}, + {"n": "Romance","v": "8"}, + {"n": "Sci-Fi","v": "9"}, + {"n": "Horror","v": "10"}, + {"n": "Drama","v": "11"}, + {"n": "War","v": "12"} + ] + }, + { + "key": "area", + "name": "Country", + "value": [ + {"n": "All","v": ""}, + {"n": "CN","v": "大陆"}, + {"n": "HK","v": "香港"}, + {"n": "TW","v": "台湾"}, + {"n": "US","v": "美国"}, + {"n": "FR","v": "法国"}, + {"n": "EN","v": "英国"}, + {"n": "JP","v": "日本"}, + {"n": "KR","v": "韩国"} + ] + }, + { + "key": "year", + "name": "Year", + "value": [ + {"n": "All","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "By", + "value": [ + {"n": "Time","v": "time"}, + {"n": "Hits","v": "hits"}, + {"n": "Score","v": "score"} + ] + } + ], + "2": [ + { + "key": "cateId", + "name": "Sort", + "value": [ + {"n": "All","v": ""}, + {"n": "Bilibili","v": "22"}, + {"n": "CN","v": "13"}, + {"n": "HK/TW","v": "14"}, + {"n": "KR/JP","v": "15"}, + {"n": "US/UK","v": "16"} + ] + }, + { + "key": "year", + "name": "Year", + "value": [ + {"n": "All","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "By", + "value": [ + {"n": "Time","v": "time"}, + {"n": "Hits","v": "hits"}, + {"n": "Score","v": "score"} + ] + } + ], + "3": [ + { + "key": "area", + "name": "Country", + "value": [ + {"n": "All","v": ""}, + {"n": "CN","v": "内地"}, + {"n": "HK/TW","v": "港台"}, + {"n": "JP/KR","v": "日韩"}, + {"n": "US/UK","v": "欧美"} + ] + }, + { + "key": "year", + "name": "Year", + "value": [ + {"n": "All","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "By", + "value": [ + {"n": "Time","v": "time"}, + {"n": "Hits","v": "hits"}, + {"n": "Score","v": "score"} + ] + } + ], + "4": [ + { + "key": "cateId", + "name": "Sort", + "value": [ + {"n": "All","v": ""}, + {"n": "Bilibili","v": "23"} + ] + }, + { + "key": "area", + "name": "Country", + "value": [ + {"n": "All","v": ""}, + {"n": "CN","v": "国产"}, + {"n": "JP","v": "日本"}, + {"n": "US","v": "欧美"}, + {"n": "Other","v": "其他"} + ] + }, + { + "key": "year", + "name": "Year", + "value": [ + {"n": "All","v": ""}, + {"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"} + ] + }, + { + "key": "by", + "name": "By", + "value": [ + {"n": "Time","v": "time"}, + {"n": "Hits","v": "hits"}, + {"n": "Score","v": "score"} + ] + } + ] + } +} diff --git a/CatVodTVSpider/xpath/agefans.json b/CatVodTVSpider/xpath/agefans.json new file mode 100644 index 0000000..0e9b003 --- /dev/null +++ b/CatVodTVSpider/xpath/agefans.json @@ -0,0 +1,49 @@ +{ + "ua": "", + "homeUrl": "https://www.agefans.cc/update", + "cateManual": { + "全部": "", + "中国": "-中国-all-all", + "日本": "-日本-all-all", + "欧美": "-欧美-all-all" + }, + "homeVodNode": "//div[@class='blockcontent']//ul[@class='ul_li_a6']/li", + "homeVodName": "/h4[@class='anime_icon2_name']/a/text()", + "homeVodId": "/h4[@class='anime_icon2_name']/a/@href", + "homeVodIdR": "/detail/(\\w+)", + "homeVodImg": "//img/@src", + "homeVodMark": "//img/@title", + "cateUrl": "https://www.agefans.cc/catalog/all-all-all-all-all-time-{catePg}{cateId}", + "cateVodNode": "//div[@class='blockcontent1']/div/a", + "cateVodName": "/img/@alt", + "cateVodId": "/@href", + "cateVodIdR": "/detail/(\\w+)", + "cateVodImg": "/img/@src", + "cateVodMark": "/span[@class='newname']/text()", + "dtUrl": "https://www.agefans.cc/detail/{vid}", + "dtNode": "//div[@id='container']", + "dtName": "//div[@class='div_left']/div[@class='baseblock'][1]/div[@class='blockcontent']/img/@alt", + "dtImg": "//div[@class='div_left']/div[@class='baseblock'][1]/div[@class='blockcontent']/img/@src", + "dtCate": "//div[@class='div_left']/div[@class='baseblock'][2]//span[@class='detail_imform_tag' and contains(text(), '剧情类型')]/following-sibling::*/text()", + "dtYear": "//div[@class='div_left']/div[@class='baseblock'][2]//span[@class='detail_imform_tag' and contains(text(), '首播时间')]/following-sibling::*/text()", + "dtArea": "//div[@class='div_left']/div[@class='baseblock'][2]//span[@class='detail_imform_tag' and contains(text(), '地区')]/following-sibling::*/text()", + "dtMark": "", + "dtDirector": "//div[@class='div_left']/div[@class='baseblock'][2]//span[@class='detail_imform_tag' and contains(text(), '制作公司')]/following-sibling::*/text()", + "dtDesc": "//div[@class='div_right']/div[@class='baseblock'][2]//p/text()", + "dtFromNode": "//div[@id='playlist-div']/ul[@class='menu0']/li", + "dtFromName": "/text()", + "dtUrlNode": "//div[@id='playlist-div']/div[@class='main0']/div", + "dtUrlSubNode": "//li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/play/(\\S+)", + "dtUrlName": "/text()", + "playUrl": "https://www.agefans.cc/play/{playUrl}", + "playUa": "", + "searchUrl": "https://www.agefans.cc/search?query={wd}&page=1", + "scVodNode": "//div[@class='blockcontent1']/div/a", + "scVodName": "/img/@alt", + "scVodId": "/@href", + "scVodIdR": "/detail/(\\w+)", + "scVodImg": "/img/@src", + "scVodMark": "/span[@class='newname']/text()" +} \ No newline at end of file diff --git a/CatVodTVSpider/xpath/bde4.json b/CatVodTVSpider/xpath/bde4.json new file mode 100644 index 0000000..d10ccc3 --- /dev/null +++ b/CatVodTVSpider/xpath/bde4.json @@ -0,0 +1,45 @@ +{ + "ua": "", + "homeUrl": "https://www.mp4er.cc", + "decodeTk": "填上猫大解密接口的口令,注意保密", + "cateManual": { + "全部": "*", + "电影": "0", + "电视剧": "1" + }, + "filter": {"*":[{"key":"s","name":"类型","value":[{"n":"全部","v":"all"},{"n":"动作","v":"dongzuo"},{"n":"爱情","v":"aiqing"},{"n":"喜剧","v":"xiju"},{"n":"科幻","v":"kehuan"},{"n":"恐怖","v":"kongbu"},{"n":"战争","v":"zhanzheng"},{"n":"武侠","v":"wuxia"},{"n":"魔幻","v":"mohuan"},{"n":"剧情","v":"juqing"},{"n":"动画","v":"donghua"},{"n":"惊悚","v":"jingsong"},{"n":"3D","v":"3D"},{"n":"灾难","v":"zainan"},{"n":"悬疑","v":"xuanyi"},{"n":"警匪","v":"jingfei"},{"n":"文艺","v":"wenyi"},{"n":"青春","v":"qingchun"},{"n":"冒险","v":"maoxian"},{"n":"犯罪","v":"fanzui"},{"n":"纪录","v":"jilu"},{"n":"古装","v":"guzhuang"},{"n":"奇幻","v":"qihuan"},{"n":"国语","v":"guoyu"},{"n":"综艺","v":"zongyi"},{"n":"历史","v":"lishi"},{"n":"运动","v":"yundong"},{"n":"原创压制","v":"yuanchuang"},{"n":"美剧","v":"meiju"},{"n":"韩剧","v":"hanju"},{"n":"国产电视剧","v":"guoju"},{"n":"日剧","v":"riju"},{"n":"英剧","v":"yingju"},{"n":"德剧","v":"deju"},{"n":"俄剧","v":"eju"},{"n":"巴剧","v":"baju"},{"n":"加剧","v":"jiaju"},{"n":"西剧","v":"anish"},{"n":"意大利剧","v":"yidaliju"},{"n":"泰剧","v":"taiju"},{"n":"港台剧","v":"gangtaiju"},{"n":"法剧","v":"faju"},{"n":"澳剧","v":"aoju"}]},{"key":"area","name":"地区","value":[{"n":"不限","v":""},{"n":"中国大陆","v":"中国大陆"},{"n":"中国香港","v":"中国香港"},{"n":"中国台湾","v":"中国台湾"},{"n":"美国","v":"美国"},{"n":"英国","v":"英国"},{"n":"日本","v":"日本"},{"n":"韩国","v":"韩国"},{"n":"法国","v":"法国"},{"n":"印度","v":"印度"},{"n":"德国","v":"德国"},{"n":"西班牙","v":"西班牙"},{"n":"意大利","v":"意大利"},{"n":"澳大利亚","v":"澳大利亚"},{"n":"比利时","v":"比利时"},{"n":"瑞典","v":"瑞典"},{"n":"荷兰","v":"荷兰"},{"n":"丹麦","v":"丹麦"},{"n":"加拿大","v":"加拿大"},{"n":"俄罗斯","v":"俄罗斯"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"order","name":"排序","value":[{"n":"更新时间","v":"0"},{"n":"豆瓣评分","v":"1"}]}],"0":[{"key":"s","name":"类型","value":[{"n":"全部","v":"all"},{"n":"动作","v":"dongzuo"},{"n":"爱情","v":"aiqing"},{"n":"喜剧","v":"xiju"},{"n":"科幻","v":"kehuan"},{"n":"恐怖","v":"kongbu"},{"n":"战争","v":"zhanzheng"},{"n":"武侠","v":"wuxia"},{"n":"魔幻","v":"mohuan"},{"n":"剧情","v":"juqing"},{"n":"动画","v":"donghua"},{"n":"惊悚","v":"jingsong"},{"n":"3D","v":"3D"},{"n":"灾难","v":"zainan"},{"n":"悬疑","v":"xuanyi"},{"n":"警匪","v":"jingfei"},{"n":"文艺","v":"wenyi"},{"n":"青春","v":"qingchun"},{"n":"冒险","v":"maoxian"},{"n":"犯罪","v":"fanzui"},{"n":"纪录","v":"jilu"},{"n":"古装","v":"guzhuang"},{"n":"奇幻","v":"qihuan"},{"n":"国语","v":"guoyu"},{"n":"综艺","v":"zongyi"},{"n":"历史","v":"lishi"},{"n":"运动","v":"yundong"},{"n":"原创压制","v":"yuanchuang"},{"n":"美剧","v":"meiju"},{"n":"韩剧","v":"hanju"},{"n":"国产电视剧","v":"guoju"},{"n":"日剧","v":"riju"},{"n":"英剧","v":"yingju"},{"n":"德剧","v":"deju"},{"n":"俄剧","v":"eju"},{"n":"巴剧","v":"baju"},{"n":"加剧","v":"jiaju"},{"n":"西剧","v":"anish"},{"n":"意大利剧","v":"yidaliju"},{"n":"泰剧","v":"taiju"},{"n":"港台剧","v":"gangtaiju"},{"n":"法剧","v":"faju"},{"n":"澳剧","v":"aoju"}]},{"key":"area","name":"地区","value":[{"n":"不限","v":""},{"n":"中国大陆","v":"中国大陆"},{"n":"中国香港","v":"中国香港"},{"n":"中国台湾","v":"中国台湾"},{"n":"美国","v":"美国"},{"n":"英国","v":"英国"},{"n":"日本","v":"日本"},{"n":"韩国","v":"韩国"},{"n":"法国","v":"法国"},{"n":"印度","v":"印度"},{"n":"德国","v":"德国"},{"n":"西班牙","v":"西班牙"},{"n":"意大利","v":"意大利"},{"n":"澳大利亚","v":"澳大利亚"},{"n":"比利时","v":"比利时"},{"n":"瑞典","v":"瑞典"},{"n":"荷兰","v":"荷兰"},{"n":"丹麦","v":"丹麦"},{"n":"加拿大","v":"加拿大"},{"n":"俄罗斯","v":"俄罗斯"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"order","name":"排序","value":[{"n":"更新时间","v":"0"},{"n":"豆瓣评分","v":"1"}]}],"1":[{"key":"s","name":"类型","value":[{"n":"全部","v":"all"},{"n":"动作","v":"dongzuo"},{"n":"爱情","v":"aiqing"},{"n":"喜剧","v":"xiju"},{"n":"科幻","v":"kehuan"},{"n":"恐怖","v":"kongbu"},{"n":"战争","v":"zhanzheng"},{"n":"武侠","v":"wuxia"},{"n":"魔幻","v":"mohuan"},{"n":"剧情","v":"juqing"},{"n":"动画","v":"donghua"},{"n":"惊悚","v":"jingsong"},{"n":"3D","v":"3D"},{"n":"灾难","v":"zainan"},{"n":"悬疑","v":"xuanyi"},{"n":"警匪","v":"jingfei"},{"n":"文艺","v":"wenyi"},{"n":"青春","v":"qingchun"},{"n":"冒险","v":"maoxian"},{"n":"犯罪","v":"fanzui"},{"n":"纪录","v":"jilu"},{"n":"古装","v":"guzhuang"},{"n":"奇幻","v":"qihuan"},{"n":"国语","v":"guoyu"},{"n":"综艺","v":"zongyi"},{"n":"历史","v":"lishi"},{"n":"运动","v":"yundong"},{"n":"原创压制","v":"yuanchuang"},{"n":"美剧","v":"meiju"},{"n":"韩剧","v":"hanju"},{"n":"国产电视剧","v":"guoju"},{"n":"日剧","v":"riju"},{"n":"英剧","v":"yingju"},{"n":"德剧","v":"deju"},{"n":"俄剧","v":"eju"},{"n":"巴剧","v":"baju"},{"n":"加剧","v":"jiaju"},{"n":"西剧","v":"anish"},{"n":"意大利剧","v":"yidaliju"},{"n":"泰剧","v":"taiju"},{"n":"港台剧","v":"gangtaiju"},{"n":"法剧","v":"faju"},{"n":"澳剧","v":"aoju"}]},{"key":"area","name":"地区","value":[{"n":"不限","v":""},{"n":"中国大陆","v":"中国大陆"},{"n":"中国香港","v":"中国香港"},{"n":"中国台湾","v":"中国台湾"},{"n":"美国","v":"美国"},{"n":"英国","v":"英国"},{"n":"日本","v":"日本"},{"n":"韩国","v":"韩国"},{"n":"法国","v":"法国"},{"n":"印度","v":"印度"},{"n":"德国","v":"德国"},{"n":"西班牙","v":"西班牙"},{"n":"意大利","v":"意大利"},{"n":"澳大利亚","v":"澳大利亚"},{"n":"比利时","v":"比利时"},{"n":"瑞典","v":"瑞典"},{"n":"荷兰","v":"荷兰"},{"n":"丹麦","v":"丹麦"},{"n":"加拿大","v":"加拿大"},{"n":"俄罗斯","v":"俄罗斯"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"order","name":"排序","value":[{"n":"更新时间","v":"0"},{"n":"豆瓣评分","v":"1"}]}]}, + "homeVodNode": "//div[contains(@class, 'pusher ui body')]//div[contains(@class,'ui seven')]/div[position() < 6]", + "homeVodName": "/div[@class='content']/span/text()", + "homeVodId": "/a[@class='image']/@href", + "homeVodIdR": "/(\\S+).htm", + "homeVodImg": "/a[@class='image']/img/@data-src", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "concat(//span[@class='ep'],//span[@class='rate'])", + "cateUrl": "https://www.mp4er.cc/s/{cateId}/{catePg}", + "cateVodNode": "//div[contains(@class,'ui container')]/div[contains(@class,'ui eight doubling')]/div", + "cateVodName": "/div[@class='content']/span/text()", + "cateVodId": "/a[@class='image']/@href", + "cateVodIdR": "/(\\S+).htm", + "cateVodImg": "/a[@class='image']/img/@src", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "concat(//span[@class='ep'],//span[@class='rate'])", + "dtUrl": "https://www.mp4er.cc/{vid}.htm", + "dtNode": "//div[contains(@class,'ui container movie-info')]", + "dtName": "/h2/text()", + "dtNameR": ".+《(\\S+)》.+", + "dtImg": "//div[@class='info0']/img/@src", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//div[@class='info0']/ul/li/a[contains(@target,'new')]/text()", + "dtArea": "//div[@class='info0']/ul/li/strong[contains(text(),'地区')]/parent::text()", + "dtActor": "//div[@class='info0']/ul/li/a[contains(@href,'performer')]/following-sibling::*/text()", + "dtDirector": "//div[@class='info0']/ul/li/a[contains(@href,'director')]/text()", + "dtDesc": "substring-after(//div[@class='info0']//div[@class='summary']/html(), 'br>')", + "dtFromNode": "//div[@class='info1']/h4/div", + "dtFromName": "/text()", + "dtUrlNode": "//div[@class='info1']", + "dtUrlSubNode": "/a", + "dtUrlId": "@href", + "dtUrlIdR": "(\\S+).htm", + "dtUrlName": "/text()", + "playUrl": "https://www.mp4er.cc/{playUrl}.htm" +} \ No newline at end of file diff --git a/CatVodTVSpider/xpath/cjtys.json b/CatVodTVSpider/xpath/cjtys.json new file mode 100644 index 0000000..a5fc9bc --- /dev/null +++ b/CatVodTVSpider/xpath/cjtys.json @@ -0,0 +1,52 @@ +{ + "ua": "", + "homeUrl": "https://cjt521.com", + "dcVipFlag": "true", + "dcPlayUrl": "true", + "cateNode": "//div[contains(@class,'ec-allnav')]//ul/li/a[contains(@href, 'vod/type')]", + "cateName": "/spam/text()", + "cateId": "/@href", + "cateIdR": "/index.php/vod/type/id/(\\d+).html", + "cateManual": {}, + "homeVodNode": "//div[contains(@class,'top20')]//h2[contains(@class,'title-name')and(/span[contains(text(),'最新')])]/parent::*/parent::*/div[contains(@class,'vodlist') and not(contains(@style,'display:none'))]//a[contains(@class,'aplus-exp')]", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/index.php/vod/play/id/(\\w+)/sid/.+", + "homeVodImg": "/div[contains(@class, 'eclazy')]/@data-original", + "homeVodMark": "/span[contains(@class, 'pack-prb')]/text()", + "cateUrl": "https://cjt521.com/index.php/vod/show/id/{cateId}/page/{catePg}.html", + "cateVodNode": "//div[contains(@class, 'vodlist')]//a[contains(@class,'aplus-exp')]", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/index.php/vod/play/id/(\\w+)/sid/.+", + "cateVodImg": "/div[contains(@class, 'eclazy')]/@data-original", + "cateVodMark": "/span[contains(@class, 'pack-prb')]/text()", + "dtUrl": "https://cjt521.com/index.php/vod/detail/id/{vid}.html", + "dtNode": "//div[@class='vod-detail']", + "dtName": "//div[contains(@class,'s-top-right')]/div[contains(@class,'s-top-info-title')]/h1/text()", + "dtImg": "//a[contains(@class,'s-cover-img')]/img/@src", + "dtCate": "//div[contains(@class,'s-top-info-detail')]/p[contains(@class,'item') and contains(/span/text(),'类型')]/span/following-sibling::*/text()", + "dtArea": "//div[contains(@class,'s-top-info-detail')]/p[contains(@class,'item') and contains(/span/text(),'地区')]/span/following-sibling::*/text()", + "dtYear": "//div[contains(@class,'s-top-info-detail')]/p[contains(@class,'item') and contains(/span/text(),'年代')]/span/following-sibling::*/text()", + "dtActor": "//div[contains(@class,'s-top-info-detail')]/p[contains(@class,'item') and contains(/span/text(),'演员')]/span/following-sibling::*/text()", + "dtDirector": "//div[contains(@class,'s-top-info-detail')]/p[contains(@class,'item') and contains(/span/text(),'导演')]/span/following-sibling::*/text()", + "dtDesc": "//div[contains(@class,'s-top-info-detail')]/p[contains(@class,'item-desc')]/span/following-sibling::*/text()", + "dtFromNode": "//div[contains(@class, 'play_source')]/div[contains(@class,'play_source_tab')]/div/a", + "dtFromName": "/text()", + "dtFromNameR": "(.+)\\(", + "dtUrlNode": "//div[contains(@class, 'play_source')]//div[contains(@class, 'play_list_box')]//ul", + "dtUrlSubNode": "/li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/index.php/vod/play/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://cjt521.com/index.php/vod/play/{playUrl}.html", + "playUa": "", + "searchUrl": "https://cjt521.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "" +} diff --git a/CatVodTVSpider/xpath/dm84.json b/CatVodTVSpider/xpath/dm84.json new file mode 100644 index 0000000..01b459a --- /dev/null +++ b/CatVodTVSpider/xpath/dm84.json @@ -0,0 +1,41 @@ +{ + "ua": "", + "homeUrl": "https://dm84.tv", + "cateNode": "//ul[contains(@class,'nav_row')]/li/a[contains(@href, '.html')]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/list-(\\d+).html", + "cateManual": {}, + "homeVodNode": "//ul[@class='v_list']/li/div[@class='item']", + "homeVodName": "/a[@class='title']/@title", + "homeVodId": "/a[@class='title']/@href", + "homeVodIdR": "/v/(\\w+).html", + "homeVodImg": "/a/@data-bg", + "homeVodMark": "/span[@class='desc']/text()", + "cateUrl": "https://dm84.tv/list-{cateId}-{catePg}.html", + "cateVodNode": "//ul[@class='v_list']/li/div[@class='item']", + "cateVodName": "/a[@class='title']/@title", + "cateVodId": "/a[@class='title']/@href", + "cateVodIdR": "/v/(\\w+).html", + "cateVodImg": "/a/@data-bg", + "cateVodMark": "/span[@class='desc']/text()", + "dtUrl": "https://dm84.tv/v/{vid}.html", + "dtNode": "//div[contains(@class,'v_content')]", + "dtName": "/parent::*/h1[@class='v_title']/a/text()", + "dtImg": "/div[@class='cover']/img/@src", + "dtFromNode": "//ul[contains(@class,'play_from')]/li", + "dtFromName": "/text()", + "dtUrlNode": "//ul[contains(@class,'play_list')]", + "dtUrlSubNode": "/li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/p/(\\S+).html", + "dtUrlName": "/text()", + "playUrl": "https://dm84.tv/p/{playUrl}.html", + "searchUrl": "https://dm84.tv/s----------.html?wd={wd}", + "scVodNode": "//ul[@class='v_list']/li/div[@class='item']", + "scVodName": "/a[@class='title']/@title", + "scVodId": "/a[@class='title']/@href", + "scVodIdR": "/v/(\\w+).html", + "scVodImg": "/a/@data-bg", + "scVodMark": "/span[@class='desc']/text()" +} \ No newline at end of file diff --git a/CatVodTVSpider/xpath/egg.json b/CatVodTVSpider/xpath/egg.json new file mode 100644 index 0000000..5cd72dc --- /dev/null +++ b/CatVodTVSpider/xpath/egg.json @@ -0,0 +1,44 @@ +{ + "ua": "", + "homeUrl": "https://www.dandanzan.cc", + "decodeTk": "填上猫大解密接口的口令,注意保密", + "cateNode": "//div[@class='nav']/ul/li/a[@href!='/']", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/(\\S+)/", + "cateManual": {}, + "homeVodNode": "//div[contains(@class,'lists-thumb-top') and @id='slider']//div[@class='lists-content']/ul/li", + "homeVodName": "/h2/a/text()", + "homeVodId": "/h2/a/@href", + "homeVodIdR": "/(\\S+).html", + "homeVodImg": "/a/img/@src", + "homeVodMark": "/a/div[@class='note']/span/text()", + "cateUrl": "https://www.dandanzan.cc/{cateId}/", + "cateVodNode": "//div[@class='lists-content']/ul/li", + "cateVodName": "/h2/a/text()", + "cateVodId": "/h2/a/@href", + "cateVodIdR": "/(\\S+).html", + "cateVodImg": "/a/img/@src", + "cateVodMark": "/a/div[@class='note']/span/text()", + "dtUrl": "http://www.dandanzan.cc/{vid}.html", + "dtNode": "//header[@class='product-header']", + "dtName": "//h1[contains(@class,'product-title')]/text()", + "dtImg": "/img[1]/@src", + "dtCate": "/div[@class='product-excerpt' and contains(text(),'类型')]/span/a/text()", + "dtYear": "//h1[contains(@class,'product-title')]/span[1]/text()", + "dtYearR": "\\((\\S+)\\)", + "dtArea": "/div[@class='product-excerpt' and contains(text(),'地区')]/span/a/text()", + "dtActor": "/div[@class='product-excerpt' and contains(text(),'主演')]/span/a/text()", + "dtDirector": "/div[@class='product-excerpt' and contains(text(),'导演')]/span/a/text()", + "dtDesc": "/div[@class='product-excerpt' and contains(text(),'剧情简介')]/span/text()", + "dtFromNode": "/nil", + "dtUrlNode": "/nil", + "playUrl": "https://www.dandanzan.cc/url.php", + "searchUrl": "https://www.dandanzan.cc/so/{wd}-{wd}--.html", + "scVodNode": "//div[@class='lists-content']/ul/li", + "scVodName": "/h2/a/text()", + "scVodId": "/h2/a/@href", + "scVodIdR": "/(\\S+).html", + "scVodImg": "/a/img/@src", + "scVodMark": "/a/div[@class='note']/span/text()" +} \ No newline at end of file diff --git a/CatVodTVSpider/xpath/jpys.json b/CatVodTVSpider/xpath/jpys.json new file mode 100644 index 0000000..5d0ea6d --- /dev/null +++ b/CatVodTVSpider/xpath/jpys.json @@ -0,0 +1,64 @@ +{ + "ua": "", + "homeUrl": "https://www.jpysvip.net", + "dcVipFlag": "true", + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class,'myui-header__menu')]/li[@class='dropdown-hover']//ul/li/a[contains(@href, 'vodtype') and not(contains(@href, '26'))]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/vodtype/(\\d+).html", + "cateManual": {}, + "homeVodNode": "//div[contains(@class, 'col-lg-wide-75')]//ul[contains(@class,'myui-vodlist')]/li//a[contains(@class,'myui-vodlist__thumb')]", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/voddetail/(\\w+).html", + "homeVodImg": "@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span[contains(@class,'pic-text')]/text()", + "cateUrl": "https://www.jpysvip.net/vodtype/{cateId}-{catePg}.html", + "cateVodNode": "//ul[contains(@class,'myui-vodlist')]//li//a[contains(@class,'myui-vodlist__thumb')]", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/voddetail/(\\w+).html", + "cateVodImg": "@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span[contains(@class,'pic-text')]/text()", + "dtUrl": "https://www.jpysvip.net/voddetail/{vid}.html", + "dtNode": "//div[contains(@class,'col-lg-wide-75')]", + "dtName": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/@title", + "dtNameR": "", + "dtImg": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/img/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '分类')]/following-sibling::*/text()", + "dtCateR": "", + "dtYear": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '地区')]/following-sibling::*/text()", + "dtAreaR": "", + "dtMark": "", + "dtMarkR": "", + "dtActor": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '主演')]/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '导演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '简介')]/parent::text()", + "dtDescR": "", + "dtFromNode": "//a[@data-toggle='tab' and contains(@href, 'playlist')]", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//div[contains(@class,'tab-content')]/div[contains(@id, 'playlist')]", + "dtUrlSubNode": "//li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/vodplay/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://www.jpysvip.net/vodplay/{playUrl}.html", + "playUa": "", + "searchUrl": "https://www.jpysvip.net/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "" +} \ No newline at end of file diff --git a/CatVodTVSpider/xpath/lezhutv.json b/CatVodTVSpider/xpath/lezhutv.json new file mode 100644 index 0000000..d26125a --- /dev/null +++ b/CatVodTVSpider/xpath/lezhutv.json @@ -0,0 +1,72 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "http://www.lezhutv.com", + "dcVipFlag": "true", + "pCfgJs": "http://www.lezhutv.com/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//div[contains(@class,'head')]/div[contains(@class, 'nav')]/a[contains(@href,'type')]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/type/(\\S+).html", + "cateManual": { + "电影":"1", + "连续剧":"2", + "动漫":"4", + "韩剧":"14", + "综艺":"3"}, + "homeVodNode": "//ul[contains(@class, 'tbox_m')]/li[contains(@class,'vbox')]/a[contains(@class,'vbox_t')]", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/detail/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span/text()", + "cateUrl": "http://www.lezhutv.com/type/{cateId}-{catePg}.html", + "cateVodNode": "//ul[contains(@class, 'tbox_m')]/li[contains(@class,'vbox')]/a[contains(@class,'vbox_t')]", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/detail/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span/text()", + "dtUrl": "http://www.lezhutv.com/detail/{vid}.html", + "dtNode": "//body", + "dtName": "//div[@class='data']/h4/text()", + "dtNameR": "", + "dtImg": "//div[@class='dbox']/div[contains(@class,'img item-lazy')]/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//span[contains(text(), '分类')]/following-sibling::*/text()", + "dtCateR": "", + "dtYear": "//span[contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "//span[contains(text(), '地区')]/following-sibling::*/text()", + "dtAreaR": "", + "dtMark": "//span[contains(text(), '更新')]/following-sibling::*/text()", + "dtMarkR": "", + "dtActor": "//p[@class='act']/span[contains(text(), '主演')]/parent::text()", + "dtActorR": "", + "dtDirector": "//p[@class='dir']/span[contains(text(), '导演')]/parent::text()", + "dtDirectorR": "", + "dtDesc": "//span[contains(text(), '简介')]/parent::text()", + "dtDescR": "", + "dtFromNode": "//div[contains(@class, 'tbox_t')]/h3/i[contains(@class, 'icon-pin')]/parent::*", + "dtFromName": "concat('播放源',count(parent::*/preceding::*/div[contains(@class, 'tbox_t')]/h3/i[contains(@class, 'icon-pin')]) + 1)", + "dtFromNameR": "(\\S+).0", + "dtUrlNode": "//div[contains(@class,'tabs_block')]/ul[contains(@class, 'list_block show')]", + "dtUrlSubNode": "/li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/play/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "http://www.lezhutv.com/play/{playUrl}.html", + "playUa": "", + "searchUrl": "http://www.lezhutv.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "" +} diff --git a/CatVodTVSpider/xpath/miniku.json b/CatVodTVSpider/xpath/miniku.json new file mode 100644 index 0000000..29ec2ec --- /dev/null +++ b/CatVodTVSpider/xpath/miniku.json @@ -0,0 +1,54 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://www.miniku.cc", + "dcVipFlag": "true", + "pCfgJs": "https://www.miniku.cc/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class, 'nav-menu-items')]/li/a[contains(@href, 'vodtype')]", + "cateName": "/@title", + "cateId": "/@href", + "cateIdR": "/vodtype/(\\d+)/", + "homeVodNode": "//div[contains(@class, 'module-items')]/div[contains(@class, 'module-item')]", + "homeVodName": "/div[contains(@class, 'module-item-titlebox')]/a/@title", + "homeVodId": "/div[contains(@class, 'module-item-titlebox')]/a/@href", + "homeVodIdR": "/voddetail/(\\w+)/", + "homeVodImg": "/div[contains(@class, 'module-item-cover')]/div[contains(@class, 'module-item-pic')]/img/@data-src", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/div[contains(@class,'module-item-text')]/text()", + "cateUrl": "https://www.miniku.cc/vodshow/{cateId}--------{catePg}---/", + "cateVodNode": "//div[@class='module-items']/div/div/div[@class='module-item-pic']", + "cateVodName": "/a/@title", + "cateVodId": "/a/@href", + "cateVodIdR": "/voddetail/(\\w+)/", + "cateVodImg": "/img/@data-src", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "", + "dtUrl": "https://www.miniku.cc/voddetail/{vid}/", + "dtNode": "//div[@class='box view-heading']", + "dtName": "//div[@class='video-info']/div[@class='video-info-header']/h1/text()", + "dtNameR": "", + "dtImg": "//div[@class='video-cover']//div[@class='module-item-pic']/img/@data-src", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//div[@class='video-info']//div[@class='tag-link']/span/following-sibling::*/text()", + "dtYear": "", + "dtArea": "", + "dtActor": "", + "dtDirector": "", + "dtDesc": "", + "dtFromNode": "//div[contains(@class, 'module-tab-content')]/div[contains(@class, 'module-tab-item')]/span", + "dtFromName": "/text()", + "dtUrlNode": "//div[contains(@class, 'module-blocklist')]", + "dtUrlSubNode": "/div/a", + "dtUrlId": "/@href", + "dtUrlIdR": "/vodplay/(\\S+)/", + "dtUrlName": "/span/text()", + "playUrl": "https://www.miniku.cc/vodplay/{playUrl}/", + "searchUrl": "https://www.miniku.cc/vodsearch/{wd}----------/", + "scVodNode": "//div[@class='module-list']", + "scVodName": "//a/@alt", + "scVodId": "/a/@href", + "scVodImg": "/img/@data-src", + "scVodMark": "" +} diff --git a/CatVodTVSpider/xpath/olevod.json b/CatVodTVSpider/xpath/olevod.json new file mode 100644 index 0000000..744f686 --- /dev/null +++ b/CatVodTVSpider/xpath/olevod.json @@ -0,0 +1,67 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://olevod.com", + "dcVipFlag": "true", + "pCfgJs": "https://olevod.com/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class,'top_nav clearfix')]//li/a[contains(@href,'type')and not(contains(@href,'5'))]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/index.php/vod/type/id/(\\d+).html", + "cateManual": {}, + "homeVodNode": "//div[contains(@class, 'pannel')]//ul[contains(@class,'vodlist vodlist_wi list_v12 clearfix')]/li//a[contains(@class,'vodlist_thumb')]", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/index.php/vod/type/id/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span[contains(@class,'text_dy') and contains(@class,'style')]/text()", + "cateUrl": "https://olevod.com/index.php/vod/type/id/{cateId}/page/{catePg}.html", + "cateVodNode": "//ul[contains(@class,'vodlist vodlist_wi list_v12 clearfix')]//li//a[contains(@class,'vodlist_thumb')]", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/index.php/vod/detail/id/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span[contains(@class,'text_dy') and contains(@class,'style')]/text()", + "dtUrl": "https://olevod.com/index.php/vod/detail/id/{vid}.html", + "dtNode": "//div[contains(@class,'hot_banner')]", + "dtName": "//div[@class='content_box clearfix']/div/a/@title", + "dtNameR": "", + "dtImg": "//div[@class='content_box clearfix']/div/a/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtYear": "//div[contains(@class,'content_detail')]//span[contains(@class,'text_muted hidden_xs') and contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "//div[contains(@class,'content_detail')]//span[contains(@class,'text_muted hidden_xs') and contains(text(), '地区')]/following-sibling::*/text()", + "dtAreaR": "", + "dtCate": "//div[contains(@class,'content_detail')]//span[contains(@class,'text_muted hidden_xs') and contains(text(), '类型')]/following-sibling::*/text()", + "dtCateR": "", + "dtMark": "//div[contains(@class,'content_detail')]//span[contains(text(), '状态')]/following-sibling::*/text()", + "dtMarkR": "", + "dtActor": "//div[contains(@class,'content_detail')]//span[contains(text(), '主演')]/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//div[contains(@class,'content_detail')]//span[contains(text(), '导演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "///div[contains(@class,'content_detail')]//span[contains(@class,'left text_muted') and contains(text(), '简介')]/parent::text()", + "dtDescR": "", + "dtFromNode": "//div[contains(@class,'play_source_tab')]/a", + "dtFromName": "/@alt", + "dtFromNameR": "", + "dtUrlNode": "//div[contains(@class,'playlist_full')]/ul[contains(@class,'content_playlist clearfix')]", + "dtUrlSubNode": "//li/a", + "dtUrlId": "/@href", + "dtUrlIdR": "/index.php/vod/play/id/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://olevod.com/index.php/vod/play/id/{playUrl}.html", + "playUa": "", + "searchUrl": "https://olevod.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "" +} diff --git a/CatVodTVSpider/xpath/onedianshi.json b/CatVodTVSpider/xpath/onedianshi.json new file mode 100644 index 0000000..91799f1 --- /dev/null +++ b/CatVodTVSpider/xpath/onedianshi.json @@ -0,0 +1,60 @@ +{ + "ua": "", + "homeUrl": "https://www.onedianshi.com", + "dcVipFlag": "true", + "pCfgJs": "https://www.onedianshi.com/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[@class='myui-header__menu']/li/a[contains(@href, 'vod')]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/vod/type/id/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//ul[contains(@class,'myui-vodlist')]/li/div/a", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/vod/detail/id/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "//span[contains(@class,'pic-text')]/text()", + "cateUrl": "https://www.onedianshi.com/index.php/vod/show/id/{cateId}/page/{catePg}.html", + "cateVodNode": "//ul[contains(@class,'myui-vodlist')]/li/div/a", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/vod/detail/id/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "//span[contains(@class,'pic-text')]/text()", + "dtUrl": "https://www.onedianshi.com/vod/detail/id/{vid}.html", + "dtNode": "//body", + "dtName": "//div[@class='myui-content__thumb']/a/@title", + "dtNameR": "", + "dtImg": "//div[@class='myui-content__thumb']/a/img/@data-original", + "dtImgR": "", + "dtCate": "//span[contains(text(), '分类')]/following-sibling::a/text()", + "dtYear": "//span[contains(text(), '年份')]/following-sibling::a/text()", + "dtDirector": "//span[contains(text(), '导演')]/following-sibling::a/text()", + "dtActor": "//span[contains(text(), '主演')]/following-sibling::a/text()", + "dtDesc": "//span[contains(text(), '简介')]/following-sibling::span/text()", + "dtFromNode": "//ul[contains(@class,'nav-tabs')]/li/a", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//div[contains(@class,'tab-content')]/div/ul", + "dtUrlSubNode": "/li/a", + "dtUrlId": "/@href", + "dtUrlIdR": "/vod/play/id/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://www.onedianshi.com/vod/play/id/{playUrl}.html", + "playUa": "", + "searchUrl": "https://www.onedianshi.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "" +} + + diff --git a/CatVodTVSpider/xpath/pianba.json b/CatVodTVSpider/xpath/pianba.json new file mode 100644 index 0000000..8b50ea0 --- /dev/null +++ b/CatVodTVSpider/xpath/pianba.json @@ -0,0 +1,65 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://www.pianba.net", + "dcVipFlag": "true", + "pCfgJs": "https://www.pianba.net/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class,'stui-header__menu')]/li/a[contains(@href, 'sort') and not(contains(@href,'label'))]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/sort/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//div[contains(@class, 'stui-pannel__bd')]//ul[contains(@class,'stui-vodlist')]/li//a[contains(@class,'stui-vodlist__thumb')]", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/html/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span[contains(@class,'pic-text')]/text()", + "cateUrl": "https://www.pianba.net/sort/{cateId}-{catePg}.html", + "cateVodNode": "//ul[contains(@class,'stui-vodlist')]//li//a[contains(@class,'stui-vodlist__thumb')]", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/html/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span[contains(@class,'pic-text')]/text()", + "dtUrl": "https://www.pianba.net/html/{vid}.html", + "dtNode": "//div[@class='stui-content']", + "dtName": "//div[@class='stui-content__detail']/h1/text()", + "dtNameR": "", + "dtImg": "//div[@class='stui-content__thumb']/a/img/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtYear": "//div[@class='stui-content__detail']//span[contains(@class,'data2') and contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtCate": "//div[@class='stui-content__detail']//span[contains(@class,'data2') and contains(text(), '类型')]/following-sibling::*/text()", + "dtCateR": "", + "dtArea": "//div[@class='stui-content__detail']//span[contains(@class,'data2') and contains(text(), '地区')]/following-sibling::*/text()", + "dtAreaR": "", + "dtDirector": "//div[@class='stui-content__detail']//span[contains(@class,'data2') and contains(text(), '导演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtActor": "//div[@class='stui-content__detail']//span[contains(@class,'data2') and contains(text(), '演员')]/following-sibling::*/text()", + "dtActorR": "", + "dtDesc": "//div[@class='stui-content__detail']//span[contains(@class,'data2') and contains(text(), '简介')]/parent::text()", + "dtDescR": "", + "dtFromNode": "//div[contains(@class,'stui-vodlist__head')]/span", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//ul[contains(@class,'stui-content__playlist')]", + "dtUrlSubNode": "//li/a", + "dtUrlId": "/@href", + "dtUrlIdR": "/yun/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://www.pianba.net/yun/{playUrl}.html", + "playUa": "", + "searchUrl": "https://www.pianba.net/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "" +} diff --git a/CatVodTVSpider/xpath/saohuotv2.json b/CatVodTVSpider/xpath/saohuotv2.json new file mode 100644 index 0000000..6af0a18 --- /dev/null +++ b/CatVodTVSpider/xpath/saohuotv2.json @@ -0,0 +1,44 @@ +{ + "ua": "", + "homeUrl": "https://saohuotv.com", + "cateNode": "//ul[contains(@class,'type_list')]/li/a[contains(@href, '.html')]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/list/(\\d+).html", + "cateManual": {}, + "homeVodNode": "//ul[@class='v_list']/li/div[@class='v_img']", + "homeVodName": "/a/@title", + "homeVodId": "/a/@href", + "homeVodIdR": "/movie/(\\w+).html", + "homeVodImg": "/a/img/@data-original", + "homeVodMark": "/div[@class='v_note']/text()", + "cateUrl": "https://saohuotv.com/list/{cateId}-{catePg}.html", + "cateVodNode": "//ul[@class='v_list']/li/div[@class='v_img']", + "cateVodName": "/a/@title", + "cateVodId": "/a/@href", + "cateVodIdR": "/movie/(\\w+).html", + "cateVodImg": "/a/img/@data-original", + "cateVodMark": "/div[@class='v_note']/text()", + "dtUrl": "http://saohuotv.com/movie/{vid}.html", + "dtNode": "//body", + "dtName": "//section[contains(@class,'v_info_box')]/h1[1]/text()", + "dtImg": "//section[contains(@class,'v_info')]/div[@class='m_background']/@style", + "dtImgR": "background-image:url\\((\\S+)\\)", + "dtCate": "//section[contains(@class,'v_info_box')]/p[1]/text()", + "dtDesc": "//p[contains(@class,'show_part')]/text()", + "dtFromNode": "//ul[@class='from_list']/li", + "dtFromName": "/text()", + "dtUrlNode": "//ul[@class='play_list' or @class='large_list']/li", + "dtUrlSubNode": "/a", + "dtUrlId": "@href", + "dtUrlIdR": "/play/(\\S+).html", + "dtUrlName": "/text()", + "playUrl": "http://saohuotv.com/play/{playUrl}.html", + "searchUrl": "https://saohuotv.com/search.php?searchword={wd}", + "scVodNode": "//ul[@class='v_list']/li/div[@class='v_img']", + "scVodName": "/a/@title", + "scVodId": "/a/@href", + "scVodIdR": "/movie/(\\w+).html", + "scVodImg": "/a/img/@data-original", + "scVodMark": "/div[@class='v_note']/text()" +} \ No newline at end of file diff --git a/CatVodTVSpider/xpath/tvci.json b/CatVodTVSpider/xpath/tvci.json new file mode 100644 index 0000000..1eb3ea3 --- /dev/null +++ b/CatVodTVSpider/xpath/tvci.json @@ -0,0 +1,68 @@ +{ + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", + "homeUrl": "https://tv.ci", + "dcVipFlag": "true", + "pCfgJs": "https://tv.ci/static/js/playerconfig.js", + "pCfgJsR": "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list", + "dcShow2Vip": {}, + "dcPlayUrl": "true", + "cateNode": "//ul[contains(@class, 'myui-header__menu nav-menu')]/li[@class='dropdown-hover']//div/ul/li/a[contains(@href, 'type') and not(contains(@href,'label'))]", + "cateName": "/text()", + "cateId": "/@href", + "cateIdR": "/type/(\\w+).html", + "cateManual": {}, + "homeVodNode": "//div[contains(@class, 'col-lg-wide-75')]//ul[contains(@class,'myui-vodlist')]/li//a[contains(@class,'myui-vodlist__thumb')]", + "homeVodName": "/@title", + "homeVodId": "/@href", + "homeVodIdR": "/v/(\\w+).html", + "homeVodImg": "/@data-original", + "homeVodImgR": "\\S+(http\\S+)", + "homeVodMark": "/span[contains(@class,'pic-text')]/text()", + "cateUrl": "https://tv.ci/vodshow/{cateId}-{area}--{class}-{lang}----{catePg}---{year}.html", + "cateVodNode": "//ul[contains(@class,'myui-vodlist')]//li//a[contains(@class,'myui-vodlist__thumb')]", + "cateVodName": "/@title", + "cateVodId": "/@href", + "cateVodIdR": "/v/(\\w+).html", + "cateVodImg": "/@data-original", + "cateVodImgR": "\\S+(http\\S+)", + "cateVodMark": "/span[contains(@class,'pic-text')]/text()", + "dtUrl": "https://tv.ci/v/{vid}.html", + "dtNode": "//div[contains(@class,'col-lg-wide-75')]", + "dtName": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/@title", + "dtNameR": "", + "dtImg": "//div[@class='myui-content__thumb']/a[contains(@class,'myui-vodlist__thumb')]/img/@data-original", + "dtImgR": "\\S+(http\\S+)", + "dtCate": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '分类')]/following-sibling::*/text()", + "dtCateR": "", + "dtYear": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '年份')]/following-sibling::*/text()", + "dtYearR": "", + "dtArea": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '地区')]/following-sibling::*/text()", + "dtAreaR": "", + "dtMark": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '更新')]/following-sibling::*/text()", + "dtMarkR": "", + "dtActor": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '主演')]/following-sibling::*/text()", + "dtActorR": "", + "dtDirector": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '导演')]/following-sibling::*/text()", + "dtDirectorR": "", + "dtDesc": "//div[@class='myui-content__detail']//span[contains(@class,'text-muted') and contains(text(), '简介')]/parent::text()", + "dtDescR": "", + "dtFromNode": "//a[@data-toggle='tab' and contains(@href, 'playlist')]", + "dtFromName": "/text()", + "dtFromNameR": "", + "dtUrlNode": "//div[contains(@class,'tab-content')]/div[contains(@id, 'playlist')]", + "dtUrlSubNode": "//li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/p/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "https://tv.ci/p/{playUrl}.html", + "playUa": "", + "searchUrl": "https://tv.ci/index.php/ajax/suggest?mid=1&wd={wd}&limit=10", + "scVodNode": "json:list", + "scVodName": "name", + "scVodId": "id", + "scVodIdR": "", + "scVodImg": "pic", + "scVodMark": "", + "filter":{"1":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":""},{"n":"动作片","v":"6"},{"n":"喜剧片","v":"7"},{"n":"爱情片","v":"8"},{"n":"科幻片","v":"9"},{"n":"奇幻片","v":"38"},{"n":"恐怖片","v":"10"},{"n":"剧情片","v":"11"},{"n":"战争片","v":"12"},{"n":"纪录片","v":"22"},{"n":"悬疑片","v":"44"},{"n":"冒险片","v":"45"},{"n":"犯罪片","v":"46"}]},{"key":"class","name":"剧情","value":[{"n":"全部","v":""},{"n":"喜剧","v":"喜剧"},{"n":"爱情","v":"爱情"},{"n":"恐怖","v":"恐怖"},{"n":"动作","v":"动作"},{"n":"科幻","v":"科幻"},{"n":"剧情","v":"剧情"},{"n":"战争","v":"战争"},{"n":"警匪","v":"警匪"},{"n":"犯罪","v":"犯罪"},{"n":"动画","v":"动画"},{"n":"奇幻","v":"奇幻"},{"n":"武侠","v":"武侠"},{"n":"冒险","v":"冒险"},{"n":"枪战","v":"枪战"},{"n":"悬疑","v":"悬疑"},{"n":"惊悚","v":"惊悚"},{"n":"经典","v":"经典"},{"n":"青春","v":"青春"}]},{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"大陆","v":"大陆"},{"n":"香港","v":"香港"},{"n":"台湾","v":"台湾"},{"n":"美国","v":"美国"},{"n":"法国","v":"法国"},{"n":"英国","v":"英国"},{"n":"日本","v":"日本"},{"n":"韩国","v":"韩国"},{"n":"德国","v":"德国"},{"n":"泰国","v":"泰国"},{"n":"印度","v":"印度"},{"n":"意大利","v":"意大利"},{"n":"西班牙","v":"西班牙"},{"n":"加拿大","v":"加拿大"},{"n":"其他","v":"其他"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":""},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],"2":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":""},{"n":"国产剧","v":"13"},{"n":"港台剧","v":"14"},{"n":"日韩剧","v":"15"},{"n":"欧美剧","v":"16"},{"n":"海外剧","v":"23"},{"n":"动画片","v":"50"}]},{"key":"class","name":"剧情","value":[{"n":"全部","v":""},{"n":"古装","v":"古装"},{"n":"青春偶像","v":"青春偶像"},{"n":"喜剧","v":"喜剧"},{"n":"家庭","v":"家庭"},{"n":"犯罪","v":"犯罪"},{"n":"动作","v":"动作"},{"n":"奇幻","v":"奇幻"},{"n":"剧情","v":"剧情"},{"n":"历史","v":"历史"},{"n":"经典","v":"经典"},{"n":"乡村","v":"乡村"},{"n":"情景","v":"情景"},{"n":"商战","v":"商战"},{"n":"网剧","v":"网剧"},{"n":"其他","v":"其他"}]},{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"内地","v":"内地"},{"n":"韩国","v":"韩国"},{"n":"香港","v":"香港"},{"n":"台湾","v":"台湾"},{"n":"日本","v":"日本"},{"n":"美国","v":"美国"},{"n":"泰国","v":"泰国"},{"n":"英国","v":"英国"},{"n":"新加坡","v":"新加坡"},{"n":"其他","v":"其他"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"}]},{"key":"lang","name":"语言","value":[{"n":"全部","v":""},{"n":"国语","v":"国语"},{"n":"英语","v":"英语"},{"n":"粤语","v":"粤语"},{"n":"闽南话","v":"闽南话"},{"n":"韩语","v":"韩语"},{"n":"日语","v":"日语"},{"n":"其它","v":"其它"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":""},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],"3":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":""},{"n":"内地综艺","v":"29"},{"n":"港台综艺","v":"30"},{"n":"日韩综艺","v":"31"},{"n":"欧美综艺","v":"32"}]},{"key":"class","name":"剧情","value":[{"n":"全部","v":""},{"n":"选秀","v":"选秀"},{"n":"情感","v":"情感"},{"n":"访谈","v":"访谈"},{"n":"播报","v":"播报"},{"n":"旅游","v":"旅游"},{"n":"音乐","v":"音乐"},{"n":"美食","v":"美食"},{"n":"纪实","v":"纪实"},{"n":"曲艺","v":"曲艺"},{"n":"生活","v":"生活"},{"n":"财经","v":"财经"},{"n":"求职","v":"求职"}]},{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"内地","v":"内地"},{"n":"港台","v":"港台"},{"n":"日韩","v":"日韩"},{"n":"欧美","v":"欧美"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"}]},{"key":"lang","name":"语言","value":[{"n":"全部","v":""},{"n":"国语","v":"国语"},{"n":"英语","v":"英语"},{"n":"粤语","v":"粤语"},{"n":"闽南语","v":"闽南语"},{"n":"韩语","v":"韩语"},{"n":"日语","v":"日语"},{"n":"其它","v":"其它"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":""},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],"4":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":""},{"n":"国产动漫","v":"33"},{"n":"日韩动漫","v":"34"},{"n":"欧美动漫","v":"35"},{"n":"港台动漫","v":"36"},{"n":"海外动漫","v":"37"},{"n":"动漫电影","v":"39"},{"n":"番剧","v":"60"},{"n":"国创","v":"61"}]},{"key":"class","name":"剧情","value":[{"n":"全部","v":""},{"n":"情感","v":"情感"},{"n":"科幻","v":"科幻"},{"n":"热血","v":"热血"},{"n":"推理","v":"推理"},{"n":"搞笑","v":"搞笑"},{"n":"冒险","v":"冒险"},{"n":"萝莉","v":"萝莉"},{"n":"校园","v":"校园"},{"n":"动作","v":"动作"},{"n":"机战","v":"机战"},{"n":"运动","v":"运动"},{"n":"战争","v":"战争"},{"n":"少年","v":"少年"},{"n":"少女","v":"少女"},{"n":"社会","v":"社会"},{"n":"原创","v":"原创"},{"n":"亲子","v":"亲子"},{"n":"益智","v":"益智"},{"n":"励志","v":"励志"}]},{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"国产","v":"国产"},{"n":"日本","v":"日本"},{"n":"欧美","v":"欧美"},{"n":"其他","v":"其他"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"}]},{"key":"lang","name":"语言","value":[{"n":"全部","v":""},{"n":"国语","v":"国语"},{"n":"英语","v":"英语"},{"n":"粤语","v":"粤语"},{"n":"闽南语","v":"闽南语"},{"n":"韩语","v":"韩语"},{"n":"日语","v":"日语"},{"n":"其它","v":"其它"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":""},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}]}} +} diff --git a/CatVodTVSpider/xpath/yinghua.json b/CatVodTVSpider/xpath/yinghua.json new file mode 100644 index 0000000..9166fe6 --- /dev/null +++ b/CatVodTVSpider/xpath/yinghua.json @@ -0,0 +1,54 @@ +{ + "ua": "", + "homeUrl": "http://www.yinghuacd.com", + "cateManual": {"国产动漫":"guochandongman","日本动漫":"ribendongman","美国动漫":"meiguodongman","动漫电影":"movie","亲子动漫":"qinzi"}, + "homeVodNode": "//div[@class='img']/ul/li", + "homeVodName": "/p[@class='tname']/a/text()", + "homeVodNameR": "", + "homeVodId": "/a/@href", + "homeVodIdR": "/show/(\\w+).html", + "homeVodImg": "/a/img/@src", + "homeVodImgR": "", + "homeVodMark": "/p[contains(text(),'最新')]/a/text()", + "homeVodMarkR": "", + "cateUrl": "http://www.yinghuacd.com/{cateId}/", + "cateVodNode": "//div[@class='imgs']/ul/li", + "cateVodName": "/p[@class='tname']/a/text()", + "cateVodId": "/a/@href", + "cateVodIdR": "/show/(\\w+).html", + "cateVodImg": "/a/img/@src", + "cateVodMark": "/p[contains(text(),'最新')]/a/text()", + "dtUrl": "http://www.yinghuacd.com/show/{vid}.html", + "dtNode": "//div[@class='fire l']", + "dtName": "//div[@class='thumb l']/img/@alt", + "dtNameR": "", + "dtImg": "//div[@class='thumb l']/img/@src", + "dtImgR": "", + "dtCate": "//div[@class='sinfo']//label[contains(text(),'类型')]/parent::*/a/text()", + "dtCateR": "", + "dtYear": "//div[@class='sinfo']//label[contains(text(),'上映')]/parent::*/a/text()", + "dtYearR": "", + "dtArea": "//div[@class='sinfo']//label[contains(text(),'地区')]/parent::*/a/text()", + "dtAreaR": "", + "dtMark": "//div[@class='sinfo']/p/text()", + "dtMarkR": "", + "dtDesc": "//div[@class='info']/text()", + "dtDescR": "", + "dtFromNode": "//div[contains(@class,'tabs')]/ul[@class='menu0']/li", + "dtFromName": "/text()", + "dtUrlNode": "//div[contains(@class,'movurl')]/ul", + "dtUrlSubNode": "/li/a", + "dtUrlId": "@href", + "dtUrlIdR": "/v/(\\S+).html", + "dtUrlName": "/text()", + "dtUrlNameR": "", + "playUrl": "http://www.yinghuacd.com/v/{playUrl}.html", + "playUa": "", + "searchUrl": "http://www.yinghuacd.com/search/{wd}", + "scVodNode": "//div[@class='lpic']/ul/li", + "scVodName": "/a/img/@alt", + "scVodId": "/a/@href", + "scVodIdR": "/show/(\\w+).html", + "scVodImg": "/a/img/@src", + "scVodMark": "//font[@class='red']/text()" +}