猫咪av

破解vip视频免费观看

  1. // ==UserScript==
  2. // @name 猫咪av
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.4
  5. // @description 破解vip视频免费观看
  6. // @author thunder-sword
  7. // @match *://*/home
  8. // @match *://*/page/vip/*
  9. // @match *://*/page/remen/*
  10. // @require https://cdn.staticfile.net/crypto-js/4.2.0/crypto-js.min.js#sha256=dppVXeVTurw1ozOPNE3XqhYmDJPOosfbKQcHyQSE58w=
  11. // @license MIT
  12. // @icon https://www.google.com/s2/favicons?sz=64&domain=f2c013d5bbbb.com
  13. // @grant none
  14. // @connect mjson.szaction.cc
  15. // ==/UserScript==
  16.  
  17. //变量作用:负责一些配置
  18. var settings = {
  19. m3u8Prefix: "https://tma.qianchengwandian.top", //setSource(config?.m3u8_host_encrypt ?? data?.source?.m3u8_host)(video.tsx)
  20. apiPrefix: "https://mjson.szaction.cc", //_domain = _domain.endsWith('/') ? _domain : `${_domain}/`;(useAxios.ts)
  21. }
  22.  
  23. //变量作用:管理当前pageIndex
  24. var pageIndex=1;
  25. //变量作用:管理当前mediaType
  26. var mediaType=1;
  27.  
  28. //常量作用:页面端type和mediaType不是一一对应的,需要转换一下(/data/category/base-2.js)
  29. const mediaTypes={
  30. 58: "base-vip-mmtj-",
  31. 59: "base-vip-ycgc-",
  32. 62: "base-vip-zfyx-",
  33. 60: "base-vip-hlaiq-",
  34. 61: "base-vip-sjzy-",
  35. 63: "base-vip-cydm-",
  36. 64: "base-vip-omdp-",
  37. 65: "base-vip-txzq-",
  38. 150: "base-remen-xpsd-",
  39. 151: "base-remen-djjx-",
  40. 152: "base-remen-gccm-",
  41. 153: "base-remen-jpgq-",
  42. 154: "base-remen-zmcs-",
  43. 155: "base-remen-thss-",
  44. 156: "base-remen-spxm-",
  45. 157: "base-remen-avjs-",
  46. }
  47.  
  48. //常量作用:加解密所需常量
  49. const key = CryptoJS.enc.Utf8.parse("IdTJq0HklpuI6mu8iB%OO@!vd^4K&uXW");
  50. const iv = "$0v@krH7V2";
  51. const mode = CryptoJS.mode.CBC;
  52. const padding = CryptoJS.pad.Pkcs7;
  53. const formatter = CryptoJS.format.OpenSSL;
  54. const secretKey = "D7hGKHnWThaECaQ3ji4XyAF3MfYKJ53M";
  55.  
  56. //来自util.js
  57. // this.key = "SWRUSnEwSGtscHVJNm11OGlCJU9PQCF2ZF40SyZ1WFc=";
  58. // this.iv = "JDB2QGtySDdWMg==";
  59. // this.sign_key = "JkI2OG1AJXpnMzJfJXUqdkhVbEU0V2tTJjFKNiUleG1VQGZO";
  60. // this.suffix = 123456;
  61. // this.statDomain = process.env.REACT_APP_STATIC_STAT_DOMAIN;
  62. // this.secretKey = "D7hGKHnWThaECaQ3ji4XyAF3MfYKJ53M";
  63.  
  64. //判断第三方库是否导入成功
  65. if('undefined'===typeof CryptoJS || undefined===CryptoJS.AES){
  66. showStackToast("导入第三方库crypto-js失败","red");
  67. throw Error("导入第三方库crypto-js失败,请尝试更换cdn或更改网络环境");
  68. }
  69.  
  70. function addVidKeyParam2(url) {
  71. let secret_key = secretKey;
  72. let currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
  73. let storedTime = sessionStorage.getItem('timestamp'); // Get the stored timestamp from sessionStorage
  74.  
  75. let time; // Declare the time variable
  76.  
  77. // If there is a stored time and 5 minutes haven't passed, reuse the stored time
  78. if (storedTime && currentTime - parseInt(storedTime) < 300) {
  79. time = parseInt(storedTime) + 300; // Use the stored timestamp and add 300 seconds
  80. } else {
  81. // If no stored time or more than 5 minutes have passed, generate a new timestamp
  82. time = currentTime + 300; // Add 300 seconds to the current time
  83. sessionStorage.setItem('timestamp', currentTime.toString()); // Store the new timestamp in sessionStorage
  84. }
  85.  
  86. // Convert the time to a hexadecimal string
  87. let hexTime = time.toString(16);
  88. console.log('Hex time:', hexTime);
  89.  
  90. // The URI for which we're generating the key
  91. let uri = url;
  92. console.log('URI:', uri);
  93.  
  94. // Concatenate secret_key, uri, and time (decimal) for the MD5 hash
  95. let paramToAppend = secret_key + uri + time; // Time in decimal
  96. console.log('Param to append for MD5:', paramToAppend);
  97.  
  98. // Calculate the MD5 hash using CryptoJS
  99. let key = CryptoJS.MD5(paramToAppend).toString();
  100. console.log('Generated key (MD5):', key);
  101.  
  102. // Return the URL with wsSecret (the MD5 hash) and wsTime (in decimal)
  103. return '?wsSecret=' + key + "&wsTime=" + time; // wsTime is the time in seconds (decimal)
  104. }
  105.  
  106. function encrypt(plaintext, suffix = "123456") {
  107. var tmp=CryptoJS.enc.Utf8.parse(plaintext);
  108. let new_iv = CryptoJS.enc.Utf8.parse(iv + suffix);
  109. var enc=CryptoJS.AES.encrypt(tmp, key, {
  110. iv: new_iv,
  111. mode: mode,
  112. padding: padding,
  113. formatter: formatter
  114. });
  115. return enc.toString();
  116. }
  117.  
  118. //let __data = u.decrypt(response.data.data, response.data.suffix);(useAxios.ts)
  119. function decrypt(ciphertext, suffix = "123456") {
  120. let new_iv = CryptoJS.enc.Utf8.parse(iv + suffix);
  121. var dec=CryptoJS.AES.decrypt(ciphertext, key, {
  122. iv: new_iv,
  123. mode: mode,
  124. padding: padding,
  125. formatter: formatter
  126. });
  127. var s=dec.toString(CryptoJS.enc.Utf8);
  128. return JSON.parse(s);
  129. }
  130.  
  131. function getSignature(data) {
  132. function objKeySort(arys) {
  133. let newObj = {};
  134. let newkey = Object.keys(arys).sort();
  135. for (var i = 0; i < newkey.length; i++) {
  136. newObj[newkey[i]] = arys[newkey[i]];
  137. }
  138. return newObj;
  139. }
  140. data = objKeySort(data);
  141. let pre_sign = "";
  142. for (let i in data) {
  143. pre_sign += i + "=" + data[i] + "&";
  144. }
  145. let key = '&B68m@%zg32_%u*vHUlE4WkS&1J6%%xmU@fN';
  146. pre_sign += key;
  147. return CryptoJS.MD5(pre_sign).toString();
  148. }
  149.  
  150. //作用:生成toast,让其在toast_container中,显示在页面中上部,会永久性向页面添加一个id为ths_toast_container的div标签
  151. function showStackToast(message, backcolor='rgb(76, 175, 80)', timeout=3000){
  152. //没有容器则生成容器
  153. let box=document.querySelector("body > div#ths_toast_container");
  154. if(!box){
  155. box=document.createElement('div');
  156. box.id="ths_toast_container";
  157. box.style.cssText = `
  158. position: fixed;
  159. top: 10px;
  160. left: 50%;
  161. transform: translateX(-50%);
  162. right: 10px;
  163. width: 300px;
  164. height: auto;
  165. display: flex;
  166. z-index: 9999;
  167. flex-direction: column-reverse;`;
  168. document.body.appendChild(box);
  169. }
  170. //创建toast
  171. const toast = document.createElement('div');
  172. toast.innerText = message;
  173. toast.style.cssText = `
  174. padding: 10px;
  175. background-color: ${backcolor};
  176. color: rgb(255, 255, 255);
  177. border-radius: 10px;
  178. font-size: 24px;
  179. font-weight: bold;
  180. text-align: center;
  181. box-shadow: rgb(0 0 0 / 30%) 0px 5px 10px;
  182. opacity: 1;
  183. transition: opacity 0.3s ease-in-out 0s;
  184. z-index: 9999;
  185. margin: 5px;
  186. `;
  187. box.appendChild(toast);
  188. toast.style.opacity = 1;
  189. if(timeout > 0){
  190. setTimeout(() => {
  191. toast.style.opacity = 0;
  192. setTimeout(() => {
  193. box.removeChild(toast);
  194. }, 300);
  195. }, timeout);
  196. }
  197. return toast;
  198. }
  199.  
  200. class NetworkError extends Error {
  201. constructor(message) {
  202. super(message);
  203. this.name = "NetworkError";
  204. }
  205. }
  206.  
  207. //api='https://mmnew.tlxxw.cc/api/list/base';
  208.  
  209. async function query(url, obj={}){
  210. let data='';
  211. try{
  212. //data=encrypt(JSON.stringify(obj));
  213. //data=JSON.stringify(obj);
  214. }catch(e){
  215. console.log("发送包aes加密失败");
  216. console.log(obj);
  217. console.log(key);
  218. console.log(gzip);
  219. throw e;
  220. }
  221. const ret = await fetch(url, {
  222. method: 'GET',
  223. headers: {
  224. //'Content-Type': 'application/json'
  225. }})
  226. .then(function(response) {
  227. if(!response.ok) {
  228. throw new NetworkError(`HTTP error! status: ${response.status}`);
  229. }
  230. return response.text();
  231. }).catch(error => {
  232. if (error instanceof NetworkError) {
  233. console.error("Network error: ", error.message);
  234. throw error;
  235. } else {
  236. console.error("Other error: ", error);
  237. //alert("发生其他错误");
  238. throw error;
  239. }
  240. });
  241. const tmp=JSON.parse(ret);
  242. if(!tmp || 0!==tmp.code){
  243. showStackToast("解析返回包失败!","red");
  244. console.log(tmp);
  245. throw Error("解析返回包失败!");
  246. }
  247. //解密返回包
  248. try{
  249. data=decrypt(tmp.data, tmp.suffix);
  250. }catch(e){
  251. console.log("返回包aes解密失败");
  252. console.log(tmp);
  253. console.log(key);
  254. throw e;
  255. }
  256. return data;
  257. }
  258.  
  259. //作用:弹出新窗口播放指定m3u8视频
  260. function playMedia(mediaUrl, width=800, height=600) {
  261. /* 第三个参数规定了新窗口的大小,不加则会以新窗口的形式出现 */
  262. var windowHandle = window.open("", "_blank", `width=${width}, height=${height}`);
  263. windowHandle.document.write(`
  264. <html>
  265. <head>
  266. <script src="https://cdn.staticfile.net/dplayer/1.27.1/DPlayer.min.js"></script>
  267. <script src="https://cdn.staticfile.net/hls.js/1.5.1/hls.min.js"></script>
  268. </head>
  269. <body>
  270. <div id="dplayer"></div>
  271. <script>
  272. //var url = prompt("请输入要播放的视频url");
  273. var dp = new DPlayer({
  274. container: document.getElementById('dplayer'),
  275. autoplay: true,
  276. theme: '#FADFA3',
  277. loop: true,
  278. lang: 'zh',
  279. screenshot: true,
  280. hotkey: true,
  281. preload: 'auto',
  282. video: {
  283. url: "${mediaUrl}",
  284. type: 'hls'
  285. }
  286. });
  287. </script>
  288. </body>
  289. </html>
  290. `);
  291. }
  292.  
  293. //await query("https://utt.51jiajiao.top/data/index/home.js?1718510400000");
  294.  
  295. //插入视频条目执行函数
  296. async function insertList(box, videoList){
  297. showStackToast("正在破解中...");
  298. //修改第一个容器内的标识
  299. box.firstElementChild.firstElementChild.innerText="已破解VIP视频";
  300. let list=box.querySelector("div:nth-child(2) > div");
  301. //循环添加事件
  302. for(let i=0; i < videoList.length && i < list.childNodes.length; i++){
  303. //先去除绑定
  304. list.childNodes[i].onclick= () => {
  305. if(!videoList[i].video_url){
  306. alert("未找到该视频地址");
  307. throw Error("未找到该视频地址");
  308. }
  309. let video_url=videoList[i].video_url.replaceAll('//', '/')
  310. let m3u8url=settings['m3u8Prefix']+video_url;
  311. m3u8url=m3u8url+addVidKeyParam2(video_url);
  312. showStackToast("视频地址获取成功,将弹窗播放");
  313. showStackToast(videoList[i].title);
  314. console.log(m3u8url);
  315. console.log(videoList[i]);
  316. playMedia(m3u8url);
  317. };
  318. }
  319. showStackToast("破解完成!");
  320. }
  321.  
  322. //获取当前时间戳
  323. function getTime(){
  324. var time = new Date();
  325. if (time.getHours() < 4){
  326. time.setHours(0, 0, 0, 0)
  327. }
  328. else if (time.getHours() < 8){
  329. time.setHours(4, 0, 0, 0)
  330. }
  331. else if (time.getHours() < 12){
  332. time.setHours(8, 0, 0, 0)
  333. }
  334. else if (time.getHours() < 16){
  335. time.setHours(12, 0, 0, 0)
  336. }
  337. else if (time.getHours() < 20){
  338. time.setHours(16, 0, 0, 0)
  339. }
  340. else if (time.getHours() < 24){
  341. time.setHours(20, 0, 0, 0)
  342. }
  343. else{
  344. time.setHours(24, 0, 0, 0)
  345. }
  346.  
  347. let ts = time.getTime();
  348. return ts;
  349. }
  350.  
  351. //main要执行的内容
  352. async function main_func(){
  353. //let apiUrl=settings["apiPrefix"]+"/data/index/home.js?"+getTime();
  354. let apiUrl=settings["apiPrefix"]+"/data/index/home.js?";
  355. //showStackToast(apiUrl);
  356. const ret = await query(apiUrl);
  357. if(!ret || !ret.vip_list || !ret.vip_list.data){
  358. alert("api访问异常");
  359. throw Error("api访问异常");
  360. }
  361. showStackToast(`成功找到【${ret.vip_list.data.length}】个vip视频`);
  362. //查找box
  363. let box = document.querySelector("div.mw1100 > div:nth-child(1)");
  364. if(!box){
  365. showStackToast("未找到box!", "red");
  366. throw Error("未找到box!");
  367. }
  368. showStackToast("成功找到box!");
  369. insertList(box, ret.vip_list.data);
  370. }
  371.  
  372. ///page/vip/和/page/remen/的执行内容
  373. async function func_list(user_id=0){
  374. let urlType=mediaTypes[mediaType];
  375. if(undefined === urlType){
  376. alert("脚本异常,urlType为空");
  377. throw Error("脚本异常,urlType为空");
  378. }
  379. //let apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?"+getTime();
  380. let apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?";
  381. //showStackToast(apiUrl);
  382. const ret = await query(apiUrl);
  383. if(!ret || !ret.list || !ret.list.data){
  384. alert("api访问异常");
  385. throw Error("api访问异常");
  386. }
  387. showStackToast(`成功找到【${ret.list.data.length}】个vip视频`);
  388. //查找box
  389. let box = document.querySelector("div.mw1100");
  390. if(!box){
  391. showStackToast("未找到box!", "red");
  392. throw Error("未找到box!");
  393. }
  394. showStackToast("成功找到box!");
  395. insertList(box, ret.list.data);
  396. //找到上一页、下一页按钮容器
  397. let pagination=document.querySelector("div.fl.align_center.justify_center.gap20.mb20");
  398. //清空原有按钮
  399. //pagination.innerHTML='';
  400. pagination.removeChild(pagination.lastChild);
  401. pagination.removeChild(pagination.firstChild);
  402. pagination.removeChild(pagination.childNodes[1]);
  403. //找到上一页按钮
  404. var prePage=pagination.firstChild;
  405. prePage.textContent="上一页";
  406. prePage.setAttribute("style", "padding: 5px .6rem;border: 1px solid #bebebd;text-align: center;border-radius: 7px;cursor: pointer;background: #fff;");
  407. //pagination.appendChild(prePage);
  408. //绑定事件
  409. prePage.addEventListener("click", async () => {
  410. if(pageIndex<=1){
  411. showStackToast(`当前page${pageIndex},不能再向上一页`);
  412. throw Error(`当前page${pageIndex},不能再向上一页`);
  413. }
  414. pageIndex-=1;
  415. //apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?"+getTime();
  416. apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?";
  417. //showStackToast(apiUrl);
  418. const ret = await query(apiUrl);
  419. if(!ret || !ret.list || !ret.list.data){
  420. alert("api访问异常");
  421. throw Error("api访问异常");
  422. }
  423. showStackToast(`成功找到【${ret.list.data.length}】个vip视频`);
  424. insertList(box, ret.list.data);
  425. });
  426. //找到下一页按钮
  427. var nextPage=pagination.lastChild;
  428. nextPage.textContent="下一页";
  429. nextPage.setAttribute("style", "padding: 5px .6rem;border: 1px solid #bebebd;text-align: center;border-radius: 7px;cursor: pointer;background: #fff;");
  430. //pagination.appendChild(nextPage);
  431. //绑定事件
  432. nextPage.addEventListener("click", async () => {
  433. pageIndex+=1;
  434. //apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?"+getTime();
  435. apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?";
  436. //showStackToast(apiUrl);
  437. const ret = await query(apiUrl);
  438. if(!ret || !ret.list || !ret.list.data){
  439. alert("api访问异常");
  440. throw Error("api访问异常");
  441. }
  442. showStackToast(`成功找到【${ret.list.data.length}】个vip视频`);
  443. insertList(box, ret.list.data);
  444. });
  445. }
  446.  
  447. // 主函数:检验是否在可执行域,不是则不管
  448. async function mainFunction(){
  449. //每次执行都重置pageIndex
  450. pageIndex=1;
  451. if('/home'===window.location.pathname){
  452. //执行main相关内容
  453. showStackToast("当前位于main页面");
  454. setTimeout(async () => {
  455. await main_func();
  456. }, 2000);
  457. } else if(-1!==window.location.pathname.search("/page/vip/") || -1!==window.location.pathname.search("/page/remen/")){
  458. //执行video_list相关内容
  459. showStackToast("当前位于list页面");
  460. //修改mediaType
  461. const e=window.location.pathname.match(/(\d+)$/);
  462. if(!e || e.length < 2){
  463. alert(`没有找到type,当前url${window.location}`);
  464. throw Error(`没有找到type,当前url${window.location}`);
  465. }
  466.  
  467. if(!e[1] in mediaTypes){
  468. showStackToast("未找到vip视频标识,将忽略");
  469. throw Error("未找到vip视频标识,将忽略");
  470. }
  471.  
  472. mediaType=e[1];
  473.  
  474. setTimeout(async () => {
  475. await func_list();
  476. }, 300);
  477. }
  478. //否则什么也不执行
  479. }
  480.  
  481. setTimeout(async () => {
  482. 'use strict';
  483. //alert("测试");
  484. // console.log(encrypt);
  485. // console.log(decrypt);//decrypt用于方便调试
  486. // console.log(query);
  487. // console.log(getSignature);
  488. // console.log(addVidKeyParam2);
  489. let previousUrl = '';
  490. const observer = new MutationObserver(async function(mutations) {
  491. let nowUrl=window.location.href;
  492. if (nowUrl !== previousUrl) {
  493. console.log(`URL changed from ${previousUrl} to ${window.location.href}`);
  494. previousUrl = nowUrl;
  495. await mainFunction();
  496. }
  497. });
  498. const config = {subtree: true, childList: true};
  499. observer.observe(document, config);
  500. }, 500);