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

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

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

// ==UserScript==
// @name         AnimeStars фикс кнопок в браузере Opera GX
// @namespace    https://animestars.org/
// @version      1.2
// @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
// Resize canvas под родительский блок
function resizeCanvas() {
    const rect = poster.getBoundingClientRect();
    const dpr = window.devicePixelRatio || 1;
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    canvas.style.width = rect.width + "px";
    canvas.style.height = rect.height + "px";
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}

// Следим за изменением размеров контейнера
const resizeObserver = new ResizeObserver(resizeCanvas);
resizeObserver.observe(poster);

        // Рендер с 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 scale = Math.max(dw / sw, dh / sh);

        const scaledWidth = sw * scale;
        const scaledHeight = sh * scale;

        // По горизонтали оставим центрирование
        const dx = (dw - scaledWidth) / 2;
        // По вертикали — всегда от верхней границы
        const dy = 0;

        ctx.clearRect(0, 0, dw, dh);
        ctx.drawImage(hiddenVideo, dx, dy, scaledWidth, scaledHeight);
    }
    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或关注我们的公众号极客氢云获取最新地址