// ==UserScript==
// @name YouTube Split
// @namespace http://tampermonkey.net/
// @version 2.0
// @author sosal
// @match *://www.youtube.com/watch*
// @grant none
// @description Устанавливает сплит по времени
// ==/UserScript==
(function() {
'use strict';
// --- Конфигурация ---
let splitMinutes = null; // Значение сплита в минутах, по умолчанию null (сплит не активен)
let totalVideoMinutes = 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;
}
#split-volume-control input[type="range"] {
flex-grow: 1;
min-width: 80px;
-webkit-appearance: none;
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;
}
#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%;
}
#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-stats {
font-size: 15px;
color: var(--yt-spec-text-primary);
font-weight: 500;
margin-left: 10px; /* Отступ от других элементов */
}
/* Стили для оверлея */
#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;
}
updateSplitStatsDisplay(); // Обновляем также статистику
}
// Обновление отображения статистики выкупленных/всего минут
function updateSplitStatsDisplay() {
const statsElement = document.getElementById("split-stats");
if (statsElement) {
const boughtMinutes = splitMinutes === null ? 0 : splitMinutes;
const totalMinutesText = totalVideoMinutes !== null ? `${totalVideoMinutes}` : '?';
statsElement.textContent = `Выкуплено: ${boughtMinutes} / Всего: ${totalMinutesText} минут`;
}
}
// Функция для изменения значения в поле ввода сплита
// Эта функция вызывается кнопками +/- и меняет ТОЛЬКО ПОЛЕ ВВОДА
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;
// Не обновляем global splitMinutes здесь и не вызываем updateSplitDisplay()
}
// Функция для запуска интервала проверки сплита
function startSplitCheckInterval() {
if (!splitCheckIntervalId) {
splitCheckIntervalId = setInterval(checkSplitCondition, 500);
console.log("YouTube Split: Запущена проверка сплита (interval).");
}
}
// Функция для остановки интервала проверки сплита
function stopSplitCheckInterval() {
if (splitCheckIntervalId) {
clearInterval(splitCheckIntervalId);
splitCheckIntervalId = null;
console.log("YouTube Split: Проверка сплита остановлена.");
}
}
// Создание панели управления сплитом и вставка ее под видео
// Эта функция вызывается, когда нужный контейнер уже найден и panelAdded === false
function addControlPanel(primaryContainer) {
if (panelAdded) {
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; // Устанавливаем активный сплит из поля ввода
if (splitMinutes > 0) {
startSplitCheckInterval(); // Запускаем проверку если сплит > 0
setButton.textContent = "СПЛИТ НАЧАТ"; // Меняем текст кнопки
setButton.style.background = 'var(--yt-spec-call-to-action)'; // Опционально: меняем цвет кнопки
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();
if (video.paused && oldSplitMinutes === null) {
video.play();
}
}
}
} else { // splitMinutes === 0
stopSplitCheckInterval();
splitTriggered = false;
removeOverlay();
setButton.textContent = "НАЧАТЬ СПЛИТ"; // Возвращаем текст кнопки
setButton.style.background = 'var(--yt-spec-brand-suggested-action)'; // Возвращаем цвет кнопки
if (video && video.paused && oldSplitMinutes !== null && oldSplitMinutes > 0) {
video.play();
}
}
// Убеждаемся, что поле ввода и статистика показывают актуальное *активное* значение после нажатия
updateSplitDisplay();
updateSplitStatsDisplay(); // Обновляем статистику
} 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);
// --- Группа ввода сплита и +/- кнопок ---
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";
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 = parseFloat(this.value); // Убедимся, что это число
}
// Сохраняем значение в localStorage
localStorage.setItem(localStorageVolumeKey, this.value);
});
volumeControlGroup.appendChild(volumeLabel);
volumeControlGroup.appendChild(volumeSlider);
// --- Статистика выкупленных/всего минут ---
const statsElement = document.createElement("span");
statsElement.id = "split-stats";
// Начальный текст будет установлен функцией updateSplitStatsDisplay
// --- Собираем панель ---
panel.appendChild(setButton);
panel.appendChild(label);
panel.appendChild(inputGroup);
panel.appendChild(volumeControlGroup); // Добавляем регулятор громкости
panel.appendChild(statsElement); // Добавляем статистику
// Вставляем панель в найденный контейнер (#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) {
// Эта функция вызывается только когда сплит уже активен (splitMinutes не null)
if (splitMinutes === null) return;
splitMinutes += minutesToAdd;
updateSplitDisplay(); // Обновляем поле ввода на панели и статистику
const thresholdSeconds = splitMinutes * 60;
if (video && video.currentTime < thresholdSeconds) {
removeOverlay();
splitTriggered = false;
video.play();
}
// Оверлей остается, если продления недостаточно
}
// Проверка времени видео (вызывается только когда splitCheckIntervalId активен)
function checkSplitCondition() {
// Ищем видео каждый раз, на случай его пересоздания YouTube
if (!video) {
video = document.querySelector("video");
if (!video) {
console.log("YouTube Split: Видеоэлемент не найден в checkSplitCondition, останавливаем проверку сплита.");
stopSplitCheckInterval();
splitTriggered = false;
removeOverlay();
return;
}
// Убеждаемся, что аудио плеер есть и громкость применена
initAudioPlayer();
const volumeSlider = document.getElementById('split-volume-slider');
if(audioPlayer && volumeSlider) audioPlayer.volume = parseFloat(volumeSlider.value); // Применяем актуальную громкость
}
// Обновляем totalVideoMinutes, если он еще не определен и duration доступна
if (totalVideoMinutes === null && video && isFinite(video.duration) && video.duration > 0) {
totalVideoMinutes = Math.ceil(video.duration / 60); // Округляем вверх
console.log(`YouTube Split: Общая длительность видео определена: ${totalVideoMinutes} минут.`);
updateSplitStatsDisplay(); // Обновляем статистику с новым значением total
}
// Проверяем условие сплита только если 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();
}
}
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() НЕ вызывается здесь
// updateSplitStatsDisplay() НЕ вызывается здесь
// updateSplitDisplay() вызывается при изменении splitMinutes
// updateSplitStatsDisplay() вызывается при изменении splitMinutes И totalVideoMinutes
}
// Проверяем длительность видео, если она еще не определена
if (totalVideoMinutes === null && isFinite(video.duration) && video.duration > 0) {
totalVideoMinutes = Math.ceil(video.duration / 60);
console.log(`YouTube Split: Общая длительность видео определена: ${totalVideoMinutes} минут.`);
if (panelAdded) { // Если панель уже есть, обновляем статистику
updateSplitStatsDisplay();
}
}
} else {
// Элементы еще не найдены
if (panelAdded) {
console.log("YouTube Split: Необходимые элементы отсутствуют, удаляем панель и сбрасываем состояние.");
const existingPanel = document.getElementById("split-control-panel");
if(existingPanel) existingPanel.remove();
panelAdded = false; // Сбрасываем флаг
// Не сбрасываем splitMinutes на null здесь
}
video = null; // Сбрасываем видео, если оно пропало
totalVideoMinutes = null; // Сбрасываем длительность, если видео пропало
}
}
// --- Запуск скрипта ---
if (!setupIntervalId) {
setupIntervalId = setInterval(setupElementsAndPanel, 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; // Сбрасываем сплит
totalVideoMinutes = null; // Сбрасываем общую длительность
video = null;
splitTriggered = false;
panelAdded = false; // Сбрасываем флаг
if (!setupIntervalId) {
setupIntervalId = setInterval(setupElementsAndPanel, 500);
console.log("YouTube Split: Перезапущен основной интервал поиска элементов после смены URL.");
}
}
});
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;
}
if (urlObserver) {
urlObserver.disconnect();
}
});
})();