Universal Video Split

Универсальный сплит по времени для VK, Rutube, YouTube (v1.2). Панель управления, статистика, оверлей, звук, таймер. Исправлена ошибка TrustedHTML. Добавлен автоплей при старте сплита.

// ==UserScript==
// @name         Universal Video Split
// @namespace    http://tampermonkey.net/
// @version      1.2
// @author       Gemini (Combined by AI)
// @match        *://vk.com/video_ext.php*
// @match        *://vkvideo.ru/video_ext.php*
// @match        *://rutube.ru/video/*
// @match        *://www.youtube.com/watch*
// @grant        GM_addStyle
// @grant        GM_log
// @description  Универсальный сплит по времени для VK, Rutube, YouTube (v1.2). Панель управления, статистика, оверлей, звук, таймер. Исправлена ошибка TrustedHTML. Добавлен автоплей при старте сплита.
// ==/UserScript==

(function() {
    'use strict';

    // --- КОНФИГУРАЦИЯ (Общая) ---
    let splitMinutes = null;
    let totalVideoMinutes = null;
    const extendCost = 300;
    const splitSoundUrl = 'https://github.com/lardan099/donat/raw/refs/heads/main/alert_orig.mp3';
    const overlayGifUrl = 'https://i.imgur.com/SS5Nfff.gif';
    const localStorageVolumeKey = 'universalSplitAlertVolume';
    const localStorageTimerKey = 'universalSplitOverlayTimer';
    const defaultOverlayTimerDuration = 360;

    // --- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ СОСТОЯНИЯ ---
    let currentPlatform = 'unknown';
    let platformConfig = null;
    let video = null;
    let overlay = null;
    let splitTriggered = false;
    let audioPlayer = null;
    let splitCheckIntervalId = null;
    let setupIntervalId = null;
    let panelAdded = false;
    let panelElement = null;
    let controlsElement = null; // VK only
    let visibilityCheckIntervalId = null; // VK only
    let navigationObserver = null;
    let lastUrl = location.href;

    let overlayTimerDuration = parseInt(localStorage.getItem(localStorageTimerKey), 10);
    if (isNaN(overlayTimerDuration) || overlayTimerDuration < 0) {
        overlayTimerDuration = defaultOverlayTimerDuration;
    }
    let overlayTimerIntervalId = null;
    let overlayCountdownRemaining = overlayTimerDuration;

    // --- КОНФИГУРАЦИЯ ПЛАТФОРМ ---
    const platformConfigs = {
        vk: {
            idPrefix: 'vk',
            controlPanelSelector: '#split-control-panel-vk',
            videoSelector: 'video',
            insertionSelector: '.videoplayer_title',
            insertionMethod: 'afterend',
            controlsElementSelector: '.videoplayer_controls',
            needsVisibilityCheck: true,
            styles: `
                #split-control-panel-vk { background: #f0f2f5; border: 1px solid #dce1e6; color: #333; display: none; opacity: 0; transition: opacity 0.2s ease-in-out; position: relative; z-index: 10; }
                #split-control-panel-vk.visible { display: flex; opacity: 1; }
                #split-control-panel-vk label { color: #656565; } #split-control-panel-vk label i { color: #828282; }
                #split-control-panel-vk input[type="number"] { background: #fff; color: #000; border: 1px solid #c5d0db; }
                #split-control-panel-vk input[type="number"]:focus { border-color: #aebdcb; }
                #split-control-panel-vk button { background: #e5ebf1; color: #333; border: 1px solid #dce1e6; }
                #split-control-panel-vk button:hover { background: #dae2ea; } #split-control-panel-vk button:active { background: #ccd5e0; border-color: #c5d0db; }
                #split-control-panel-vk .split-input-group button { background: #f0f2f5; border: 1px solid #dce1e6; } #split-control-panel-vk .split-input-group button:hover { background: #e5ebf1; }
                #split-control-panel-vk .set-split-button { background: #5181b8; color: #fff; border: none; } #split-control-panel-vk .set-split-button:hover { background: #4a76a8; }
                #split-control-panel-vk .set-split-button.active { background: #6a9e42; } #split-control-panel-vk .set-split-button.active:hover { background: #5c8c38; }
                #split-control-panel-vk .split-volume-control label { color: #656565; }
                #split-control-panel-vk .split-volume-control input[type="range"] { background: #dae2ea; }
                #split-control-panel-vk .split-volume-control input[type="range"]::-webkit-slider-thumb { background: #5181b8; } #split-control-panel-vk .split-volume-control input[type="range"]::-moz-range-thumb { background: #5181b8; }
                #split-control-panel-vk .split-stats { color: #333; }
            `
        },
        rutube: {
            idPrefix: 'rutube',
            controlPanelSelector: '#split-control-panel-rutube',
            videoSelector: 'video',
            insertionSelector: '.video-pageinfo-container-module__pageInfoContainer',
            insertionMethod: 'prepend',
            needsVisibilityCheck: false,
            styles: `
                #split-control-panel-rutube { background: #222; border: 1px solid #444; color: #eee; }
                #split-control-panel-rutube label { color: #aaa; } #split-control-panel-rutube label i { color: #888; }
                #split-control-panel-rutube input[type="number"] { background: #333; color: #eee; border: 1px solid #555; }
                #split-control-panel-rutube button { background: #444; color: #eee; border: none; } #split-control-panel-rutube button:hover { background: #555; }
                #split-control-panel-rutube .split-input-group button { background: #333; border: 1px solid #555; } #split-control-panel-rutube .split-input-group button:hover { background: #444; }
                #split-control-panel-rutube .set-split-button { background: #007bff; color: white; } #split-control-panel-rutube .set-split-button:hover { background: #0056b3; }
                #split-control-panel-rutube .set-split-button.active { background: #28a745; } #split-control-panel-rutube .set-split-button.active:hover { background: #1e7e34; }
                #split-control-panel-rutube .split-volume-control label { color: #aaa; }
                #split-control-panel-rutube .split-volume-control input[type="range"] { background: #444; }
                #split-control-panel-rutube .split-volume-control input[type="range"]::-webkit-slider-thumb { background: #007bff; } #split-control-panel-rutube .split-volume-control input[type="range"]::-moz-range-thumb { background: #007bff; }
                #split-control-panel-rutube .split-stats { color: #eee; }
            `
        },
        youtube: {
            idPrefix: 'youtube',
            controlPanelSelector: '#split-control-panel-youtube',
            videoSelector: 'video',
            insertionSelector: 'ytd-watch-flexy #primary',
            insertionMethod: 'prepend',
            needsVisibilityCheck: false,
            styles: `
                #split-control-panel-youtube { background: var(--yt-spec-badge-chip-background); border: 1px solid var(--yt-spec-border-div); color: var(--yt-spec-text-primary); max-width: var(--ytd-watch-flexy-width); }
                ytd-watch-flexy:not([theater]) #primary #split-control-panel-youtube { margin-left: auto; margin-right: auto; }
                ytd-watch-flexy[theater] #primary #split-control-panel-youtube { max-width: 100%; }
                #split-control-panel-youtube label { color: var(--yt-spec-text-secondary); } #split-control-panel-youtube label i { color: var(--yt-spec-text-disabled); }
                #split-control-panel-youtube input[type="number"] { background: var(--yt-spec-filled-button-background); color: var(--yt-spec-text-primary); border: 1px solid var(--yt-spec-action-simulate-border); }
                #split-control-panel-youtube button { background: var(--yt-spec-grey-1); color: var(--yt-spec-text-primary); border: none; } #split-control-panel-youtube button:hover { background: var(--yt-spec-grey-2); }
                #split-control-panel-youtube .split-input-group button { background: var(--yt-spec-filled-button-background); border: 1px solid var(--yt-spec-action-simulate-border); } #split-control-panel-youtube .split-input-group button:hover { background: var(--yt-spec-grey-2); }
                #split-control-panel-youtube .set-split-button { background: var(--yt-spec-brand-suggested-action); color: var(--yt-spec-text-reverse); } #split-control-panel-youtube .set-split-button:hover { background: var(--yt-spec-brand-suggested-action-hover); }
                #split-control-panel-youtube .set-split-button.active { background: var(--yt-spec-call-to-action); } #split-control-panel-youtube .set-split-button.active:hover { background: var(--yt-spec-call-to-action-hover); }
                #split-control-panel-youtube .split-volume-control label { color: var(--yt-spec-text-secondary); }
                #split-control-panel-youtube .split-volume-control input[type="range"] { background: var(--yt-spec-grey-1); }
                #split-control-panel-youtube .split-volume-control input[type="range"]::-webkit-slider-thumb { background: var(--yt-spec-brand-button-background); } #split-control-panel-youtube .split-volume-control input[type="range"]::-moz-range-thumb { background: var(--yt-spec-brand-button-background); }
                #split-control-panel-youtube .split-stats { color: var(--yt-spec-text-primary); }
            `
        }
    };

    // --- ОБЩИЕ СТИЛИ (Панель + Оверлей) ---
    function injectGlobalStyles() {
        let platformStyles = platformConfig ? platformConfig.styles : '';

        GM_addStyle(`
            /* --- Общие стили панели управления --- */
            .split-control-panel-universal { margin-top: 10px; margin-bottom: 15px; padding: 10px 15px; border-radius: 8px; display: flex; flex-wrap: wrap; align-items: center; gap: 10px 15px; font-family: -apple-system, BlinkMacSystemFont, "Roboto", "Helvetica Neue", Geneva, "Noto Sans Armenian", "Noto Sans Bengali", "Noto Sans Cherokee", "Noto Sans Devanagari", "Noto Sans Ethiopic", "Noto Sans Georgian", "Noto Sans Hebrew", "Noto Sans Kannada", "Noto Sans Khmer", "Noto Sans Lao", "Noto Sans Osmanya", "Noto Sans Tamil", "Noto Sans Telugu", "Noto Sans Thai", sans-serif,arial,Tahoma,verdana; font-size: 13px; width: 100%; box-sizing: border-box; line-height: 1.4; }
            .split-control-panel-universal label { font-weight: 500; flex-shrink: 0; }
            .split-control-panel-universal label i { font-style: normal; font-size: 11px; display: block; }
            .split-input-group { display: flex; align-items: center; gap: 4px; }
            .split-control-panel-universal input[type="number"] { width: 55px; padding: 6px 8px; border-radius: 4px; text-align: center; font-size: 14px; -moz-appearance: textfield; }
            .split-control-panel-universal input[type="number"]::-webkit-outer-spin-button, .split-control-panel-universal input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
            .split-control-panel-universal button { padding: 6px 12px; font-size: 13px; cursor: pointer; border-radius: 4px; transition: background 0.15s ease-in-out; font-weight: 500; flex-shrink: 0; line-height: normal; }
            .split-input-group button { padding: 6px 8px; }
            .set-split-button { order: -1; margin-right: auto; }
            .split-volume-control { display: flex; align-items: center; gap: 5px; margin-left: auto; }
            .split-volume-control label { flex-shrink: 0; }
            .split-volume-control input[type="range"] { flex-grow: 1; min-width: 70px; -webkit-appearance: none; appearance: none; height: 6px; outline: none; opacity: 0.8; transition: opacity .2s; border-radius: 3px; cursor: pointer; }
            .split-volume-control input[type="range"]:hover { opacity: 1; }
            .split-volume-control input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; cursor: pointer; border-radius: 50%; }
            .split-volume-control input[type="range"]::-moz-range-thumb { width: 12px; height: 12px; cursor: pointer; border-radius: 50%; border: none; }
            .split-stats { font-size: 14px; font-weight: 500; white-space: nowrap; }

             /* --- Общие стили оверлея --- */
            .split-overlay-universal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.95); color: white; display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 999999 !important; font-family: -apple-system, BlinkMacSystemFont, "Roboto", "Helvetica Neue", Geneva, "Noto Sans Armenian", "Noto Sans Bengali", "Noto Sans Cherokee", "Noto Sans Devanagari", "Noto Sans Ethiopic", "Noto Sans Georgian", "Noto Sans Hebrew", "Noto Sans Kannada", "Noto Sans Khmer", "Noto Sans Lao", "Noto Sans Osmanya", "Noto Sans Tamil", "Noto Sans Telugu", "Noto Sans Thai", sans-serif,arial,Tahoma,verdana; text-align: center; padding: 20px; box-sizing: border-box; }
            .split-overlay-universal .warning-message { font-size: clamp(24px, 4vw, 36px); margin-bottom: 15px; color: yellow; font-weight: bold; text-shadow: 0 0 8px rgba(255, 255, 0, 0.5); }
            .split-overlay-universal .main-message { font-size: clamp(40px, 8vw, 72px); font-weight: bold; margin-bottom: 20px; color: red; text-shadow: 0 0 15px rgba(255, 0, 0, 0.7); }
            .split-overlay-universal .overlay-timer, .split-overlay-universal .overlay-remaining-minutes { font-size: clamp(28px, 5vw, 48px); font-weight: bold; margin-bottom: 20px; }
            .split-overlay-universal .overlay-timer { color: orange; } .split-overlay-universal .overlay-remaining-minutes { color: cyan; }
            .split-overlay-universal .overlay-timer-control { margin-bottom: 20px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center; color: white; font-size: 18px; }
            .split-overlay-universal .overlay-timer-control label { font-weight: 500; }
            .split-overlay-universal .overlay-timer-control input[type="number"] { width: 70px; padding: 8px 10px; background: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 4px; text-align: center; font-size: 18px; -moz-appearance: textfield; }
            .split-overlay-universal .overlay-timer-control input[type="number"]::-webkit-outer-spin-button, .split-overlay-universal .overlay-timer-control input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
            .split-overlay-universal .overlay-timer-control button { padding: 8px 12px; font-size: 16px; cursor: pointer; background: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 4px; transition: background 0.2s ease-in-out; font-weight: 500; }
            .split-overlay-universal .overlay-timer-control button:hover { background: rgba(255, 255, 255, 0.2); }
            .split-overlay-universal .extend-buttons { display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; margin-bottom: 40px; }
            .split-overlay-universal .extend-buttons button { padding: 12px 25px; font-size: clamp(18px, 3vw, 24px); cursor: pointer; background: #dc3545; border: none; color: white; border-radius: 4px; font-weight: bold; transition: background 0.2s ease-in-out; }
            .split-overlay-universal .extend-buttons button:hover { background: #c82333; }
            .split-overlay-universal img { max-width: 90%; max-height: 45vh; height: auto; border-radius: 8px; margin-top: 20px; }

            /* --- Стили конкретных платформ --- */
            ${platformStyles}
        `);
    }

    // --- ФУНКЦИИ ЯДРА ---

    function getElementId(baseId) {
        return platformConfig ? `${baseId}-${platformConfig.idPrefix}` : baseId;
    }

    function updateSplitDisplay() {
        const inputField = document.getElementById(getElementId("split-input"));
        if (inputField) inputField.valueAsNumber = splitMinutes === null ? 0 : splitMinutes;
        updateSplitStatsDisplay();
    }

    function updateSplitStatsDisplay() {
        const statsElement = document.getElementById(getElementId("split-stats"));
        if (statsElement) {
            const boughtMinutes = splitMinutes === null ? 0 : splitMinutes;
            statsElement.textContent = `Выкуплено: ${boughtMinutes} / Всего: ${totalVideoMinutes !== null ? totalVideoMinutes : '?'} минут`;
        }
        if (overlay) updateOverlayRemainingMinutes();
    }

    function modifySplitInput(minutesToModify) {
        const inputField = document.getElementById(getElementId("split-input"));
        if (!inputField) return;
        let currentVal = inputField.valueAsNumber;
        if (isNaN(currentVal)) currentVal = 0;
        let newVal = currentVal + minutesToModify;
        if (newVal < 0) newVal = 0;
        inputField.valueAsNumber = newVal;
    }

    function modifyTimerInputOverlay(secondsToModify) {
        const inputField = document.getElementById(getElementId("overlay-timer-input"));
        if (!inputField) return;
        let currentVal = inputField.valueAsNumber;
        if (isNaN(currentVal)) currentVal = 0;
        let newVal = currentVal + secondsToModify;
        if (newVal < 0) newVal = 0;
        inputField.valueAsNumber = newVal;

        overlayTimerDuration = newVal;
        localStorage.setItem(localStorageTimerKey, overlayTimerDuration.toString());
        overlayCountdownRemaining = overlayTimerDuration;
        if (overlayCountdownRemaining < 0) overlayCountdownRemaining = 0;

        if (overlayTimerIntervalId) { clearInterval(overlayTimerIntervalId); overlayTimerIntervalId = null; }
        updateOverlayTimer();
        if (overlayTimerDuration > 0) {
            overlayTimerIntervalId = setInterval(updateOverlayTimer, 1000);
        }
    }

    function startSplitCheckInterval() {
        if (!splitCheckIntervalId) {
            splitCheckIntervalId = setInterval(checkSplitCondition, 500);
            GM_log(`[${currentPlatform || 'unknown'}] Split check interval started.`);
        }
    }

    function stopSplitCheckInterval() {
        if (splitCheckIntervalId) {
            clearInterval(splitCheckIntervalId);
            splitCheckIntervalId = null;
            GM_log(`[${currentPlatform || 'unknown'}] Split check interval stopped.`);
        }
    }

    function addMinutesToActiveSplit(minutesToAdd) {
        if (splitMinutes === null) return;
        splitMinutes += minutesToAdd;
        updateSplitDisplay();

        const thresholdSeconds = splitMinutes * 60;
        if (video && video.currentTime < thresholdSeconds && splitTriggered) {
            removeOverlay();
            splitTriggered = false;
            if (video.paused) {
                 video.play().catch(e => GM_log(`[${currentPlatform}] Error playing video after adding minutes: ${e.message}`));
            }
        }
        GM_log(`[${currentPlatform}] Added ${minutesToAdd} minutes. New split: ${splitMinutes}`);
    }

    function updateOverlayTimer() {
        const timerElement = document.getElementById(getElementId('overlay-timer'));
        if (!timerElement) {
             if (overlayTimerIntervalId) { clearInterval(overlayTimerIntervalId); overlayTimerIntervalId = null; }
             return;
        }
        if (overlayCountdownRemaining > 0) {
            const minutes = Math.floor(overlayCountdownRemaining / 60);
            const seconds = overlayCountdownRemaining % 60;
            timerElement.textContent = `ЖДЕМ ${minutes}:${seconds < 10 ? '0' : ''}${seconds}, ИНАЧЕ СКИП`;
            overlayCountdownRemaining--;
        } else {
            timerElement.textContent = `ЖДЕМ 0:00, ИНАЧЕ СКИП`;
            if (overlayTimerIntervalId) { clearInterval(overlayTimerIntervalId); overlayTimerIntervalId = null; }
        }
    }

    function updateOverlayRemainingMinutes() {
        const remainingElement = document.getElementById(getElementId('overlay-remaining-minutes'));
        if (remainingElement) {
            const remainingMinutes = totalVideoMinutes !== null && splitMinutes !== null ? Math.max(0, totalVideoMinutes - splitMinutes) : '?';
            remainingElement.textContent = `ОСТАЛОСЬ ${remainingMinutes} минут выкупить`;
        }
    }

    function checkSplitCondition() {
        if (!platformConfig) return;

        if (!video) {
            video = document.querySelector(platformConfig.videoSelector);
            if (!video) return;
            GM_log(`[${currentPlatform}] Video element found.`);
            initAudioPlayer();
            const volumeSlider = document.getElementById(getElementId('split-volume-slider'));
            if (audioPlayer && volumeSlider) { try { audioPlayer.volume = parseFloat(volumeSlider.value); } catch(e){} }
        }

        if (totalVideoMinutes === null && isFinite(video.duration) && video.duration > 0) {
            totalVideoMinutes = Math.ceil(video.duration / 60);
            GM_log(`[${currentPlatform}] Total video duration found: ${totalVideoMinutes} minutes.`);
            if (panelAdded) updateSplitStatsDisplay();
        } else if ((!isFinite(video.duration) || video.duration <= 0) && totalVideoMinutes !== null) {
            // Optional reset if duration becomes invalid
        }

        if (splitMinutes !== null && splitMinutes > 0) {
            const thresholdSeconds = splitMinutes * 60;
            if (isFinite(video.currentTime) && video.currentTime >= thresholdSeconds && !splitTriggered) {
                GM_log(`[${currentPlatform}] Split triggered at ${video.currentTime.toFixed(1)}s (threshold: ${thresholdSeconds}s).`);
                video.pause();
                splitTriggered = true;
                showOverlay();
                if (audioPlayer) {
                    audioPlayer.pause();
                    audioPlayer.currentTime = 0;
                    audioPlayer.play().catch(e => GM_log(`[${currentPlatform}] Error playing split sound: ${e.message}`));
                }
            }
            if (splitTriggered && isFinite(video.currentTime) && video.currentTime < thresholdSeconds) {
                GM_log(`[${currentPlatform}] Video time (${video.currentTime.toFixed(1)}s) is now before split threshold (${thresholdSeconds}s). Removing overlay.`);
                removeOverlay();
                splitTriggered = false;
            }
        } else if (splitTriggered) {
            GM_log(`[${currentPlatform}] Split cancelled (splitMinutes is ${splitMinutes}). Removing overlay.`);
            removeOverlay();
            splitTriggered = false;
        }
    }

    function showOverlay() {
        if (overlay) return;
        GM_log(`[${currentPlatform}] Showing split overlay.`);

        overlay = document.createElement("div");
        overlay.id = getElementId("split-overlay");
        overlay.className = 'split-overlay-universal';

        const warningMessage = document.createElement("div");
        warningMessage.className = "warning-message";
        warningMessage.textContent = "⚠️ НУЖНО ДОНАТНОЕ ТОПЛИВО ⚠️";

        const mainMessage = document.createElement("div");
        mainMessage.className = "main-message";
        mainMessage.textContent = "СПЛИТ НЕ ОПЛАЧЕН";

        const timerElement = document.createElement("div");
        timerElement.id = getElementId('overlay-timer');
        timerElement.className = "overlay-timer";

        const remainingMinutesElement = document.createElement("div");
        remainingMinutesElement.id = getElementId('overlay-remaining-minutes');
        remainingMinutesElement.className = "overlay-remaining-minutes";

        const overlayTimerControlGroup = document.createElement("div");
        overlayTimerControlGroup.id = getElementId('overlay-timer-control');
        overlayTimerControlGroup.className = "overlay-timer-control";

        const timerLabel = document.createElement("label");
        timerLabel.setAttribute("for", getElementId('overlay-timer-input'));
        timerLabel.textContent = "Таймер (сек):";

        const timerInputField = document.createElement("input");
        timerInputField.type = "number";
        timerInputField.id = getElementId('overlay-timer-input');
        timerInputField.min = "0";
        timerInputField.value = overlayTimerDuration;

        const timerButtons = [
            { text: '-60', seconds: -60 }, { text: '-10', seconds: -10 }, { text: '-5', seconds: -5 },
            { text: '+5', seconds: 5 }, { text: '+10', seconds: 10 }, { text: '+60', seconds: 60 }
        ];

        overlayTimerControlGroup.appendChild(timerLabel);
        overlayTimerControlGroup.appendChild(timerInputField);
        timerButtons.forEach(btnInfo => {
            const button = document.createElement("button");
            button.textContent = btnInfo.text;
            button.dataset.seconds = btnInfo.seconds;
            overlayTimerControlGroup.appendChild(button);
        });

        const extendButtonsContainer = document.createElement("div");
        extendButtonsContainer.id = getElementId('split-extend-buttons');
        extendButtonsContainer.className = "extend-buttons";

        const extendButtonConfigs = [
            { minutes: 1, cost: extendCost }, { minutes: 5, cost: extendCost * 5 },
            { minutes: 10, cost: extendCost * 10 }, { minutes: 20, cost: extendCost * 20 }
        ];
        extendButtonConfigs.forEach(config => {
            const button = document.createElement("button");
            button.textContent = `+ ${config.minutes} минут${getMinuteEnding(config.minutes)} - ${config.cost} рублей`;
            button.addEventListener("click", () => addMinutesToActiveSplit(config.minutes));
            extendButtonsContainer.appendChild(button);
        });

        const gifElement = document.createElement("img");
        gifElement.src = overlayGifUrl;
        gifElement.alt = "Split GIF";

        overlay.appendChild(warningMessage);
        overlay.appendChild(mainMessage);
        overlay.appendChild(timerElement);
        overlay.appendChild(remainingMinutesElement);
        overlay.appendChild(overlayTimerControlGroup);
        overlay.appendChild(extendButtonsContainer);
        overlay.appendChild(gifElement);

        document.body.appendChild(overlay);

        overlay.querySelector(`#${getElementId('overlay-timer-input')}`).addEventListener('input', function() {
            const val = this.valueAsNumber;
            if (!isNaN(val) && val >= 0) {
                 overlayTimerDuration = val;
                 localStorage.setItem(localStorageTimerKey, overlayTimerDuration.toString());
                 overlayCountdownRemaining = overlayTimerDuration;
                 if (overlayTimerIntervalId) clearInterval(overlayTimerIntervalId);
                 if (overlayTimerDuration > 0) overlayTimerIntervalId = setInterval(updateOverlayTimer, 1000);
                 else overlayTimerIntervalId = null;
                 updateOverlayTimer();
            } else { this.valueAsNumber = overlayTimerDuration; }
        });
        overlay.querySelectorAll(`#${getElementId('overlay-timer-control')} button`).forEach(button => {
            button.addEventListener("click", () => modifyTimerInputOverlay(parseInt(button.dataset.seconds, 10)));
        });

        overlayCountdownRemaining = overlayTimerDuration;
        if (overlayCountdownRemaining < 0) overlayCountdownRemaining = 0;
        updateOverlayTimer();
        updateOverlayRemainingMinutes();
        if (overlayTimerDuration > 0 && !overlayTimerIntervalId) {
            overlayTimerIntervalId = setInterval(updateOverlayTimer, 1000);
        }
    }

    function getMinuteEnding(count) {
        count = Math.abs(count); const d1 = count % 10; const d2 = count % 100;
        if (d2 >= 11 && d2 <= 19) return ''; if (d1 === 1) return 'а'; if (d1 >= 2 && d1 <= 4) return 'ы'; return '';
    }

    function removeOverlay() {
        if (overlay) {
             GM_log(`[${currentPlatform || 'unknown'}] Removing split overlay.`);
            overlay.remove(); overlay = null;
            if (overlayTimerIntervalId) { clearInterval(overlayTimerIntervalId); overlayTimerIntervalId = null; }
            if (audioPlayer) { audioPlayer.pause(); }
        }
    }

    function initAudioPlayer() {
        if (!audioPlayer && splitSoundUrl && !splitSoundUrl.includes('ВАША_ПРЯМАЯ_ССЫЛКА')) {
            GM_log(`[${currentPlatform || 'unknown'}] Initializing audio player with URL: ${splitSoundUrl}`);
            try {
                audioPlayer = new Audio(splitSoundUrl);
                audioPlayer.preload = 'auto';
                audioPlayer.onerror = (e) => { GM_log(`[${currentPlatform || 'unknown'}] ERROR loading audio: ${e.target.error.message}`); console.error("Universal Split: Ошибка загрузки звука:", e); audioPlayer = null; };
                let savedVolume = localStorage.getItem(localStorageVolumeKey) ?? '0.5';
                audioPlayer.volume = parseFloat(savedVolume);
            } catch (error) { GM_log(`[${currentPlatform || 'unknown'}] ERROR creating Audio object: ${error.message}`); console.error("Universal Split: Ошибка создания Audio:", error); audioPlayer = null; }
        } else if (audioPlayer) {
             let savedVolume = localStorage.getItem(localStorageVolumeKey) ?? '0.5';
             try { audioPlayer.volume = parseFloat(savedVolume); } catch(e){}
        }
    }

    // --- ФУНКЦИИ УПРАВЛЕНИЯ ПАНЕЛЬЮ ---

    function addControlPanel() {
        if (panelAdded || !platformConfig) return;
        const insertionElement = document.querySelector(platformConfig.insertionSelector);
        if (!insertionElement) { GM_log(`[${currentPlatform}] Insertion element (${platformConfig.insertionSelector}) not found.`); return; }

        GM_log(`[${currentPlatform}] Adding control panel...`);
        panelElement = document.createElement("div");
        panelElement.id = getElementId("split-control-panel");
        panelElement.className = 'split-control-panel-universal';

        const setButton = document.createElement("button");
        setButton.id = getElementId('set-split-button');
        setButton.className = 'set-split-button';
        setButton.textContent = "НАЧАТЬ СПЛИТ";

        const splitLabel = document.createElement("label");
        splitLabel.setAttribute("for", getElementId('split-input'));
        splitLabel.appendChild(document.createTextNode("Сплит (мин):"));
        const splitLabelInstruction = document.createElement("i");
        splitLabelInstruction.textContent = "(уст. перед \"Начать\")";
        splitLabel.appendChild(splitLabelInstruction);

        const splitInputGroup = document.createElement("div");
        splitInputGroup.id = getElementId('split-input-group');
        splitInputGroup.className = 'split-input-group';

        const splitInputField = document.createElement("input");
        splitInputField.type = "number";
        splitInputField.id = getElementId('split-input');
        splitInputField.min = "0";
        splitInputField.value = splitMinutes === null ? 0 : splitMinutes;
        splitInputGroup.appendChild(splitInputField);

        const splitModifyButtons = [ { text: '+1', minutes: 1 }, { text: '+5', minutes: 5 }, { text: '+10', minutes: 10 }, { text: '+20', minutes: 20 } ];
        splitModifyButtons.forEach(btnInfo => {
            const button = document.createElement("button");
            button.textContent = btnInfo.text; button.dataset.minutes = btnInfo.minutes;
            splitInputGroup.appendChild(button);
        });

        const volumeControlGroup = document.createElement("div");
        volumeControlGroup.id = getElementId('split-volume-control');
        volumeControlGroup.className = 'split-volume-control';
        const volumeLabel = document.createElement("label");
        volumeLabel.setAttribute("for", getElementId('split-volume-slider'));
        volumeLabel.textContent = "Громк. алерта:";
        const volumeSlider = document.createElement("input");
        volumeSlider.type = "range"; volumeSlider.id = getElementId('split-volume-slider');
        volumeSlider.min = "0"; volumeSlider.max = "1"; volumeSlider.step = "0.05";
        volumeSlider.value = localStorage.getItem(localStorageVolumeKey) ?? '0.5';
        volumeControlGroup.appendChild(volumeLabel); volumeControlGroup.appendChild(volumeSlider);

        const statsElement = document.createElement("span");
        statsElement.id = getElementId('split-stats'); statsElement.className = 'split-stats';
        statsElement.textContent = "Выкуплено: 0 / Всего: ? минут";

        panelElement.appendChild(setButton); panelElement.appendChild(splitLabel);
        panelElement.appendChild(splitInputGroup); panelElement.appendChild(volumeControlGroup);
        panelElement.appendChild(statsElement);

        switch (platformConfig.insertionMethod) {
            case 'prepend': insertionElement.insertBefore(panelElement, insertionElement.firstChild); break;
            case 'appendChild': insertionElement.appendChild(panelElement); break;
            case 'before': insertionElement.parentNode.insertBefore(panelElement, insertionElement); break;
            case 'afterend': insertionElement.parentNode.insertBefore(panelElement, insertionElement.nextSibling); break;
            default: insertionElement.insertBefore(panelElement, insertionElement.firstChild);
        }
        panelAdded = true;

        // --- Start Button Listener ---
        setButton.addEventListener("click", () => {
            const inputVal = parseInt(splitInputField.value, 10);
            if (!isNaN(inputVal) && inputVal >= 0) {
                splitMinutes = inputVal;
                if (splitMinutes > 0) {
                    startSplitCheckInterval();
                    setButton.textContent = "СПЛИТ НАЧАТ";
                    setButton.classList.add("active");
                    checkSplitCondition(); // Проверить сразу

                    // --- AUTOPLAY LOGIC ---
                    if (video && video.paused) {
                         GM_log(`[${currentPlatform}] Attempting to play video on split start...`);
                         video.play().catch(e => GM_log(`[${currentPlatform}] Autoplay on split start failed: ${e.message}`));
                    }
                    // --- END AUTOPLAY LOGIC ---

                } else { // splitMinutes is 0 or invalid
                    stopSplitCheckInterval();
                    splitTriggered = false;
                    removeOverlay();
                    setButton.textContent = "НАЧАТЬ СПЛИТ";
                    setButton.classList.remove("active");
                }
                updateSplitDisplay(); // Update stats/input field
            } else {
                alert("Введите корректное число минут.");
            }
        });
        // --- End Button Listener ---

        splitInputGroup.querySelectorAll("button").forEach(button => {
             button.addEventListener("click", () => modifySplitInput(parseInt(button.dataset.minutes, 10)));
        });
        volumeSlider.addEventListener("input", function() {
            const newVolume = parseFloat(this.value);
            if (audioPlayer) audioPlayer.volume = newVolume;
            localStorage.setItem(localStorageVolumeKey, newVolume.toString());
        });

        updateSplitDisplay(); // Initial update

        if (platformConfig.needsVisibilityCheck) {
            controlsElement = document.querySelector(platformConfig.controlsElementSelector);
            if (controlsElement) startVisibilityCheckInterval();
            else GM_log(`[${currentPlatform}] Native controls element (${platformConfig.controlsElementSelector}) not found for visibility check.`);
        }
        GM_log(`[${currentPlatform}] Control panel added successfully.`);
    }

    function ensurePanelPosition() {
        if (!panelAdded || !panelElement || !platformConfig) return;
        const insertionElement = document.querySelector(platformConfig.insertionSelector);
        if (!insertionElement) { GM_log(`[${currentPlatform}] Insertion element (${platformConfig.insertionSelector}) disappeared. Removing panel.`); removeControlPanel(); return; }

        let currentPositionCorrect = false;
        switch(platformConfig.insertionMethod) {
            case 'prepend': currentPositionCorrect = (insertionElement.firstChild === panelElement); break;
            case 'appendChild': currentPositionCorrect = (insertionElement.lastChild === panelElement); break;
            case 'before': currentPositionCorrect = (insertionElement.previousSibling === panelElement); break;
            case 'afterend': currentPositionCorrect = (insertionElement.nextSibling === panelElement); break;
            default: currentPositionCorrect = (insertionElement.firstChild === panelElement);
        }
        if (!currentPositionCorrect) {
            GM_log(`[${currentPlatform}] Panel position incorrect. Re-inserting...`);
             switch(platformConfig.insertionMethod) {
                 case 'prepend': insertionElement.insertBefore(panelElement, insertionElement.firstChild); break;
                 case 'appendChild': insertionElement.appendChild(panelElement); break;
                 case 'before': insertionElement.parentNode.insertBefore(panelElement, insertionElement); break;
                 case 'afterend': insertionElement.parentNode.insertBefore(panelElement, insertionElement.nextSibling); break;
                 default: insertionElement.insertBefore(panelElement, insertionElement.firstChild);
             }
        }
    }

    function removeControlPanel() {
        if (panelElement) { panelElement.remove(); panelElement = null; }
        panelAdded = false; stopVisibilityCheckInterval(); controlsElement = null;
        GM_log(`[${currentPlatform || 'unknown'}] Control panel removed.`);
    }

    // --- СПЕЦИФИЧНЫЕ ФУНКЦИИ ДЛЯ VK (Видимость) ---
    function checkControlsVisibility() {
        if (!panelElement || !controlsElement || currentPlatform !== 'vk') return;
        const isHiddenByStyle = controlsElement.style.display === 'none'; const isHiddenByClass = controlsElement.classList.contains('hidden');
        const controlsAreVisible = !isHiddenByStyle && !isHiddenByClass;
        if (controlsAreVisible && !panelElement.classList.contains('visible')) { panelElement.classList.add('visible'); }
        else if (!controlsAreVisible && panelElement.classList.contains('visible')) { panelElement.classList.remove('visible'); }
    }
    function startVisibilityCheckInterval() {
        if (!visibilityCheckIntervalId && platformConfig?.needsVisibilityCheck) {
            GM_log(`[${currentPlatform}] Starting controls visibility check interval.`);
            visibilityCheckIntervalId = setInterval(checkControlsVisibility, 300); checkControlsVisibility();
        }
    }
    function stopVisibilityCheckInterval() {
        if (visibilityCheckIntervalId) {
            GM_log(`[${currentPlatform}] Stopping controls visibility check interval.`);
            clearInterval(visibilityCheckIntervalId); visibilityCheckIntervalId = null;
        }
    }

    // --- ОПРЕДЕЛЕНИЕ ПЛАТФОРМЫ ---
    function getCurrentPlatform() {
        const hostname = location.hostname; const pathname = location.pathname;
        if (hostname.includes('vk.com') || hostname.includes('vkvideo.ru')) { if (pathname.includes('/video_ext.php')) return 'vk'; }
        else if (hostname.includes('rutube.ru')) { if (pathname.startsWith('/video/')) return 'rutube'; }
        else if (hostname.includes('youtube.com')) { if (pathname.startsWith('/watch')) return 'youtube'; }
        return 'unknown';
    }

    // --- СБРОС СОСТОЯНИЯ ---
    function resetState() {
        const platformToLog = currentPlatform !== 'unknown' ? currentPlatform : 'Previous';
        GM_log(`[${platformToLog}] Resetting state...`);
        stopSplitCheckInterval(); stopVisibilityCheckInterval();
        if (overlayTimerIntervalId) { clearInterval(overlayTimerIntervalId); overlayTimerIntervalId = null; }
        removeOverlay(); removeControlPanel();
        splitMinutes = null; totalVideoMinutes = null; video = null; splitTriggered = false;
    }

    // --- ГЛАВНАЯ ФУНКЦИЯ НАСТРОЙКИ ---
    function setupPlatformSplit() {
        const detectedPlatform = getCurrentPlatform();
        if (detectedPlatform !== currentPlatform) {
             if (currentPlatform !== 'unknown') { GM_log(`Platform changed from ${currentPlatform} to ${detectedPlatform} or unknown. Resetting...`); resetState(); }
             currentPlatform = detectedPlatform;
             if (currentPlatform !== 'unknown') {
                 platformConfig = platformConfigs[currentPlatform];
                 GM_log(`[${currentPlatform}] Initializing...`); injectGlobalStyles(); initAudioPlayer();
             } else { platformConfig = null; return; }
        }
        if (currentPlatform === 'unknown' || !platformConfig) return;
        if (panelAdded) {
            ensurePanelPosition();
            if (platformConfig.needsVisibilityCheck && !visibilityCheckIntervalId && controlsElement) startVisibilityCheckInterval();
            if (splitMinutes !== null && splitMinutes > 0 && !splitCheckIntervalId) startSplitCheckInterval();
            return;
        }
        video = document.querySelector(platformConfig.videoSelector);
        const insertionElement = document.querySelector(platformConfig.insertionSelector);
        if (video && insertionElement) {
            addControlPanel();
            if (panelAdded && splitMinutes !== null && splitMinutes > 0 && !splitCheckIntervalId) startSplitCheckInterval();
        }
    }

    // --- ИНИЦИАЛИЗАЦИЯ И ОТСЛЕЖИВАНИЕ НАВИГАЦИИ ---
    function initialize() {
        GM_log("Universal Video Split: Initializing (v1.2)...");
        lastUrl = location.href;
        if (!setupIntervalId) { setupIntervalId = setInterval(setupPlatformSplit, 750); }
        navigationObserver = new MutationObserver((mutations) => {
             if (location.href !== lastUrl) {
                 GM_log(`URL changed from ${lastUrl} to ${location.href}`); lastUrl = location.href;
                 setupPlatformSplit(); // Reset/setup handled inside
                  if (!setupIntervalId) setupIntervalId = setInterval(setupPlatformSplit, 750);
             }
        });
        navigationObserver.observe(document.body, { childList: true, subtree: true });
        setupPlatformSplit(); // Initial run
    }

    // --- Очистка при выходе ---
    window.addEventListener('beforeunload', () => {
        GM_log("Universal Video Split: Unloading, cleaning up...");
        resetState();
        if (setupIntervalId) { clearInterval(setupIntervalId); setupIntervalId = null; }
        if (navigationObserver) { navigationObserver.disconnect(); navigationObserver = null; }
        if (audioPlayer) { try { audioPlayer.pause(); } catch(e){} audioPlayer = null; }
    });

    // --- Запуск ---
    initialize();

})();