// ==UserScript==
// @name YouTube Split
// @namespace http://tampermonkey.net/
// @version 2.2
// @author Gemini 2.5 Flash
// @match *://www.youtube.com/watch*
// @grant none
// @description Устанавливает сплит по времени, панель управления под видео, показывает статистику "Выкуплено/Всего минут", оверлей "СПЛИТ НЕ ОПЛАЧЕН" с гифкой и проигрывает звук при достижении порога.
// ==/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'; // URL гифки для оверлея
const localStorageVolumeKey = 'ytSplitAlertVolume';
// --- Глобальные переменные состояния ---
let video = null;
let overlay = null;
let splitTriggered = false;
let audioPlayer = null;
let splitCheckIntervalId = null;
let setupIntervalId = null;
let panelAdded = false;
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);
}
#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;
margin-bottom: 40px; /* Отступ перед гифкой */
}
#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);
}
/* Стили для гифки на оверлее */
#split-overlay img {
max-width: 115%; /* Увеличим максимальную ширину */
max-height: 50vh; /* Увеличим максимальную высоту (например, до 50% высоты вьюпорта) */
height: auto;
border-radius: 8px;
margin-top: 20px; /* Добавим отступ сверху, чтобы отделить от кнопок */
}
`;
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);
}
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;
}
function startSplitCheckInterval() {
if (!splitCheckIntervalId) {
splitCheckIntervalId = setInterval(checkSplitCondition, 500);
}
}
function stopSplitCheckInterval() {
if (splitCheckIntervalId) {
clearInterval(splitCheckIntervalId);
splitCheckIntervalId = null;
}
}
function addControlPanel(primaryContainer) {
if (panelAdded) {
ensurePanelPosition();
updateSplitDisplay();
return;
}
if (!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();
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("Введите корректное число минут.");
}
});
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";
volumeSlider.step = "0.05";
let savedVolume = localStorage.getItem(localStorageVolumeKey);
if (savedVolume === null) {
savedVolume = '0.5';
}
volumeSlider.value = savedVolume;
volumeSlider.addEventListener("input", function() {
if (audioPlayer) {
audioPlayer.volume = parseFloat(this.value);
}
localStorage.setItem(localStorageVolumeKey, this.value);
});
volumeControlGroup.appendChild(volumeLabel);
volumeControlGroup.appendChild(volumeSlider);
if (audioPlayer) {
audioPlayer.volume = parseFloat(savedVolume);
}
const statsElement = document.createElement("span");
statsElement.id = "split-stats";
panel.appendChild(setButton);
panel.appendChild(label);
panel.appendChild(inputGroup);
panel.appendChild(volumeControlGroup);
panel.appendChild(statsElement);
primaryContainer.insertBefore(panel, primaryContainer.firstChild);
panelAdded = true;
updateSplitDisplay();
updateSplitStatsDisplay();
}
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) {
primaryContainer.insertBefore(panel, primaryContainer.firstChild);
}
}
}
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() {
if (!video) {
video = document.querySelector("video");
if (!video) {
stopSplitCheckInterval();
splitTriggered = false;
removeOverlay();
return;
}
initAudioPlayer();
const volumeSlider = document.getElementById('split-volume-slider');
if(audioPlayer && volumeSlider) audioPlayer.volume = parseFloat(volumeSlider.value);
}
if (totalVideoMinutes === null && isFinite(video.duration) && video.duration > 0) {
totalVideoMinutes = Math.ceil(video.duration / 60);
if (panelAdded) {
updateSplitStatsDisplay();
}
}
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 {
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);
});
// --- Добавление гифки ---
const gifElement = document.createElement("img");
gifElement.src = overlayGifUrl;
// CSS стили для гифки определены в блоке styles
overlay.appendChild(warningMessage);
overlay.appendChild(splitMessage);
overlay.appendChild(extendButtonsContainer);
overlay.appendChild(gifElement); // Добавляем гифку после кнопок
document.body.appendChild(overlay);
}
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;
}
}
}
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);
let savedVolume = localStorage.getItem(localStorageVolumeKey);
if (savedVolume !== null) {
audioPlayer.volume = parseFloat(savedVolume);
} else {
audioPlayer.volume = 0.5;
}
}
} else {
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) {
addControlPanel(primaryContainer);
} else {
ensurePanelPosition();
}
if (totalVideoMinutes === null && isFinite(video.duration) && video.duration > 0) {
totalVideoMinutes = Math.ceil(video.duration / 60);
if (panelAdded) {
updateSplitStatsDisplay();
}
}
} else {
if (panelAdded) {
const existingPanel = document.getElementById("split-control-panel");
if(existingPanel) existingPanel.remove();
panelAdded = false;
}
video = null;
totalVideoMinutes = null;
}
}
if (!setupIntervalId) {
setupIntervalId = setInterval(setupElementsAndPanel, 500);
}
let lastUrl = location.href;
const urlObserver = new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
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);
}
}
});
urlObserver.observe(document.body, {
childList: true,
subtree: true
});
window.addEventListener('beforeunload', function() {
stopSplitCheckInterval();
if (setupIntervalId) {
clearInterval(setupIntervalId);
setupIntervalId = null;
}
if (audioPlayer) {
audioPlayer.pause();
audioPlayer = null;
}
if (urlObserver) {
urlObserver.disconnect();
}
});
})();