Advertisement
Alexey711

Untitled

Jun 1st, 2025
91
0
6 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2.     // @name         YouTube Enhanced Player
  3.     // @name:en      YouTube Enhanced Player
  4.     // @name:es      YouTube Reproductor Mejorado
  5.     // @namespace    http://tampermonkey.net/
  6.     // @version      1.6.3
  7.     // @description  Запоминает позицию просмотра видео и возобновляет с этого места (минус 5 секунд), сохраняет выбранную громкость
  8.     // @description:en Remembers video playback position and resumes from that point (minus 5 seconds), saves selected volume
  9.     // @description:es Recuerda la posición de reproducción y continúa desde ese punto (menos 5 segundos), guarda el volumen seleccionado
  10.     // @author       YourName
  11.     // @match        https://www.youtube.com/*
  12.     // @grant        none
  13.     // @icon         https://img.icons8.com/?size=100&id=55200&format=png&color=000000
  14.     // @license      MIT
  15.     // ==/UserScript==
  16.  
  17.     (function() {
  18.         'use strict';
  19.  
  20.         function getVideoId() {
  21.             const urlParams = new URLSearchParams(window.location.search);
  22.             return urlParams.get('v');
  23.         }
  24.  
  25.         function saveVideoTime(videoId, currentTime) {
  26.             localStorage.setItem(`yt_time_${videoId}`, currentTime.toString());
  27.         }
  28.  
  29.         function loadVideoTime(videoId) {
  30.             const savedTime = localStorage.getItem(`yt_time_${videoId}`);
  31.             return savedTime ? parseFloat(savedTime) : 0;
  32.         }
  33.  
  34.         function saveVolume(volume) {
  35.             localStorage.setItem('yt_volume', volume.toString());
  36.         }
  37.  
  38.         function loadVolume() {
  39.             const savedVolume = localStorage.getItem('yt_volume');
  40.             return savedVolume ? parseInt(savedVolume, 10) : 100;
  41.         }
  42.  
  43.         function showSaveNotification() {
  44.             const overlay = document.querySelector('.html5-video-player .ytp-player-content')
  45.                          || document.querySelector('.ytp-chrome-top')
  46.                          || document.body;
  47.  
  48.             if (getComputedStyle(overlay).position === 'static') {
  49.                 overlay.style.position = 'relative';
  50.             }
  51.  
  52.             const old = overlay.querySelector('.timeSaveNotification');
  53.             if (old) old.remove();
  54.  
  55.             const notif = document.createElement('div');
  56.             notif.className = 'timeSaveNotification';
  57.             Object.assign(notif.style, {
  58.                 position: 'absolute',
  59.                 bottom: '0px',
  60.                 right: '5px',
  61.                 background: 'rgba(0,0,0,0.7)',
  62.                 color: '#fff',
  63.                 padding: '5px 10px',
  64.                 borderRadius: '5px',
  65.                 zIndex: '9999',
  66.                 fontSize: '14px',
  67.                 opacity: '0',
  68.                 transition: 'opacity 0.5s ease',
  69.             });
  70.             notif.innerText = 'Время просмотра сохранено!';
  71.             overlay.appendChild(notif);
  72.  
  73.             requestAnimationFrame(() => notif.style.opacity = '1');
  74.             setTimeout(() => {
  75.                 notif.style.opacity = '0';
  76.                 setTimeout(() => notif.remove(), 500);
  77.             }, 3000);
  78.         }
  79.  
  80.         function initResumePlayback() {
  81.             const video = document.querySelector('video');
  82.             if (!video) return;
  83.  
  84.             const videoId = getVideoId();
  85.             if (!videoId) return;
  86.  
  87.             const savedTime = loadVideoTime(videoId);
  88.             if (savedTime > 0) {
  89.                 const resumeTime = Math.max(0, savedTime - 5);
  90.                 video.currentTime = resumeTime;
  91.             }
  92.  
  93.             setInterval(() => {
  94.                 if (!video.paused) {
  95.                     const videoId = getVideoId();
  96.                     if (videoId) {
  97.                         saveVideoTime(videoId, video.currentTime);
  98.                     }
  99.                 }
  100.             }, 5000);
  101.  
  102.             window.addEventListener('beforeunload', () => {
  103.                 const videoId = getVideoId();
  104.                 if (videoId) {
  105.                     saveVideoTime(videoId, video.currentTime);
  106.                 }
  107.             });
  108.         }
  109.  
  110.         function calculateVolume(position, sliderMax) {
  111.             const volume = (position / sliderMax) * 1400;
  112.             return volume.toFixed();
  113.         }
  114.  
  115.         function updateVolumeDisplay(volume) {
  116.             const old = document.getElementById('customVolumeDisplay');
  117.             if (old) old.remove();
  118.  
  119.             const btn = document.getElementById('volumeBoostButton');
  120.             if (!btn) return;
  121.  
  122.             const volumeDisplay = document.createElement('div');
  123.             volumeDisplay.id = 'customVolumeDisplay';
  124.             volumeDisplay.innerText = `${volume}%`;
  125.  
  126.             Object.assign(volumeDisplay.style, {
  127.                 position: 'absolute',
  128.                 fontSize: '14px',
  129.                 background: 'rgba(0,0,0,0.8)',
  130.                 color: '#fff',
  131.                 borderRadius: '5px',
  132.                 whiteSpace: 'nowrap',
  133.                 padding: '2px 6px',
  134.                 pointerEvents: 'none',
  135.                 transition: 'opacity 0.3s ease, transform 0.3s ease',
  136.                 opacity: '0',
  137.                 transform: 'translate(-50%, -10px)',
  138.             });
  139.  
  140.             const btnContainer = btn.parentElement;
  141.             btnContainer.style.position = 'relative';
  142.             btnContainer.appendChild(volumeDisplay);
  143.  
  144.             const btnRect = btn.getBoundingClientRect();
  145.             const containerRect = btnContainer.getBoundingClientRect();
  146.             const offsetX = btnRect.left - containerRect.left + btnRect.width / 2;
  147.             const offsetY = btnRect.top - containerRect.top;
  148.  
  149.             volumeDisplay.style.left = `${offsetX}px`;
  150.             volumeDisplay.style.top = `${offsetY}px`;
  151.  
  152.             requestAnimationFrame(() => {
  153.                 volumeDisplay.style.opacity = '1';
  154.                 volumeDisplay.style.transform = 'translate(-50%, -20px)';
  155.             });
  156.  
  157.             setTimeout(() => {
  158.                 volumeDisplay.style.opacity = '0';
  159.                 volumeDisplay.style.transform = 'translate(-50%, -10px)';
  160.                 setTimeout(() => volumeDisplay.remove(), 300);
  161.             }, 1000);
  162.         }
  163.  
  164.         function createControlPanel(video) {
  165.             const videoId = getVideoId();
  166.             if (!videoId) return;
  167.  
  168.             const style = document.createElement('style');
  169.             style.textContent = `
  170.             #volumeBoostButton input[type=range] {
  171.                 -webkit-appearance: none;
  172.                 width: 100px;
  173.                 height: 4px;
  174.                 background: #ccc;
  175.                 borderRadius: 2px;
  176.                 outline: none;
  177.             }
  178.             #volumeBoostButton input[type=range]::-webkit-slider-thumb {
  179.                 -webkit-appearance: none;
  180.                 appearance: none;
  181.                 width: 12px;
  182.                 height: 12px;
  183.                 borderRadius: 50%;
  184.                 background: #fff;
  185.                 cursor: pointer;
  186.                 box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
  187.             }`;
  188.             document.head.appendChild(style);
  189.  
  190.             const saveButton = document.createElement('button');
  191.             saveButton.id = 'manualSaveButton';
  192.             saveButton.innerText = '💾';
  193.             Object.assign(saveButton.style, {
  194.                 background: 'none',
  195.                 border: 'none',
  196.                 cursor: 'pointer',
  197.                 color: '#fff',
  198.                 fontWeight: 'bold',
  199.                 marginRight: '1px',
  200.                 fontSize: '18px',
  201.                 transition: 'transform 0.2s ease',
  202.             });
  203.             saveButton.title = 'Сохранить текущее время просмотра';
  204.  
  205.             saveButton.addEventListener('click', () => {
  206.                 saveVideoTime(videoId, video.currentTime);
  207.                 showSaveNotification();
  208.             });
  209.  
  210.             const volumeBoostButton = document.createElement('button');
  211.             volumeBoostButton.id = 'volumeBoostButton';
  212.             volumeBoostButton.innerText = '🔊';
  213.             Object.assign(volumeBoostButton.style, {
  214.                 background: 'none',
  215.                 border: 'none',
  216.                 cursor: 'pointer',
  217.                 color: '#fff',
  218.                 fontWeight: 'bold',
  219.                 marginRight: '1px',
  220.                 fontSize: '18px',
  221.                 transition: 'transform 0.2s ease',
  222.             });
  223.             volumeBoostButton.title = 'Усилитель громкости';
  224.  
  225.             const customVolumeSlider = document.createElement('input');
  226.             Object.assign(customVolumeSlider, {
  227.                 type: 'range',
  228.                 min: '100',
  229.                 max: '1400',
  230.                 step: '1',
  231.                 value: loadVolume(), // Загружаем сохраненное значение громкости
  232.             });
  233.             Object.assign(customVolumeSlider.style, {
  234.                 display: 'none',
  235.                 opacity: '0',
  236.                 transform: 'scale(0.8)',
  237.                 transition: 'opacity 0.3s ease, transform 0.3s ease',
  238.             });
  239.  
  240.             const audioContext = new (window.AudioContext || window.webkitAudioContext)();
  241.             const gainNode = audioContext.createGain();
  242.             gainNode.connect(audioContext.destination);
  243.             const videoSource = audioContext.createMediaElementSource(video);
  244.             videoSource.connect(gainNode);
  245.             gainNode.gain.value = loadVolume() / 100; // Устанавливаем начальную громкость
  246.  
  247.             customVolumeSlider.addEventListener('input', function() {
  248.                 const volume = calculateVolume(this.value, this.max);
  249.                 gainNode.gain.value = volume / 100;
  250.                 updateVolumeDisplay(volume);
  251.                 saveVolume(volume); // Сохраняем громкость
  252.             });
  253.  
  254.             function resetVolumeTo100() {
  255.                 customVolumeSlider.value = '100';
  256.                 gainNode.gain.value = 1.0;
  257.                 updateVolumeDisplay('100');
  258.                 saveVolume('100'); // Сохраняем сброшенное значение
  259.             }
  260.  
  261.             volumeBoostButton.addEventListener('mouseenter', () => {
  262.                 customVolumeSlider.style.display = 'block';
  263.                 requestAnimationFrame(() => {
  264.                     customVolumeSlider.style.opacity = '1';
  265.                     customVolumeSlider.style.transform = 'scale(1)';
  266.                 });
  267.             });
  268.  
  269.             volumeBoostButton.addEventListener('click', (e) => {
  270.                 e.stopPropagation();
  271.                 resetVolumeTo100();
  272.             });
  273.  
  274.             let hideTimeout;
  275.  
  276.             const sliderContainer = document.createElement('div');
  277.             sliderContainer.style.display = 'flex';
  278.             sliderContainer.style.alignItems = 'center';
  279.             sliderContainer.style.position = 'relative';
  280.             sliderContainer.style.padding = '0px';
  281.             sliderContainer.style.marginLeft = '0px';
  282.             sliderContainer.style.borderRadius = '1px';
  283.             sliderContainer.style.cursor = 'pointer';
  284.  
  285.             sliderContainer.addEventListener('mouseleave', () => {
  286.                 hideTimeout = setTimeout(() => {
  287.                     customVolumeSlider.style.opacity = '0';
  288.                     customVolumeSlider.style.transform = 'scale(0.8)';
  289.                     setTimeout(() => {
  290.                         customVolumeSlider.style.display = 'none';
  291.                     }, 300);
  292.                 }, 300);
  293.             });
  294.  
  295.             sliderContainer.addEventListener('mouseenter', () => {
  296.                 clearTimeout(hideTimeout);
  297.             });
  298.  
  299.             const controls = document.querySelector('.ytp-chrome-controls');
  300.             if (controls) {
  301.                 const buttonContainer = document.createElement('div');
  302.                 buttonContainer.style.display = 'flex';
  303.                 buttonContainer.style.alignItems = 'center';
  304.                 buttonContainer.style.marginRight = '10px';
  305.  
  306.                 sliderContainer.appendChild(volumeBoostButton);
  307.                 sliderContainer.appendChild(customVolumeSlider);
  308.  
  309.                 buttonContainer.appendChild(saveButton);
  310.                 buttonContainer.appendChild(sliderContainer);
  311.  
  312.                 controls.insertBefore(buttonContainer, controls.firstChild);
  313.  
  314.                 sliderContainer.addEventListener('wheel', (e) => {
  315.                     e.preventDefault();
  316.                     const step = 50;
  317.                     let val = parseInt(customVolumeSlider.value, 10);
  318.                     if (e.deltaY < 0) {
  319.                         val = Math.min(val + step, parseInt(customVolumeSlider.max, 10));
  320.                     } else {
  321.                         val = Math.max(val - step, parseInt(customVolumeSlider.min, 10));
  322.                     }
  323.                     customVolumeSlider.value = val;
  324.                     customVolumeSlider.dispatchEvent(new Event('input'));
  325.                 });
  326.             }
  327.  
  328.             updateVolumeDisplay(loadVolume()); // Отображаем начальную громкость
  329.         }
  330.  
  331.         function init() {
  332.             initResumePlayback();
  333.             const video = document.querySelector('video');
  334.             if (video) createControlPanel(video);
  335.         }
  336.  
  337.         const checkVideo = setInterval(() => {
  338.             if (document.querySelector('video') && document.querySelector('.ytp-chrome-controls')) {
  339.                 clearInterval(checkVideo);
  340.                 init();
  341.             }
  342.         }, 500);
  343.     })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement