Greasy Fork is available in English.
获取某鹅通m3u8内容 重新拼装真实ts地址和解密真实密钥 发送给扩展
// ==UserScript== // @name 小鹅通 通用m3u8获取 // @namespace https://94cat.com/ // @version 0.10 // @description 获取某鹅通m3u8内容 重新拼装真实ts地址和解密真实密钥 发送给扩展 // @author mz // @match https://*/* // @match http://*/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant none // @run-at document-start // @license GPL v3 // ==/UserScript== (function () { 'use strict'; //let URLext = {}; const URLext = new Map(); const _indexOf = String.prototype.indexOf; String.prototype.indexOf = function () { if (arguments[0] == "#EXTM3U") { // 先尝试不传入 URLext 有可能m3u8本身包含远程链接 不需要URLext parseSendM3U8(this); URLext.forEach((item) => { parseSendM3U8(this, item); }); } return _indexOf.apply(this, arguments); } String.prototype.indexOf.toString = function () { return _indexOf.toString(); } const _JSONparse = JSON.parse; JSON.parse = function () { let data = _JSONparse.apply(this, arguments); findMedia(data); return data; } JSON.parse.toString = function () { return _JSONparse.toString(); } async function findMedia(data, raw = undefined, depth = 0) { for (let key in data) { if (typeof data[key] == "object") { if (depth > 25) { continue; } if (!raw) { raw = data; } findMedia(data[key], raw, ++depth); continue; } if (typeof data[key] == "string" && key == "video_urls" && data[key].slice(-4) == "__ba") { let base64 = data[key].replace("__ba", ""); base64 = base64.replaceAll("@", "1").replaceAll("#", "2").replaceAll("$", "3").replaceAll("%", "4"); let json = _JSONparse(atob(base64)); if (!json) { return } //console.error(json); for (let obj of json) { fetch(obj.url).then(response => response.text()) .then(m3u8 => { const lines = m3u8.split('\n'); let keyFlag = false; for (let i = 0; i < lines.length; i++) { if (lines[i] == '#EXT-X-ENDLIST') { break; } if (!keyFlag && lines[i].includes("#EXT-X-KEY:METHOD=AES-128,URI=")) { const match = lines[i].match(/URI="([^"]*)"/); if (match && match[1]) { keyFlag = true; if (window.__user_id) { getKey(match[1] + "&uid=" + window.__user_id, window.__user_id); } else if (document.cookie) { for (let cookie of document.cookie.split(';')) { cookie = cookie.trim(); if (cookie.substring(0, 10) == "userInfo={") { cookie = cookie.slice(9); cookie = isJSON(cookie); cookie && cookie.user_id && getKey(match[1] + "&uid=" + cookie.user_id, cookie.user_id); break; } } } } continue; } if (lines[i][0] != "#") { lines[i] = `${obj.ext.host}/${obj.ext.path}/${lines[i]}&${obj.ext.param}`; } } m3u8 = lines.join('\n'); let url = URL.createObjectURL(new Blob([new TextEncoder("utf-8").encode(m3u8)])); window.postMessage({ action: "catCatchAddMedia", url: url, href: location.href, ext: "m3u8" }); }); } } else if (data.confusion_m3u8 && data.ext) { //console.error(data.ext); URLext.set(data.ext.host, data.ext); //URLext = data.ext; } else if (data.app_id && data.host && data.param && data.path) { URLext.set(data.host, data); //URLext = data; } } } function uid2byte(uid) { const byteArray = new Array; for (let i = 0; i < uid.length; i++) { let temp = uid.charCodeAt(i); if (temp >= 65536 && temp <= 1114111) { byteArray.push(temp >> 18 & 7 | 240); byteArray.push(temp >> 12 & 63 | 128); byteArray.push(temp >> 6 & 63 | 128); byteArray.push(63 & temp | 128); } else if (temp >= 2048 && temp <= 65535) { byteArray.push(temp >> 12 & 15 | 224); byteArray.push(temp >> 6 & 63 | 128); byteArray.push(63 & temp | 128); } else if (temp >= 128 && temp <= 2047) { byteArray.push(temp >> 6 & 31 | 192); byteArray.push(63 & temp | 128); } else { byteArray.push(255 & temp); } } return byteArray; } function getKey(url, userId) { fetch(url).then(response => response.arrayBuffer()) .then(buffer => { let newKey = []; buffer = new Uint8Array(buffer); const uidByte = uid2byte(userId); for (let i in buffer) { newKey.push(buffer[i] ^ uidByte[i]); } // console.error(newKey); window.postMessage({ action: "catCatchAddKey", key: newKey, href: location.href }); }); } const _xhrOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function () { this.addEventListener("readystatechange", function (event) { const response = this.currentTarget ? this.currentTarget.response : this.response; const isJson = isJSON(response); isJson && findMedia(isJson); }); _xhrOpen.apply(this, arguments); } XMLHttpRequest.prototype.open.toString = function () { return _xhrOpen.toString(); } function isJSON(str) { if (typeof str == "object") { return str; } if (typeof str == "string") { try { return _JSONparse(str); } catch (e) { return false; } } return false; } function parseSendM3U8(m3u8, URLext = undefined) { const lines = m3u8.split('\n'); m3u8 = '' for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith("#EXT-X-KEY") && lines[i].includes('URI=""')) { continue; } if (lines[i][0] != "#") { if (!URLext && !lines[i].startsWith("http")) { return false; } if (lines[i].startsWith("http")) { m3u8 += lines[i] + "\n"; } else { m3u8 += `${URLext.host}/${URLext.path}/${lines[i]}&${URLext.param}\n`; } continue; } m3u8 += lines[i] + "\n"; if (lines[i] == "#EXT-X-ENDLIST") { break; } } // console.error(m3u8); const url = URL.createObjectURL(new Blob([new TextEncoder("utf-8").encode(m3u8)])); window.postMessage({ action: "catCatchAddMedia", url: url, href: location.href, ext: "m3u8" }); } })();