您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Устанавливает сплит по времени (только после нажатия "НАЧАТЬ СПЛИТ"), выводит панель управления под видео с кнопками настройки и громкости, показывает оверлей "СПЛИТ НЕ ОПЛАЧЕН" с кнопками продления и проигрывает звук при достижении порога. Исправлены ошибки.
当前为
- // ==UserScript==
- // @name YouTube Split
- // @namespace http://tampermonkey.net/
- // @version 1.9 // Повышаем версию
- // @author sosal
- // @match *://www.youtube.com/watch*
- // @grant none
- // @description Устанавливает сплит по времени (только после нажатия "НАЧАТЬ СПЛИТ"), выводит панель управления под видео с кнопками настройки и громкости, показывает оверлей "СПЛИТ НЕ ОПЛАЧЕН" с кнопками продления и проигрывает звук при достижении порога. Исправлены ошибки.
- // ==/UserScript==
- (function() {
- 'use strict';
- // --- Конфигурация ---
- let splitMinutes = null; // Значение сплита в минутах, по умолчанию null (сплит не активен)
- const extendCost = 300; // Стоимость продления +1 минуты в условных рублях
- // ВСТАВЬТЕ СЮДА ПРЯМУЮ ССЫЛКУ НА ВАШ ЗВУК!
- const splitSoundUrl = 'https://github.com/lardan099/donat/raw/refs/heads/main/alert_orig.mp3';
- const localStorageVolumeKey = 'ytSplitAlertVolume'; // Ключ для сохранения громкости в localStorage
- // --- Глобальные переменные состояния ---
- let video = null; // Элемент видео
- let overlay = null; // Элемент оверлея
- let splitTriggered = false; // Флаг, что сплит активирован (видео остановлено)
- let audioPlayer = null; // Для проигрывания звука
- let splitCheckIntervalId = null; // ID интервала проверки сплита (для логики сплита)
- let setupIntervalId = null; // ID интервала для поиска элементов и добавления панели
- let panelAdded = false; // Флаг, чтобы добавлять панель только один раз
- // --- CSS Стили для улучшения внешнего вида ---
- const styles = `
- /* Стили для панели управления */
- #split-control-panel {
- margin-top: 10px;
- margin-bottom: 15px;
- padding: 10px 15px;
- background: var(--yt-spec-badge-chip-background);
- border: 1px solid var(--yt-spec-border-div);
- border-radius: 8px;
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 10px 20px; /* Увеличим горизонтальный разрыв */
- color: var(--yt-spec-text-primary);
- font-family: "Roboto", Arial, sans-serif;
- font-size: 14px;
- max-width: var(--ytd-watch-flexy-width);
- width: 100%;
- box-sizing: border-box;
- }
- ytd-watch-flexy:not([use- Sarkis]) #primary #split-control-panel {
- margin-left: auto;
- margin-right: auto;
- }
- #split-control-panel label {
- font-weight: 500;
- color: var(--yt-spec-text-secondary);
- flex-shrink: 0;
- line-height: 1.3;
- }
- #split-control-panel label i {
- font-style: normal;
- font-size: 12px;
- color: var(--yt-spec-text-disabled);
- }
- /* Контейнер для поля ввода и кнопок модификации */
- #split-input-group {
- display: flex;
- align-items: center;
- gap: 5px;
- }
- #split-control-panel input[type="number"] {
- width: 60px;
- padding: 8px 10px;
- background: var(--yt-spec-filled-button-background);
- color: var(--yt-spec-text-primary);
- border: 1px solid var(--yt-spec-action-simulate-border);
- border-radius: 4px;
- text-align: center;
- font-size: 15px;
- -moz-appearance: textfield;
- }
- #split-control-panel input[type="number"]::-webkit-outer-spin-button,
- #split-control-panel input[type="number"]::-webkit-inner-spin-button {
- -webkit-appearance: none;
- margin: 0;
- }
- /* Стили для всех кнопок внутри панели */
- #split-control-panel button {
- padding: 8px 15px;
- font-size: 15px;
- cursor: pointer;
- background: var(--yt-spec-grey-1);
- color: var(--yt-spec-text-primary);
- border: none;
- border-radius: 4px;
- transition: background 0.2s ease-in-out;
- font-weight: 500;
- flex-shrink: 0;
- }
- #split-control-panel button:hover {
- background: var(--yt-spec-grey-2);
- }
- /* Стили для кнопок модификации сплита (+1, +5 и т.д.) */
- #split-input-group button {
- padding: 8px 10px;
- font-size: 14px;
- background: var(--yt-spec-filled-button-background);
- border: 1px solid var(--yt-spec-action-simulate-border);
- }
- #split-input-group button:hover {
- background: var(--yt-spec-grey-2);
- }
- #split-control-panel button#set-split-button {
- background: var(--yt-spec-brand-suggested-action);
- color: var(--yt-spec-text-reverse);
- order: -1;
- margin-right: auto;
- }
- #split-control-panel button#set-split-button:hover {
- background: var(--yt-spec-brand-suggested-action-hover);
- }
- /* Стили для регулятора громкости */
- #split-volume-control {
- display: flex;
- align-items: center;
- gap: 5px;
- }
- #split-volume-control label {
- font-weight: 500;
- color: var(--yt-spec-text-secondary);
- flex-shrink: 0;
- line-height: normal; /* Сброс line-height для этой метки */
- }
- #split-volume-control input[type="range"] {
- flex-grow: 1; /* Занимает доступное место */
- min-width: 80px; /* Минимальная ширина слайдера */
- -webkit-appearance: none; /* Удаляем стандартный стиль Chrome/Safari */
- appearance: none;
- height: 8px; /* Высота трека */
- background: var(--yt-spec-grey-1);
- outline: none;
- opacity: 0.7;
- transition: opacity .2s;
- border-radius: 4px;
- }
- #split-volume-control input[type="range"]:hover {
- opacity: 1;
- }
- /* Стили для ползунка (Chrome/Safari) */
- #split-volume-control input[type="range"]::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- width: 15px; /* Ширина ползунка */
- height: 15px; /* Высота ползунка */
- background: var(--yt-spec-brand-button-background); /* Цвет ползунка */
- cursor: pointer;
- border-radius: 50%; /* Круглый ползунок */
- }
- /* Стили для ползунка (Firefox) */
- #split-volume-control input[type="range"]::-moz-range-thumb {
- width: 15px;
- height: 15px;
- background: var(--yt-spec-brand-button-background);
- cursor: pointer;
- border-radius: 50%;
- }
- /* Стили для оверлея */
- #split-overlay {
- 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: 99999;
- font-family: "Roboto", Arial, sans-serif;
- text-align: center;
- padding: 20px;
- box-sizing: border-box;
- }
- #split-overlay #split-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 #split-main-message {
- font-size: clamp(40px, 8vw, 72px);
- font-weight: bold;
- margin-bottom: 40px;
- color: red;
- text-shadow: 0 0 15px rgba(255, 0, 0, 0.7);
- }
- #split-extend-buttons {
- display: flex;
- gap: 15px;
- flex-wrap: wrap;
- justify-content: center;
- }
- #split-extend-buttons button {
- padding: 12px 25px;
- font-size: clamp(18px, 3vw, 24px);
- cursor: pointer;
- background: var(--yt-spec-red-500);
- border: none;
- color: white;
- border-radius: 4px;
- font-weight: bold;
- transition: background 0.2s ease-in-out;
- }
- #split-extend-buttons button:hover {
- background: var(--yt-spec-red-600);
- }
- `;
- // --- Вспомогательная функция для внедрения CSS ---
- function injectStyles() {
- if (document.getElementById('yt-split-styles')) {
- return;
- }
- const styleElement = document.createElement("style");
- styleElement.id = 'yt-split-styles';
- styleElement.textContent = styles;
- document.head.appendChild(styleElement);
- console.log("YouTube Split: Стили внедрены.");
- }
- // Обновление отображения значения сплита в спинере
- // Эта функция теперь вызывается только при добавлении панели и при изменении global splitMinutes
- function updateSplitDisplay() {
- const inputField = document.getElementById("split-input");
- if (inputField) {
- inputField.valueAsNumber = splitMinutes === null ? 0 : splitMinutes;
- }
- }
- // Функция для изменения значения в поле ввода сплита
- // Эта функция вызывается кнопками +/- и меняет ТОЛЬКО ПОЛЕ ВВОДА
- function modifySplitInput(minutesToModify) {
- const inputField = document.getElementById("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 startSplitCheckInterval() {
- if (!splitCheckIntervalId) {
- splitCheckIntervalId = setInterval(checkSplitCondition, 500);
- console.log("YouTube Split: Запущена проверка сплита (interval).");
- } else {
- // console.log("YouTube Split: Проверка сплита уже запущена.");
- }
- }
- // Функция для остановки интервала проверки сплита
- function stopSplitCheckInterval() {
- if (splitCheckIntervalId) {
- clearInterval(splitCheckIntervalId);
- splitCheckIntervalId = null;
- console.log("YouTube Split: Проверка сплита остановлена.");
- }
- }
- // Создание панели управления сплитом и вставка ее под видео
- // Эта функция вызывается, когда нужный контейнер уже найден и panelAdded === false
- function addControlPanel(primaryContainer) {
- if (panelAdded) { // Дополнительная проверка, хотя setupElementsAndPanel тоже проверяет
- ensurePanelPosition(); // Убедимся в позиции существующей панели
- updateSplitDisplay(); // Обновим поле ввода на существующей панели
- return;
- }
- if (!primaryContainer) {
- console.error("YouTube Split: addControlPanel вызвана без primaryContainer!");
- return;
- }
- const panel = document.createElement("div");
- panel.id = "split-control-panel";
- // --- Кнопка "НАЧАТЬ СПЛИТ" ---
- const setButton = document.createElement("button");
- setButton.id = "set-split-button";
- setButton.textContent = "НАЧАТЬ СПЛИТ";
- setButton.addEventListener("click", function() {
- const inputField = document.getElementById("split-input");
- const inputVal = inputField.valueAsNumber;
- if (!isNaN(inputVal) && inputVal >= 0) {
- const oldSplitMinutes = splitMinutes; // Сохраняем для логики продолжения видео
- splitMinutes = inputVal; // Устанавливаем активный сплит из поля ввода
- // updateSplitDisplay(); // Не вызываем здесь, чтобы не мешать modifySplitInput
- if (splitMinutes > 0) {
- startSplitCheckInterval(); // Запускаем проверку если сплит > 0
- // Проверяем условие сразу после установки, только если видео найдено
- if (video) {
- const thresholdSeconds = splitMinutes * 60;
- if (video.currentTime >= thresholdSeconds) {
- video.pause();
- splitTriggered = true;
- showOverlay();
- if(splitSoundUrl && audioPlayer && audioPlayer.src !== 'ВАША_ПРЯМАЯ_ССЫЛКА_НА_ЗВУКОВОЙ_ФАЙЛ_ТУТ'){
- audioPlayer.pause();
- audioPlayer.currentTime = 0;
- audioPlayer.play().catch(e => console.error("YouTube Split: Ошибка при воспроизведении звука:", e));
- }
- } else {
- splitTriggered = false;
- removeOverlay();
- // Если видео было на паузе *до* нажатия "НАЧАТЬ СПЛИТ" (т.е. сплит был null), и новое время сплита не достигнуто, запускаем
- if (video.paused && oldSplitMinutes === null) {
- video.play();
- }
- }
- }
- } else { // splitMinutes === 0
- stopSplitCheckInterval();
- splitTriggered = false;
- removeOverlay();
- if (video && video.paused && oldSplitMinutes !== null && oldSplitMinutes > 0) {
- video.play();
- }
- }
- // Убеждаемся, что поле ввода показывает актуальное *активное* значение после нажатия
- updateSplitDisplay();
- } else {
- alert("Введите корректное число минут.");
- }
- });
- // --- Метка "Сплит (мин):" (Исправление TrustedHTML) ---
- const label = document.createElement("label");
- label.setAttribute("for", "split-input");
- const labelTextMain = document.createTextNode("Сплит (мин):");
- const breakElement = document.createElement("br");
- const italicElement = document.createElement("i");
- const labelTextInstruction = document.createTextNode("(уст. перед \"Начать\")");
- label.appendChild(labelTextMain);
- label.appendChild(breakElement);
- italicElement.appendChild(labelTextInstruction);
- label.appendChild(italicElement);
- // --- Конец Исправления TrustedHTML ---
- // --- Группа ввода сплита и +/- кнопок ---
- const inputGroup = document.createElement("div");
- inputGroup.id = "split-input-group";
- const inputField = document.createElement("input");
- inputField.type = "number";
- inputField.id = "split-input";
- inputField.min = "0";
- // Начальное значение будет установлено функцией updateSplitDisplay при добавлении
- inputField.valueAsNumber = splitMinutes === null ? 0 : splitMinutes;
- const modifyButtons = [
- { text: '-10', minutes: -10 },
- { text: '-5', minutes: -5 },
- { text: '-1', minutes: -1 },
- { text: '+1', minutes: 1 },
- { text: '+5', minutes: 5 },
- { text: '+10', minutes: 10 },
- { text: '+20', minutes: 20 }
- ];
- modifyButtons.forEach(btnInfo => {
- const button = document.createElement("button");
- button.textContent = btnInfo.text;
- button.addEventListener("click", () => modifySplitInput(btnInfo.minutes));
- inputGroup.appendChild(button);
- });
- inputGroup.insertBefore(inputField, inputGroup.children[0]);
- // --- Регулятор громкости алерта ---
- const volumeControlGroup = document.createElement("div");
- volumeControlGroup.id = "split-volume-control";
- const volumeLabel = document.createElement("label");
- volumeLabel.setAttribute("for", "split-volume-slider");
- volumeLabel.textContent = "Громкость алерта:";
- const volumeSlider = document.createElement("input");
- volumeSlider.type = "range";
- volumeSlider.id = "split-volume-slider";
- volumeSlider.min = "0";
- volumeSlider.max = "1"; // HTMLMediaElement volume is typically 0 to 1
- volumeSlider.step = "0.05"; // Adjust step for finer control
- // Установка начального значения громкости из localStorage (или дефолт)
- let savedVolume = localStorage.getItem(localStorageVolumeKey);
- if (savedVolume === null) {
- savedVolume = '0.5'; // Громкость по умолчанию 50%
- }
- volumeSlider.value = savedVolume;
- // Обработчик изменения громкости
- volumeSlider.addEventListener("input", function() {
- // Применяем громкость к аудиоплееру, если он есть
- if (audioPlayer) {
- audioPlayer.volume = this.value;
- }
- // Сохраняем значение в localStorage
- localStorage.setItem(localStorageVolumeKey, this.value);
- });
- volumeControlGroup.appendChild(volumeLabel);
- volumeControlGroup.appendChild(volumeSlider);
- // Применяем громкость к аудиоплееру сразу после создания панели,
- // т.к. audioPlayer мог быть создан до панели.
- if (audioPlayer) {
- audioPlayer.volume = savedVolume;
- }
- // --- Собираем панель ---
- panel.appendChild(setButton);
- panel.appendChild(label);
- panel.appendChild(inputGroup);
- panel.appendChild(volumeControlGroup); // Добавляем регулятор громкости
- // Вставляем панель в найденный контейнер (#primary) как первый дочерний элемент
- primaryContainer.insertBefore(panel, primaryContainer.firstChild);
- panelAdded = true; // Устанавливаем флаг, что панель добавлена
- console.log("YouTube Split: Панель управления добавлена под видео.");
- // Убедимся, что поле ввода показывает актуальное значение после добавления
- updateSplitDisplay();
- }
- // Функция для проверки и корректировки позиции панели
- // Вызывается из setupElementsAndPanel
- function ensurePanelPosition() {
- if (!panelAdded) return; // Проверяем позицию только если панель добавлена
- const panel = document.getElementById("split-control-panel");
- const primaryContainer = document.querySelector("ytd-watch-flexy #primary");
- if (panel && primaryContainer) {
- if (primaryContainer.firstChild !== panel) {
- console.log("YouTube Split: Панель сместилась, перемещаем обратно.");
- primaryContainer.insertBefore(panel, primaryContainer.firstChild);
- }
- }
- // Если панель есть, но контейнера нет, setupElementsAndPanel ее удалит.
- }
- function addMinutesToActiveSplit(minutesToAdd) {
- if (splitMinutes === null) return;
- splitMinutes += minutesToAdd;
- updateSplitDisplay(); // Обновляем поле ввода на панели
- const thresholdSeconds = splitMinutes * 60;
- if (video && video.currentTime < thresholdSeconds) {
- removeOverlay();
- splitTriggered = false;
- video.play();
- }
- // Оверлей остается, если продления недостаточно
- }
- function checkSplitCondition() {
- // Ищем видео каждый раз, на случай его пересоздания YouTube
- if (!video) {
- video = document.querySelector("video");
- if (!video) {
- console.log("YouTube Split: Видеоэлемент не найден в checkSplitCondition, останавливаем проверку сплита.");
- stopSplitCheckInterval();
- splitTriggered = false;
- removeOverlay();
- // Панель и аудио плеер будут обработаны основным setupIntervalId или urlObserver
- return;
- }
- // Убеждаемся, что аудио плеер есть и громкость применена
- initAudioPlayer();
- const volumeSlider = document.getElementById('split-volume-slider');
- if(audioPlayer && volumeSlider) audioPlayer.volume = volumeSlider.value;
- }
- // Проверяем условие сплита только если splitMinutes активно (не null и > 0)
- if (splitMinutes !== null && splitMinutes > 0) {
- const thresholdSeconds = splitMinutes * 60;
- if (video.currentTime >= thresholdSeconds && !splitTriggered) {
- video.pause();
- splitTriggered = true;
- showOverlay();
- // Воспроизводим звук при первом триггере
- if(splitSoundUrl && audioPlayer && audioPlayer.src !== 'ВАША_ПРЯМАЯ_ССЫЛКА_НА_ЗВУКОВОЙ_ФАЙЛ_ТУТ'){
- audioPlayer.pause();
- audioPlayer.currentTime = 0;
- audioPlayer.play().catch(e => console.error("YouTube Split: Ошибка при воспроизведении звука:", e));
- }
- }
- if (splitTriggered && video.currentTime < thresholdSeconds) {
- removeOverlay();
- splitTriggered = false;
- video.play();
- }
- } else {
- // Если splitMinutes стал null или 0 во время работы проверки
- stopSplitCheckInterval();
- splitTriggered = false;
- removeOverlay();
- if (video && video.paused) video.play();
- }
- // ensurePanelPosition() убрана отсюда
- }
- function showOverlay() {
- if (overlay) return;
- overlay = document.createElement("div");
- overlay.id = "split-overlay";
- const warningMessage = document.createElement("div");
- warningMessage.id = "split-warning-message";
- warningMessage.textContent = "⚠️ НУЖНО ДОНАТНОЕ ТОПЛИВО ⚠️";
- const splitMessage = document.createElement("div");
- splitMessage.id = "split-main-message";
- splitMessage.textContent = "СПЛИТ НЕ ОПЛАЧЕН";
- const extendButtonsContainer = document.createElement("div");
- extendButtonsContainer.id = "split-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", function() {
- addMinutesToActiveSplit(config.minutes);
- });
- extendButtonsContainer.appendChild(button);
- });
- overlay.appendChild(warningMessage);
- overlay.appendChild(splitMessage);
- overlay.appendChild(extendButtonsContainer);
- document.body.appendChild(overlay);
- console.log("YouTube Split: Оверлей показан.");
- }
- function getMinuteEnding(count) {
- const lastDigit = count % 10;
- const lastTwoDigits = count % 100;
- if (lastTwoDigits >= 11 && lastTwoDigits <= 14) {
- return ''; // минут
- }
- if (lastDigit === 1) {
- return 'а'; // минута
- }
- if (lastDigit >= 2 && lastDigit <= 4) {
- return 'ы'; // минуты
- }
- return ''; // минут
- }
- function removeOverlay() {
- if (overlay) {
- overlay.remove();
- overlay = null;
- if (audioPlayer) {
- audioPlayer.pause();
- audioPlayer.currentTime = 0;
- }
- console.log("YouTube Split: Оверлей убран.");
- }
- }
- // Инициализация аудио плеера и установка громкости
- function initAudioPlayer() {
- if (splitSoundUrl && splitSoundUrl !== 'ВАША_ПРЯМАЯ_ССЫЛКА_НА_ЗВУКОВОЙ_ФАЙЛ_ТУТ') {
- if (!audioPlayer || audioPlayer.src !== splitSoundUrl) {
- if (audioPlayer) {
- audioPlayer.pause();
- audioPlayer = null;
- }
- audioPlayer = new Audio(splitSoundUrl);
- audioPlayer.preload = 'auto';
- audioPlayer.onerror = (e) => console.error("YouTube Split: Не удалось загрузить или воспроизвести звук:", e);
- console.log("YouTube Split: Аудио плеер инициализирован.");
- // Применяем сохраненную громкость после создания плеера
- let savedVolume = localStorage.getItem(localStorageVolumeKey);
- if (savedVolume !== null) {
- audioPlayer.volume = parseFloat(savedVolume);
- } else {
- audioPlayer.volume = 0.5; // Громкость по умолчанию
- }
- }
- } else {
- console.warn("YouTube Split: URL звука для сплита не указан или является плейсхолдером.");
- if (audioPlayer) {
- audioPlayer.pause();
- audioPlayer = null;
- }
- }
- }
- // --- Главная функция, вызываемая по интервалу для поиска элементов и настройки ---
- function setupElementsAndPanel() {
- // Внедряем стили и инициализируем аудио плеер при каждой попытке,
- // функции сами проверят, нужно ли это делать.
- injectStyles();
- initAudioPlayer();
- // Пытаемся найти видео и контейнер для панели
- video = document.querySelector("video"); // Обновляем ссылку на видео
- const primaryContainer = document.querySelector("ytd-watch-flexy #primary");
- const panel = document.getElementById("split-control-panel");
- if (video && primaryContainer) {
- // Элементы найдены
- if (!panelAdded) { // Проверяем наш флаг
- // Если панель еще не добавлена
- console.log("YouTube Split: Видео и контейнер #primary найдены. Добавляем панель.");
- addControlPanel(primaryContainer); // Передаем найденный контейнер
- } else {
- // Если панель уже добавлена, убедимся, что она на своем месте
- ensurePanelPosition();
- // updateSplitDisplay() НЕ вызывается здесь, чтобы не сбрасывать поле ввода
- }
- } else {
- // Элементы еще не найдены
- // Если панель была добавлена, но контейнер исчез (например, навигация),
- // удалим панель и сбросим флаг.
- if (panelAdded) {
- console.log("YouTube Split: Необходимые элементы отсутствуют, удаляем панель и сбрасываем состояние.");
- const existingPanel = document.getElementById("split-control-panel");
- if(existingPanel) existingPanel.remove();
- panelAdded = false; // Сбрасываем флаг
- // Не сбрасываем splitMinutes на null здесь
- // splitCheckIntervalId и overlay очищаются в urlObserver или checkSplitCondition
- }
- video = null; // Сбрасываем видео, если оно пропало
- }
- }
- // --- Запуск скрипта ---
- // Запускаем интервал для поиска элементов и настройки.
- // Этот интервал будет работать постоянно на страницах *://www.youtube.com/watch*
- // и будет пытаться добавить панель и найти видео/контейнер каждые 500мс.
- if (!setupIntervalId) {
- setupIntervalId = setInterval(setupElementsAndPanel, 500); // Проверяем каждые 500мс
- console.log("YouTube Split: Запущен основной интервал поиска элементов.");
- }
- // Очистка при уходе со страницы или навигации по SPA
- let lastUrl = location.href;
- const urlObserver = new MutationObserver(() => {
- if (location.href !== lastUrl) {
- lastUrl = location.href;
- console.log("YouTube Split: URL changed, cleaning up and re-initializing.");
- // Очищаем предыдущие интервалы
- stopSplitCheckInterval(); // Останавливаем проверку сплита
- if (setupIntervalId) { // Останавливаем основной интервал поиска элементов
- clearInterval(setupIntervalId);
- setupIntervalId = null;
- }
- // Останавливаем звук и убираем оверлей
- if (audioPlayer) {
- audioPlayer.pause();
- }
- removeOverlay();
- // Удаляем старую панель, если она есть
- const oldPanel = document.getElementById("split-control-panel");
- if (oldPanel) {
- oldPanel.remove();
- }
- // Удаляем старые стили, чтобы обновились, если нужно
- const oldStyles = document.getElementById("yt-split-styles");
- if(oldStyles) oldStyles.remove();
- // Сбрасываем глобальные переменные состояния
- splitMinutes = null; // Сплит не активен по умолчанию на новой странице
- video = null; // Видео будет найдено заново в setupElementsAndPanel
- splitTriggered = false;
- panelAdded = false; // Сбрасываем флаг добавления панели
- // Перезапускаем основной интервал для нового видео
- if (!setupIntervalId) {
- setupIntervalId = setInterval(setupElementsAndPanel, 500);
- console.log("YouTube Split: Перезапущен основной интервал поиска элементов после смены URL.");
- }
- // initAudioPlayer будет вызван в setupElementsAndPanel
- }
- });
- // Наблюдаем за изменениями в body для отслеживания SPA навигации.
- urlObserver.observe(document.body, {
- childList: true,
- subtree: true
- });
- // Очистка при закрытии вкладки
- window.addEventListener('beforeunload', function() {
- console.log("YouTube Split: Очистка при выгрузке страницы.");
- stopSplitCheckInterval();
- if (setupIntervalId) {
- clearInterval(setupIntervalId);
- setupIntervalId = null;
- }
- if (audioPlayer) {
- audioPlayer.pause();
- audioPlayer = null; // Удаляем объект Audio при окончательной выгрузке
- }
- if (urlObserver) {
- urlObserver.disconnect();
- }
- });
- })();