AnimeStars фикс кнопок в браузере Opera GX

Убирает кнопку "окно в окне" (PiP) в браузере Opera GX у фоновых видео профиля на AnimeStars и его зеркалах. В других браузерах просто защищает видео, оставляя его как фон.

目前為 2025-09-16 提交的版本,檢視 最新版本

// ==UserScript==
// @name         AnimeStars фикс кнопок в браузере Opera GX
// @namespace    https://animestars.org/
// @version      1.0
// @author       Sandr
// @description  Убирает кнопку "окно в окне" (PiP) в браузере Opera GX у фоновых видео профиля на AnimeStars и его зеркалах. В других браузерах просто защищает видео, оставляя его как фон.
// @match        *://animestars.org/*
// @match        *://asstars.tv/*
// @match        *://astars.club/*
// @match        *://as1.astars.club/*
// @match        *://as1.asstars.tv/*
// @match        *://as2.asstars.tv/*
// @match        *://asstars.club/*
// @match        *://asstars.online/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

// ==== Универсальная защита фоновых видео профиля от Opera GX ====
function protectAllProfileBgVideos() {
    const SELECTOR = "#profilebg";
    const CANDIDATES_SELECTOR = "#detach-button-host, #video-detach-button, #skip-button, #send-by-qrcode-button, #lucid-mode-button, .button-container, .custom-button, .rgx-button-wrapper";

    function applyToVideo(video) {
        if (!video || video.dataset._canvas_applied) return;
        video.dataset._canvas_applied = "1";

        const poster = video.closest(".usn__poster") || video.parentElement;
        if (!poster) return;

        const src = video.querySelector("source")?.src || video.src;
        if (!src) return;

        // Скрытое видео (вне DOM)
        const hiddenVideo = document.createElement("video");
        hiddenVideo.src = src;
        hiddenVideo.muted = true;
        hiddenVideo.loop = true;
        hiddenVideo.autoplay = true;
        hiddenVideo.playsInline = true;
        hiddenVideo.crossOrigin = "anonymous";
        hiddenVideo.play().catch(() => {});

        // Canvas
        const canvas = document.createElement("canvas");
        const ctx = canvas.getContext("2d");
        Object.assign(canvas.style, {
            position: "absolute",
            top: 0,
            left: 0,
            width: "100%",
            height: "100%",
            zIndex: 0,
            pointerEvents: "none"
        });

        if (getComputedStyle(poster).position === "static") {
            poster.style.position = "relative";
        }
        poster.insertBefore(canvas, poster.firstChild);

        // Убираем оригинальное видео
        video.remove();

        // Resize canvas
        function resizeCanvas() {
            const rect = poster.getBoundingClientRect();
            const dpr = window.devicePixelRatio || 1;
            canvas.width = rect.width * dpr;
            canvas.height = rect.height * dpr;
            ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
        }
        resizeCanvas();
        window.addEventListener("resize", resizeCanvas);

        // Рендер с object-fit: cover
        function render() {
            if (hiddenVideo.readyState >= 2) {
                const sw = hiddenVideo.videoWidth;
                const sh = hiddenVideo.videoHeight;
                const dw = canvas.width;
                const dh = canvas.height;

                const srcRatio = sw / sh;
                const dstRatio = dw / dh;

                let sx = 0, sy = 0, sWidth = sw, sHeight = sh;

                if (srcRatio > dstRatio) {
                    // Видео шире → обрезаем по бокам
                    sWidth = sh * dstRatio;
                    sx = (sw - sWidth) / 2;
                } else {
                    // Видео выше → обрезаем сверху/снизу
                    sHeight = sw / dstRatio;
                    sy = (sh - sHeight) / 2;
                }

                ctx.clearRect(0, 0, dw, dh);
                ctx.drawImage(hiddenVideo, sx, sy, sWidth, sHeight, 0, 0, dw, dh);
            }
            requestAnimationFrame(render);
        }
        render();

        // MutationObserver для удаления кнопок Opera GX
        const overlayObserver = new MutationObserver(mutations => {
            mutations.forEach(m => {
                m.addedNodes.forEach(node => {
                    if (!(node instanceof Element)) return;
                    if (node.matches(CANDIDATES_SELECTOR)) {
                        node.remove();
                    }
                    const found = node.querySelector && node.querySelector(CANDIDATES_SELECTOR);
                    if (found) found.remove();
                });
            });
        });
        overlayObserver.observe(document.body, { childList: true, subtree: true });
    }

    // Применяем сразу
    document.querySelectorAll(SELECTOR).forEach(applyToVideo);

    // Следим за новыми
    const observer = new MutationObserver(mutations => {
        mutations.forEach(m => {
            m.addedNodes.forEach(node => {
                if (!(node instanceof Element)) return;
                if (node.matches && node.matches(SELECTOR)) {
                    applyToVideo(node);
                }
                const found = node.querySelector && node.querySelector(SELECTOR);
                if (found) applyToVideo(found);
            });
        });
    });
    observer.observe(document.body, { childList: true, subtree: true });
}

// Запускаем
protectAllProfileBgVideos();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址