猫咪av

破解vip视频免费观看

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         猫咪av
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  破解vip视频免费观看
// @author       thunder-sword
// @match        *://*/home
// @match        *://*/page/vip/*
// @match        *://*/page/remen/*
// @require      https://cdn.staticfile.net/crypto-js/4.2.0/crypto-js.min.js#sha256=dppVXeVTurw1ozOPNE3XqhYmDJPOosfbKQcHyQSE58w=
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=f2c013d5bbbb.com
// @grant        none
// @connect      mjson.szaction.cc
// ==/UserScript==

//变量作用:负责一些配置
var settings = {
    m3u8Prefix: "https://tma.qianchengwandian.top",        //setSource(config?.m3u8_host_encrypt ?? data?.source?.m3u8_host)(video.tsx)
    apiPrefix: "https://mjson.szaction.cc",   //_domain = _domain.endsWith('/') ? _domain : `${_domain}/`;(useAxios.ts)
}

//变量作用:管理当前pageIndex
var pageIndex=1;
//变量作用:管理当前mediaType
var mediaType=1;

//常量作用:页面端type和mediaType不是一一对应的,需要转换一下(/data/category/base-2.js)
const mediaTypes={
    58: "base-vip-mmtj-",
    59: "base-vip-ycgc-",
    62: "base-vip-zfyx-",
    60: "base-vip-hlaiq-",
    61: "base-vip-sjzy-",
    63: "base-vip-cydm-",
    64: "base-vip-omdp-",
    65: "base-vip-txzq-",
    150: "base-remen-xpsd-",
    151: "base-remen-djjx-",
    152: "base-remen-gccm-",
    153: "base-remen-jpgq-",
    154: "base-remen-zmcs-",
    155: "base-remen-thss-",
    156: "base-remen-spxm-",
    157: "base-remen-avjs-",
}

//常量作用:加解密所需常量
const key = CryptoJS.enc.Utf8.parse("IdTJq0HklpuI6mu8iB%OO@!vd^4K&uXW");
const iv = "$0v@krH7V2";
const mode = CryptoJS.mode.CBC;
const padding = CryptoJS.pad.Pkcs7;
const formatter = CryptoJS.format.OpenSSL;
const secretKey = "D7hGKHnWThaECaQ3ji4XyAF3MfYKJ53M";

//来自util.js
        // this.key = "SWRUSnEwSGtscHVJNm11OGlCJU9PQCF2ZF40SyZ1WFc=";
        // this.iv = "JDB2QGtySDdWMg==";
        // this.sign_key = "JkI2OG1AJXpnMzJfJXUqdkhVbEU0V2tTJjFKNiUleG1VQGZO";
        // this.suffix = 123456;
        // this.statDomain = process.env.REACT_APP_STATIC_STAT_DOMAIN;
        // this.secretKey = "D7hGKHnWThaECaQ3ji4XyAF3MfYKJ53M";

//判断第三方库是否导入成功
if('undefined'===typeof CryptoJS || undefined===CryptoJS.AES){
    showStackToast("导入第三方库crypto-js失败","red");
    throw Error("导入第三方库crypto-js失败,请尝试更换cdn或更改网络环境");
}

function addVidKeyParam2(url) {
        let secret_key = secretKey;
        let currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
        let storedTime = sessionStorage.getItem('timestamp'); // Get the stored timestamp from sessionStorage

        let time; // Declare the time variable

        // If there is a stored time and 5 minutes haven't passed, reuse the stored time
        if (storedTime && currentTime - parseInt(storedTime) < 300) {
            time = parseInt(storedTime) + 300; // Use the stored timestamp and add 300 seconds
        } else {
            // If no stored time or more than 5 minutes have passed, generate a new timestamp
            time = currentTime + 300; // Add 300 seconds to the current time
            sessionStorage.setItem('timestamp', currentTime.toString()); // Store the new timestamp in sessionStorage
        }

        // Convert the time to a hexadecimal string
        let hexTime = time.toString(16);
        console.log('Hex time:', hexTime);

        // The URI for which we're generating the key
        let uri = url;
        console.log('URI:', uri);

        // Concatenate secret_key, uri, and time (decimal) for the MD5 hash
        let paramToAppend = secret_key + uri + time; // Time in decimal
        console.log('Param to append for MD5:', paramToAppend);

        // Calculate the MD5 hash using CryptoJS
        let key = CryptoJS.MD5(paramToAppend).toString();
        console.log('Generated key (MD5):', key);

        // Return the URL with wsSecret (the MD5 hash) and wsTime (in decimal)
        return '?wsSecret=' + key + "&wsTime=" + time; // wsTime is the time in seconds (decimal)
}

function encrypt(plaintext, suffix = "123456") {
	var tmp=CryptoJS.enc.Utf8.parse(plaintext);
    let new_iv = CryptoJS.enc.Utf8.parse(iv + suffix);
	var enc=CryptoJS.AES.encrypt(tmp, key, {
		iv: new_iv,
		mode: mode,
		padding: padding,
        formatter: formatter
	});
	return enc.toString();
}

//let __data = u.decrypt(response.data.data, response.data.suffix);(useAxios.ts)
function decrypt(ciphertext, suffix = "123456") {
    let new_iv = CryptoJS.enc.Utf8.parse(iv + suffix);
	var dec=CryptoJS.AES.decrypt(ciphertext, key, {
		iv: new_iv,
		mode: mode,
		padding: padding,
        formatter: formatter
	});
	var s=dec.toString(CryptoJS.enc.Utf8);
	return JSON.parse(s);
}

function getSignature(data) {
    function objKeySort(arys) {
        let newObj = {};
        let newkey = Object.keys(arys).sort();
        for (var i = 0; i < newkey.length; i++) {
            newObj[newkey[i]] = arys[newkey[i]];
        }
        return newObj;
    }
        data = objKeySort(data);
        let pre_sign = "";
        for (let i in data) {
            pre_sign += i + "=" + data[i] + "&";
        }
        let key = '&B68m@%zg32_%u*vHUlE4WkS&1J6%%xmU@fN';
        pre_sign += key;
        return CryptoJS.MD5(pre_sign).toString();
}

//作用:生成toast,让其在toast_container中,显示在页面中上部,会永久性向页面添加一个id为ths_toast_container的div标签
function showStackToast(message, backcolor='rgb(76, 175, 80)', timeout=3000){
    //没有容器则生成容器
    let box=document.querySelector("body > div#ths_toast_container");
    if(!box){
        box=document.createElement('div');
        box.id="ths_toast_container";
        box.style.cssText = `
    position: fixed;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    right: 10px;
    width: 300px;
    height: auto;
    display: flex;
    z-index: 9999;
    flex-direction: column-reverse;`;
        document.body.appendChild(box);
    }
    //创建toast
    const toast = document.createElement('div');
    toast.innerText = message;
    toast.style.cssText = `
    padding: 10px;
    background-color: ${backcolor};
    color: rgb(255, 255, 255);
    border-radius: 10px;
    font-size: 24px;
    font-weight: bold;
    text-align: center;
    box-shadow: rgb(0 0 0 / 30%) 0px 5px 10px;
    opacity: 1;
    transition: opacity 0.3s ease-in-out 0s;
    z-index: 9999;
    margin: 5px;
  `;
    box.appendChild(toast);
    toast.style.opacity = 1;
    if(timeout > 0){
        setTimeout(() => {
            toast.style.opacity = 0;
            setTimeout(() => {
                box.removeChild(toast);
            }, 300);
        }, timeout);
    }
    return toast;
}

class NetworkError extends Error {
  constructor(message) {
    super(message);
    this.name = "NetworkError";
  }
}

//api='https://mmnew.tlxxw.cc/api/list/base';

async function query(url, obj={}){
	let data='';
	try{
		//data=encrypt(JSON.stringify(obj));
        //data=JSON.stringify(obj);
	}catch(e){
		console.log("发送包aes加密失败");
		console.log(obj);
		console.log(key);
		console.log(gzip);
		throw e;
	}
	const ret = await fetch(url, {
			method: 'GET',
			headers: {
				//'Content-Type': 'application/json'
			}})
		.then(function(response) {
			if(!response.ok) {
				throw new NetworkError(`HTTP error! status: ${response.status}`);
			}
			return response.text();
		}).catch(error => {
			if (error instanceof NetworkError) {
				console.error("Network error: ", error.message);
				throw error;
		} else {
			console.error("Other error: ", error);
			//alert("发生其他错误");
			throw error;
		}
		});
    const tmp=JSON.parse(ret);
    if(!tmp || 0!==tmp.code){
        showStackToast("解析返回包失败!","red");
        console.log(tmp);
        throw Error("解析返回包失败!");
    }
	//解密返回包
	try{
		data=decrypt(tmp.data, tmp.suffix);
	}catch(e){
		console.log("返回包aes解密失败");
		console.log(tmp);
		console.log(key);
		throw e;
	}
	return data;
}

//作用:弹出新窗口播放指定m3u8视频
function playMedia(mediaUrl, width=800, height=600) {
    /* 第三个参数规定了新窗口的大小,不加则会以新窗口的形式出现 */
    var windowHandle = window.open("", "_blank", `width=${width}, height=${height}`);
    windowHandle.document.write(`
        <html>
        <head>
            <script src="https://cdn.staticfile.net/dplayer/1.27.1/DPlayer.min.js"></script>
            <script src="https://cdn.staticfile.net/hls.js/1.5.1/hls.min.js"></script>
        </head>
        <body>
            <div id="dplayer"></div>
            <script>
                //var url = prompt("请输入要播放的视频url");
                var dp = new DPlayer({
                     container: document.getElementById('dplayer'),
                     autoplay: true,
                     theme: '#FADFA3',
                     loop: true,
                     lang: 'zh',
                     screenshot: true,
                     hotkey: true,
                     preload: 'auto',
                     video: {
                         url: "${mediaUrl}",
                         type: 'hls'
                     }
                 });
            </script>
        </body>
        </html>
    `);
}

//await query("https://utt.51jiajiao.top/data/index/home.js?1718510400000");

//插入视频条目执行函数
async function insertList(box, videoList){
    showStackToast("正在破解中...");
    //修改第一个容器内的标识
    box.firstElementChild.firstElementChild.innerText="已破解VIP视频";
    let list=box.querySelector("div:nth-child(2) > div");
    //循环添加事件
    for(let i=0; i < videoList.length && i < list.childNodes.length; i++){
        //先去除绑定
        list.childNodes[i].onclick= () => {
            if(!videoList[i].video_url){
                alert("未找到该视频地址");
                throw Error("未找到该视频地址");
            }
            let video_url=videoList[i].video_url.replaceAll('//', '/')
            let m3u8url=settings['m3u8Prefix']+video_url;
            m3u8url=m3u8url+addVidKeyParam2(video_url);
            showStackToast("视频地址获取成功,将弹窗播放");
            showStackToast(videoList[i].title);
            console.log(m3u8url);
            console.log(videoList[i]);
            playMedia(m3u8url);
        };
    }
    showStackToast("破解完成!");
}

//获取当前时间戳
function getTime(){
    var time = new Date();
    if (time.getHours() < 4){
        time.setHours(0, 0, 0, 0)
    }
    else if (time.getHours() < 8){
        time.setHours(4, 0, 0, 0)
    }
    else if (time.getHours() < 12){
        time.setHours(8, 0, 0, 0)
    }
    else if (time.getHours() < 16){
        time.setHours(12, 0, 0, 0)
    }
    else if (time.getHours() < 20){
        time.setHours(16, 0, 0, 0)
    }
    else if (time.getHours() < 24){
        time.setHours(20, 0, 0, 0)
    }
    else{
        time.setHours(24, 0, 0, 0)
    }

    let ts = time.getTime();
    return ts;
}

//main要执行的内容
async function main_func(){
    //let apiUrl=settings["apiPrefix"]+"/data/index/home.js?"+getTime();
    let apiUrl=settings["apiPrefix"]+"/data/index/home.js?";
    //showStackToast(apiUrl);
    const ret = await query(apiUrl);
    if(!ret || !ret.vip_list || !ret.vip_list.data){
        alert("api访问异常");
        throw Error("api访问异常");
    }
    showStackToast(`成功找到【${ret.vip_list.data.length}】个vip视频`);
    //查找box
    let box = document.querySelector("div.mw1100 > div:nth-child(1)");
    if(!box){
        showStackToast("未找到box!", "red");
        throw Error("未找到box!");
    }
    showStackToast("成功找到box!");
    insertList(box, ret.vip_list.data);
}

///page/vip/和/page/remen/的执行内容
async function func_list(user_id=0){
    let urlType=mediaTypes[mediaType];
    if(undefined === urlType){
        alert("脚本异常,urlType为空");
        throw Error("脚本异常,urlType为空");
    }
    //let apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?"+getTime();
    let apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?";
    //showStackToast(apiUrl);
    const ret = await query(apiUrl);
    if(!ret || !ret.list || !ret.list.data){
        alert("api访问异常");
        throw Error("api访问异常");
    }
    showStackToast(`成功找到【${ret.list.data.length}】个vip视频`);
    //查找box
    let box = document.querySelector("div.mw1100");
    if(!box){
        showStackToast("未找到box!", "red");
        throw Error("未找到box!");
    }
    showStackToast("成功找到box!");
    insertList(box, ret.list.data);
    //找到上一页、下一页按钮容器
    let pagination=document.querySelector("div.fl.align_center.justify_center.gap20.mb20");
    //清空原有按钮
    //pagination.innerHTML='';
    pagination.removeChild(pagination.lastChild);
    pagination.removeChild(pagination.firstChild);
    pagination.removeChild(pagination.childNodes[1]);
    //找到上一页按钮
    var prePage=pagination.firstChild;
    prePage.textContent="上一页";
    prePage.setAttribute("style", "padding: 5px .6rem;border: 1px solid #bebebd;text-align: center;border-radius: 7px;cursor: pointer;background: #fff;");
    //pagination.appendChild(prePage);
    //绑定事件
    prePage.addEventListener("click", async () => {
        if(pageIndex<=1){
            showStackToast(`当前page为${pageIndex},不能再向上一页`);
            throw Error(`当前page为${pageIndex},不能再向上一页`);
        }
        pageIndex-=1;
        //apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?"+getTime();
        apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?";
        //showStackToast(apiUrl);
        const ret = await query(apiUrl);
        if(!ret || !ret.list || !ret.list.data){
            alert("api访问异常");
            throw Error("api访问异常");
        }
        showStackToast(`成功找到【${ret.list.data.length}】个vip视频`);
        insertList(box, ret.list.data);
    });
    //找到下一页按钮
    var nextPage=pagination.lastChild;
    nextPage.textContent="下一页";
    nextPage.setAttribute("style", "padding: 5px .6rem;border: 1px solid #bebebd;text-align: center;border-radius: 7px;cursor: pointer;background: #fff;");
    //pagination.appendChild(nextPage);
    //绑定事件
    nextPage.addEventListener("click", async () => {
        pageIndex+=1;
        //apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?"+getTime();
        apiUrl=settings["apiPrefix"]+"/data/list/"+urlType+pageIndex+".js?";
        //showStackToast(apiUrl);
        const ret = await query(apiUrl);
        if(!ret || !ret.list || !ret.list.data){
            alert("api访问异常");
            throw Error("api访问异常");
        }
        showStackToast(`成功找到【${ret.list.data.length}】个vip视频`);
        insertList(box, ret.list.data);
    });
}

// 主函数:检验是否在可执行域,不是则不管
async function mainFunction(){
    //每次执行都重置pageIndex
    pageIndex=1;
    if('/home'===window.location.pathname){
        //执行main相关内容
        showStackToast("当前位于main页面");
        setTimeout(async () => {
            await main_func();
        }, 2000);
    } else if(-1!==window.location.pathname.search("/page/vip/") || -1!==window.location.pathname.search("/page/remen/")){
        //执行video_list相关内容
        showStackToast("当前位于list页面");
        //修改mediaType
        const e=window.location.pathname.match(/(\d+)$/);
        if(!e || e.length < 2){
            alert(`没有找到type,当前url为${window.location}`);
            throw Error(`没有找到type,当前url为${window.location}`);
        }

        if(!e[1] in mediaTypes){
            showStackToast("未找到vip视频标识,将忽略");
            throw Error("未找到vip视频标识,将忽略");
        }

        mediaType=e[1];

        setTimeout(async () => {
            await func_list();
        }, 300);
    }
    //否则什么也不执行
}

setTimeout(async () => {
    'use strict';
    //alert("测试");
    // console.log(encrypt);
    // console.log(decrypt);//decrypt用于方便调试
    // console.log(query);
    // console.log(getSignature);
    // console.log(addVidKeyParam2);
    let previousUrl = '';
    const observer = new MutationObserver(async function(mutations) {
        let nowUrl=window.location.href;
        if (nowUrl !== previousUrl) {
            console.log(`URL changed from ${previousUrl} to ${window.location.href}`);
            previousUrl = nowUrl;
            await mainFunction();
        }
    });
    const config = {subtree: true, childList: true};
    observer.observe(document, config);
}, 500);