小鹅通 通用m3u8获取

获取某鹅通m3u8内容 重新拼装真实ts地址和解密真实密钥 发送给扩展

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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" });
    }
})();