A2P

Anime2Potplayer,用Potplayer打开浏览器播放的动漫,这样就可以使用SVP4补帧啦!

目前为 2025-05-04 提交的版本。查看 最新版本

// ==UserScript==
// @name         A2P
// @namespace    http://tampermonkey.net/
// @version      1.1.1
// @description  Anime2Potplayer,用Potplayer打开浏览器播放的动漫,这样就可以使用SVP4补帧啦!
// @author       MakotoArai(https://github.com/MakotoArai-CN)
// @supportURL   https://blog.ciy.cool
// @license      GPL-v3
// @icon          https://cravatar.cn/avatar/1e84fce3269537e4aa7473602516bf6d?s=256
// @match        *anich.emmmm.eu.org/*
// @match        *.mutedm.com/*
// @match        *.iyinghua.com/*
// @match        *.5dm.link/*
// @match        *.dmd77.com/*
// @match        *.agefans.la/*
// @match        *43.240.156.118:8443/*
// @match        *tinaacg.net/*
// @match        *susudyy.com/*
// @match        *.5o5k.com/*
// @match        *.k6dm.com/*
// @match        *.233dm.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_notification
// @grant        GM_addStyle
// ==/UserScript==

'use strict';

const m3u8Urls = new Set();
let resultsShown = false;

function showResults(title, text, timeout) {
    // 发送桌面通知
    if (typeof GM_notification !== 'undefined') {
        GM_notification({
            title: title,
            text: text,
            timeout: timeout
        });
    }
}

// 拦截所有网络请求
if (typeof GM_xmlhttpRequest !== 'undefined') {
    const originalXHR = unsafeWindow.XMLHttpRequest;
    unsafeWindow.XMLHttpRequest = function () {
        const xhr = new originalXHR();
        const originalOpen = xhr.open;

        xhr.open = function (method, url) {
            if (url && /\.m3u8($|\?)/i.test(url)) {
                m3u8Urls.add(url);
                showResults("M3U8嗅探到:", url, 3000);
                console.log('拦截到M3U8 (XHR):', url);
                GM_setValue("Reallyurl", url);
            }
            return originalOpen.apply(this, arguments);
        };

        return xhr;
    };
}

// 监听动态创建的video元素
new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        mutation.addedNodes.forEach(function (node) {
            if (node.nodeName === 'VIDEO') checkVideoElement(node);
            if (node.querySelectorAll) node.querySelectorAll('video').forEach(checkVideoElement);
        });
    });
}).observe(document, { childList: true, subtree: true });

/**
 * .m3u8嗅探
 * @param {*} video 
 */
function checkVideoElement(video) {
    if (video.src && /\.m3u8($|\?)/i.test(video.src)) {
        m3u8Urls.add(video.src);
        // console.log('发现M3U8 video元素:', video.src);
        // showResults();
    }
}
// 初始检查
document.querySelectorAll('video').forEach(checkVideoElement);
// console.log('M3U8综合嗅探已激活');

window.onload = function () {
    console.info("%cA2P%c%s", "color:red;font-size:40px;font-weight:bold;", "color:black;font-size:16px;font-weight:normal", "\n" + GM_info.script.version);
    // 定时器用于动态嗅探视频链接
    const videoTimer = setInterval(findVideoUrl, 1000);

    // 域名包含 anich.emmmm.eu.org 则启用下面的逻辑
    if (window.location.href.includes("anich.emmmm.eu.org")) {
        // 定时器检测url是否改变,如果改变则重新调用findVideoUrl
        setInterval(function () {
            if (GM_getValue("url") !== window.location.href) {
                // 存入url方便对比
                GM_setValue("url", window.location.href);
                findVideoUrl();
            }
        }, 1500);
    }

    function Launch(App, url) {
        try {
            window.location.href = `${App}://${url}`;
            console.log("Launch to:"+url);
            
        } catch (error) {
            alert(`请先安装 ${App}`);
        }
    }

    function findVideoUrl() {
        const videoElement = document.querySelector("video");
        if (videoElement && videoElement.src) {
            clearInterval(videoTimer);
            preparePotplayerInteraction(videoElement, GM_getValue("check") ?? false);
        }
    }

    function preparePotplayerInteraction(videoElement, check = true) {
        let videoUrl = videoElement.src;
        console.log(`检测到视频链接: ${videoUrl}`);
        if (videoElement.src.includes("blob:")) videoUrl = GM_getValue("Reallyurl");;
        creatBtn(videoElement);
        if (check) {
            Launch("potplayer", videoUrl)
            // 检测是否播放,如果正在播放则暂停网页的播放
            var pause_Flag = 0;
            const checkTimer = setInterval(() => {
                const isPlaying = !videoElement.paused && !videoElement.ended && videoElement.readyState > 2;
                // console.log(isPlaying ? "正在播放" : "已暂停或结束");
                if (isPlaying) videoElement.pause();
                if (!isPlaying || pause_Flag > 100) clearInterval(checkTimer);
                pause_Flag++;
            }, 1500);
        };

    }

    function creatBtn(videoElement) {
        // 插入自定义CDN
        document.head.insertAdjacentHTML("beforeend", `
            <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
        `);
        // 右键菜单
        var menu = document.createElement("div");
        document.head.insertAdjacentHTML("beforeend", `
            <style>
                 a {text-decoration: none;}
                div.usercm{background-repeat:no-repeat;background-position:center center;background-size:cover;background-color:#fff;font-size:13px!important;width:160px;-moz-box-shadow:1px 1px 3px rgba
                (0,0,0,.3);box-shadow:0px 0px 15px #333;position:absolute;display:none;z-index:10000;opacity:0.9; border-radius: 8px;}
                div.usercm ul{list-style-type:none;list-style-position:outside;margin:0px;padding:0px;display:block}
                div.usercm ul li{margin:0px;padding:0px;line-height:35px;}
                div.usercm ul li a{color:#666;padding:0 15px;display:block}
                /* div.usercm ul li a:hover{color:#fff;background:rgba(170,222,18,0.88)} */
                div.usercm ul li a:hover{color:#fff;background:rgba(15, 120, 233, 0.88)} /* 蓝色 */
                div.usercm ul li a i{margin-right:10px}
                a.disabled{color:#c8c8c8!important;cursor:not-allowed}
                a.disabled:hover{background-color:rgba(255,11,11,0)!important}
                div.usercm{background:#fff !important;}
            </style>
        )`);
        menu.innerHTML = `
            <div class="usercm" style="left: 199px; top: 5px; display: none;">
                <ul>
                    <li style="border-bottom:1px solid gray"><a href="javascript:window.location.reload();"><i class="fa fa-refresh fa-fw"></i><span>重载网页</span></a></li>
                    <li><a href="javascript:void(0);" class="potplayer"><i class="fas fa-external-link-alt"></i><span>Potplayer(X)</span></a></li>
                    <li><a href="javascript:void(0);" class="aa2p"><i class="fas fa-robot"></i><span>自动跳转</span></a></li>
                    <li><a href="https://blog.ciy.cool"><i class="fas fa-blog"></i><span>关于我</span></a></li>
                </ul>
            </div>
            `;
        document.body.appendChild(menu);

        function aa2pFun() {
            const check = GM_getValue("check") ?? false;
            if (check) {
                GM_setValue("check", false);
                aa2p.innerHTML = `<i class="fas fa-toggle-off"></i><span>开启自动跳转</span>`;
            } else {
                GM_setValue("check", true);
                aa2p.innerHTML = `<i class="fas fa-toggle-on"></i><span>关闭自动跳转</span>`;
            }
        }



        // 自定义鼠标右键菜单行为
        (function () {
            let mouseX = 0;
            let mouseY = 0;
            let windowWidth = 0;
            let windowHeight = 0;

            // 获取元素
            const menu = document.querySelector('.usercm');

            // 鼠标移动事件
            window.addEventListener('mousemove', function (e) {
                windowWidth = window.innerWidth;
                windowHeight = window.innerHeight;
                mouseX = e.clientX;
                mouseY = e.clientY;

                // 设置菜单位置
                let left = e.pageX;
                let top = e.pageY;

                if (mouseX + menu.offsetWidth >= windowWidth) left = left - menu.offsetWidth - 5;
                if (mouseY + menu.offsetHeight >= windowHeight) top = top - menu.offsetHeight - 5;

                // 绑定右键点击事件
                document.documentElement.addEventListener('contextmenu', function (event) {
                    if (event.button === 2) { // 右键点击
                        event.preventDefault();
                        menu.style.left = `${left}px`;
                        menu.style.top = `${top}px`;
                        menu.style.display = 'block';
                    }
                });

                // 点击隐藏菜单
                document.documentElement.addEventListener('click', function () {
                    menu.style.display = 'none';
                });
            });

            // 禁用默认右键菜单
            window.oncontextmenu = function (e) {
                e.preventDefault();
                return false;
            };

            // 判断是否是移动端
            const userAgent = navigator.userAgent;
            const mobileKeywords = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod'];
            let isMobile = false;

            for (let keyword of mobileKeywords) {
                if (userAgent.indexOf(keyword) > -1) {
                    isMobile = true;
                    break;
                }
            }

        })();
        const potplayer = document.querySelector(".potplayer");
        const aa2p = document.querySelector(".aa2p");
        let videoUrl = videoElement.src;
        if (videoElement.src.includes("blob:")) videoUrl = GM_getValue("Reallyurl");;
        potplayer.addEventListener("click", function () {
            Launch("potplayer", videoUrl);
            videoElement.pause();
        })

        document.onkeydown = function (e) {
            const keyNum = window.event ? e.keyCode : e.which;
            if (e.altKey && Number.isInteger(keyNum)) {
                switch (keyNum) {
                    case 88://  X 键--> potplayer
                        console.log("potplayer://" + videoUrl);
                        Launch("potplayer", videoUrl)
                        videoElement.pause();
                        break;
                    case 90://  Z 键--> 自动跳转
                        console.log("%cAuto jump %c%s","",GM_getValue("check")?"color:green;font-weight:bold;":"color:red;font-weight:bold;", + GM_getValue("check")?"Turn on":"Turn off");
                        aa2pFun();
                        break;
                }
            }
        };

        aa2p.innerHTML = `<i class="fas fa-toggle-${GM_getValue("check") ? "on" : "off"}"></i><span>${GM_getValue("check") ? "关闭自动跳转" : "开启自动跳转"}</span>`;
        aa2p.addEventListener("click", function () { aa2pFun(); });
    }
}