I have been using this mini-player YouTube extension for a while. It's not on the Chrome Store anymore, but I have a folder of it from the developer.
The developer isn't working on this anymore, so I am asking here. I like this extension, but I have been having this issue whenever I open a YouTube link externally. It throws off the YouTube player when scrolling. It's not the biggest issue in the world, but I am just seeing if there is something that can be done to fix it. I am not a big coder, so I am asking here. I am not sure if this can be fixed or its something conflicting with new YouTube since this extension is pretty old. I pasted some links with the full code and some pictures.
"use strict";(function() { let DOCK_DOCKED = 'docked', DOCK_FLOATING = 'floating'; let PAGE_CHANNEL = 'channel', PAGE_HOME = 'home', PAGE_SEARCH = 'search', PAGE_SUBSCRIPTIONS = 'subscriptions', PAGE_WATCH = 'watch'; let SCREEN_PADDING = 16; let currentAnimation, currentAnimationCancelled, currentDockMode, currentVideoId, currentPage; let queryParams, isNewPage; let screen, screenHeader, screenTitle, screenButtonClose, screenButtonMove, screenButtonResizeTopLeft, screenButtonResizeTopRight, screenButtonResizeBottomLeft, screenButtonResizeBottomRight, isScreenMove, isScreenResize; let iframe, iframeVideo, moviePlayer, moviePlayerObserver = new MutationObserver(onMoviePlayerObserve), playerAPI, video, videoTitle; let intervalCurrentTime, intervalIframeInitialize, intervalVideoInitialize; let localStorage = window.localStorage['rjgtav.youtube-mini-player'] ? JSON.parse(window.localStorage['rjgtav.youtube-mini-player']) : {}; // Thanks to https://www.reddit.com/user/roombascj // Workaround: Chrome doesn't fire 'resize' event when it adds the scrollbar // Source: https://pastebin.com/tgDxgPza let windowResizeListener = document.createElement('iframe'); windowResizeListener.id = 'rj-resize-listener'; windowResizeListener.onload = () => windowResizeListener.contentWindow.addEventListener('resize', onWindowResize); window.addEventListener('scroll', onWindowScroll); document.addEventListener('DOMContentLoaded', onPageLoad); document.addEventListener('spfdone', onPageLoad); // #YoutubeMaterialDesign document.addEventListener('yt-navigate-finish', onPageLoad); document.addEventListener('yt-update-title', onPageTitle); function initializeIframe () { if (iframe != null) return; screen = document.createElement('div'); screen.id = 'rj-miniplayer-screen'; screen.style.position = 'fixed'; document.body.appendChild(screen); screenHeader = document.createElement('div'); screenHeader.className = 'rj-miniplayer-header'; screen.appendChild(screenHeader); screenButtonMove = document.createElement('div'); screenButtonMove.className = 'rj-miniplayer-move'; screenButtonMove.title = 'Move'; screenButtonMove.style.backgroundImage = "url('" + chrome.extension.getURL('assets/button/move.png') +"')"; screenButtonMove.addEventListener('mousedown', onScreenMouseDown); screenHeader.appendChild(screenButtonMove); screenTitle = document.createElement('div'); screenTitle.className = 'rj-miniplayer-title'; screenTitle.addEventListener('click', onScreenTitleClick); screenHeader.appendChild(screenTitle); screenButtonClose = document.createElement('div'); screenButtonClose.className = 'rj-miniplayer-close'; screenButtonClose.title = 'Close'; screenButtonClose.style.backgroundImage = "url('" + chrome.extension.getURL('assets/button/close.png') +"')"; screenButtonClose.addEventListener('click', onScreenButtonCloseClick); screenHeader.appendChild(screenButtonClose); screenButtonResizeBottomRight = document.createElement('div'); screenButtonResizeBottomRight.className = 'rj-miniplayer-resize bottom-right'; screenButtonResizeBottomRight.title = 'Resize'; screenButtonResizeBottomRight.addEventListener('mousedown', onScreenMouseDown); screen.appendChild(screenButtonResizeBottomRight); screenButtonResizeTopRight = document.createElement('div'); screenButtonResizeTopRight.className = 'rj-miniplayer-resize top-right'; screenButtonResizeTopRight.title = 'Resize'; screenButtonResizeTopRight.addEventListener('mousedown', onScreenMouseDown); screenHeader.appendChild(screenButtonResizeTopRight); iframe = document.createElement('iframe'); iframe.setAttribute('allowfullscreen', '1'); iframe.setAttribute('frameborder', '0'); iframe.onload = onIframeLoad; screen.appendChild(iframe); } function updateIframeSource () { if (iframe == null) return; // If it's the same video, we keep playing it if (currentVideoId == queryParams.v) return; currentVideoId = queryParams.v; iframeVideo = null; iframe.contentWindow.location.replace('https://www.youtube.com/embed/'+ currentVideoId +'?autoplay=1&modestbranding=1&rel=0&showinfo=0'); // We periodically update the video.currentTime, so the Share button on Youtube works as expected // Update: this has a high performance impact :/ //clearInterval(intervalCurrentTime); //intervalCurrentTime = setInterval(() => iframeVideo ? video.currentTime = iframeVideo.currentTime : null, 1000); } function initializeVideo() { if (video != null) { video.removeEventListener('playing', onVideoPlay); video.removeEventListener('play', onVideoPlay); video.removeEventListener('seeking', onVideoSeek); video.removeEventListener('seeked', onVideoSeek); } // Prevent Youtube from auto-playing // Workaround: we use intervals so this still runs when the tab isn't focused clearInterval(intervalVideoInitialize); intervalVideoInitialize = setInterval(() => { // Hide moviePlayer to increase performance moviePlayer = document.getElementById('movie_player'); moviePlayerObserver.observe(moviePlayer, { childList: true }); if (moviePlayer == null) return; playerAPI = moviePlayer.parentNode; video = playerAPI.getElementsByTagName('video')[0]; if (video == null) return; video.pause(); video.volume = 0; video.addEventListener('playing', onVideoPlay); video.addEventListener('play', onVideoPlay); video.addEventListener('seeking', onVideoSeek); video.addEventListener('seeked', onVideoSeek); clearInterval(intervalVideoInitialize); }, 25); } function _updateScreenState(noAnimation, forceAnimation) { if (playerAPI == null || screen == null) return; let dockMode = (currentPage != PAGE_WATCH || window.scrollY > screen.offsetHeight / 2) ? DOCK_FLOATING : DOCK_DOCKED; // Cancel current animation if (currentDockMode != dockMode) { screen.removeEventListener('transitionend', onScreenTransitionEnd); currentAnimationCancelled = currentAnimation != null; currentAnimation = null; } let positionEnd = _loadScreenState(dockMode), positionStart; let isAnimating = forceAnimation || currentDockMode != dockMode || currentAnimation != null; currentDockMode = dockMode; if (isAnimating) { // Update animation states if (currentAnimation == null) { // Note: updating the height/width causes a stutter/redraw on the iframe. Therefore we do it at the beginning, where it is less noticeable positionStart = screen.getBoundingClientRect(); screen.style.height = positionEnd.height +'px'; screen.style.width = positionEnd.width +'px'; screen.style.transform = 'scale('+ (positionStart.width / positionEnd.width) +')'; } else { positionStart = currentAnimation.start; } currentAnimation = { end: positionEnd, start: positionStart, onScreenTransitionEnd: currentAnimation ? currentAnimation.onScreenTransitionEnd : null }; if (currentDockMode == DOCK_DOCKED) { iframe.style.boxShadow = 'initial'; } // Animate if (!noAnimation) { window.requestAnimationFrame(() => window.requestAnimationFrame(() => window.requestAnimationFrame(() => { // Abort animation if cancelled if (currentAnimation == null) return; screen.style.transition = '.2s ease-in-out'; if (currentAnimationCancelled) { screen.style.transform = ''; onScreenTransitionEnd(); } else { screen.style.transform ='translateX('+ (currentAnimation.end.left - currentAnimation.start.left) +'px)'+'translateY('+ (currentAnimation.end.top - currentAnimation.start.top) +'px)'; } // Prevent multiple addEventListener during a single transition (e.g. when scrolling, this method is spammed) if (!currentAnimation || !currentAnimation.onScreenTransitionEnd) { currentAnimation.onScreenTransitionEnd = onScreenTransitionEnd; screen.addEventListener('transitionend', onScreenTransitionEnd); } }))); } else { onScreenTransitionEnd(); } } else { // When the movie is docked at the top of the watch page screen.style.transition = ''; screen.style.height = positionEnd.height +'px'; screen.style.width = positionEnd.width +'px'; screen.style.top = positionEnd.top +'px'; screen.style.left = positionEnd.left +'px'; } } function _updateScreenVisibility(visible) { if (visible) { if (moviePlayer) moviePlayer.style.display = 'none'; screen.style.display = 'initial'; _updateScreenState(true); } else { if (moviePlayer) moviePlayer.style.display = 'initial'; screen.style.display = 'none'; } } function _calculateDefaultScreenState(dockMode) { let state = {}; state.height = playerAPI.offsetHeight / 2; state.width = playerAPI.offsetWidth / 2; // #YoutubeMaterialDesign - when switching to the Home page and such if (currentPage != PAGE_WATCH && (state.width == 0 || state.height == 0)) { let backupState = _loadScreenState(DOCK_DOCKED, PAGE_WATCH); state.height = backupState.height / 2; state.width = backupState.width / 2; } if (currentPage == PAGE_CHANNEL || currentPage == PAGE_HOME || currentPage == PAGE_SUBSCRIPTIONS) { state.top = (window.innerHeight - state.height - SCREEN_PADDING); state.left = (window.innerWidth - state.width - SCREEN_PADDING - SCREEN_PADDING); } if (currentPage == PAGE_SEARCH) { state.top = (window.innerHeight - state.height) / 2; state.left = (window.innerWidth - state.width - SCREEN_PADDING - SCREEN_PADDING); } if (currentPage == PAGE_WATCH) { if (dockMode == DOCK_FLOATING) { let sidebar = document.getElementById('watch7-sidebar') || document.getElementById('related'); // #YoutubeMaterialDesign state.top = (window.innerHeight - state.height) / 2; state.left = (sidebar.getBoundingClientRect().left - (state.width - sidebar.offsetWidth) / 2); } else { state = playerAPI.getBoundingClientRect(); } } return _saveScreenState(dockMode, state.height, state.width, state.left, state.top); } function _loadScreenState(dockMode, page) { let state = localStorage.screenState = localStorage.screenState || {}; state = state[page || currentPage] = state[page || currentPage] || {}; state = state[dockMode] = state[dockMode] || {}; // Load default state, if none was previously saved if ((currentPage == PAGE_WATCH && dockMode == DOCK_DOCKED) || state.height == null || state.width == null || state.left == null || state.top == null) state = _calculateDefaultScreenState(dockMode); // In case the screen is outside the screen, move it back inside if (dockMode == DOCK_FLOATING) { if (state.left + state.width + SCREEN_PADDING > window.innerWidth) state.left = window.innerWidth - state.width - SCREEN_PADDING; if (state.left < SCREEN_PADDING) state.left = SCREEN_PADDING; if (state.top + state.height + SCREEN_PADDING > window.innerHeight) state.top = window.innerHeight - state.height - SCREEN_PADDING; if (state.top < 100) state.top = 100; } return state; } function _saveScreenState(dockMode, height, width, left, top) { let state = localStorage.screenState = localStorage.screenState || {}; state = state[currentPage] = state[currentPage] || {}; state = state[dockMode] = state[dockMode] || {}; // Save to localStorage state.height = height; state.width = width; state.left = left; state.top = top; window.localStorage['rjgtav.youtube-mini-player'] = JSON.stringify(localStorage); return state; } function _getBoundingDocumentRect(e) { var box = e.getBoundingClientRect(); var body = document.body; var docEl = document.documentElement; var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop; var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft; var clientTop = docEl.clientTop || body.clientTop || 0; var clientLeft = docEl.clientLeft || body.clientLeft || 0; var height = e.offsetHeight; var width = e.offsetWidth; var top = box.top + scrollTop - clientTop; var left = box.left + scrollLeft - clientLeft; return { height, width, top, left }; } //--------------------------------------------------------------------- // Event Handlers //--------------------------------------------------------------------- // Thanks to http://stackoverflow.com/questions/34077641/how-to-detect-page-navigation-on-youtube-and-modify-html-before-page-is-rendered function onPageLoad() { let isFirstTime = iframe == null; // Parse query params queryParams = {}; let params = window.location.search.substring(1).split('&'); params.forEach((param) => { let pair = param.split('='); queryParams[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); }) // Parse current page currentPage = null; currentPage = currentPage == null && queryParams.v != null ? PAGE_WATCH : currentPage; currentPage = currentPage == null && queryParams.search_query != null ? PAGE_SEARCH : currentPage; currentPage = currentPage == null && window.location.href.indexOf('/channel/') != -1 ? PAGE_CHANNEL : currentPage; currentPage = currentPage == null && window.location.href.indexOf('/feeed/subscriptions') != -1 ? PAGE_SUBSCRIPTIONS : currentPage; currentPage = currentPage == null ? PAGE_HOME : currentPage; if (currentPage == PAGE_WATCH) { initializeIframe(); initializeVideo(); onPageTitle(); updateIframeSource(); // Make sure the mini player is visible (in case it was hidden by the user) _updateScreenVisibility(true); } document.body.appendChild(windowResizeListener); // Note: we need to append this before the window.onload window.requestAnimationFrame(() => { _updateScreenState(isFirstTime, true); }); } function onPageTitle(event) { let index = document.title.indexOf('- YouTube'); if (index > -1) screenTitle.innerText = document.title.substring(0, index); } function onIframeLoad(event) { // Workaround: we use intervals so this still runs when the tab isn't focused clearInterval(intervalIframeInitialize); intervalIframeInitialize = setInterval(() => { iframeVideo = iframe.contentWindow.document.getElementsByTagName('video')[0]; if (iframeVideo) { iframeVideo.currentTime = video.currentTime; iframeVideo.onended = onIframeVideoEnded; clearInterval(intervalIframeInitialize); } }, 25); } function onIframeVideoEnded(event) { if (currentPage != PAGE_WATCH) return; // In order to trigger Youtube's functionality (e.g. AutoPlay, Playlist), we need to make the original video trigger the 'ended' event // Therefore, we seek to the end and play it. See #onVideoSeek() for details video.currentTime = video.duration - 0.25; _updateScreenVisibility(false); } function onScreenButtonCloseClick(event) { iframeVideo.pause(); _updateScreenVisibility(false); } function onScreenMouseDown(event) { event.preventDefault(); isScreenMove = event.target == screenButtonMove; isScreenResize = event.target == screenButtonResizeBottomRight || event.target == screenButtonResizeTopRight; iframe.style.pointerEvents = 'none'; screen.classList.add('dragging'); screenButtonMove.removeEventListener('mousedown', onScreenMouseDown); screenButtonResizeBottomRight.removeEventListener('mousedown', onScreenMouseDown); screenButtonResizeTopRight.removeEventListener('mousedown', onScreenMouseDown); document.body.addEventListener('mouseup', onScreenMouseUp); document.body.addEventListener('mousemove', onScreenMouseMove); } function onScreenMouseUp(event) { event.preventDefault(); if (isScreenMove) { // Save _saveScreenState(currentDockMode, screen.offsetHeight, screen.offsetWidth, screen.offsetLeft, screen.offsetTop); } if (isScreenResize) { var currentSize = screen.getBoundingClientRect(); //screen.style.left =(screen.offsetLeft - (currentSize.width - screen.offsetWidth) / 2) +'px'; //screen.style.top = (screen.offsetTop - (currentSize.height - screen.offsetHeight) / 2) +'px'; screen.style.height = currentSize.height +'px'; screen.style.width = currentSize.width +'px'; screen.style.transform = ''; // Save _saveScreenState(currentDockMode, currentSize.height, currentSize.width, screen.offsetLeft, screen.offsetTop); } isScreenMove = isScreenResize = false; iframe.style.pointerEvents = 'initial'; screen.classList.remove('dragging'); document.body.removeEventListener('mouseup', onScreenMouseUp); document.body.removeEventListener('mousemove', onScreenMouseMove); screenButtonMove.addEventListener('mousedown', onScreenMouseDown); screenButtonResizeBottomRight.addEventListener('mousedown', onScreenMouseDown); screenButtonResizeTopRight.addEventListener('mousedown', onScreenMouseDown); } function onScreenMouseMove(event) { event.preventDefault(); if (isScreenMove) { screen.style.left = Math.min(window.innerWidth - screen.offsetWidth - SCREEN_PADDING, Math.max(SCREEN_PADDING, parseInt(screen.style.left) + event.movementX)) +'px'; screen.style.top = Math.min(window.innerHeight - screen.offsetHeight - SCREEN_PADDING, Math.max(148, parseInt(screen.style.top) + event.movementY)) +'px'; } if (isScreenResize) { var currentSize = screen.getBoundingClientRect(); var width = screen.offsetWidth; var newWidth = currentSize.width + event.movementX; screen.style.transform = 'scaleX('+ newWidth / width +') scaleY('+ newWidth / width +')'; } } function onScreenTitleClick(event) { var videoUrl = window.location.href.split('&t=')[0]; window.location.href = videoUrl + (iframeVideo ? ('&t='+ parseInt(iframeVideo.currentTime)) : ''); } function onScreenTransitionEnd(event) { if (event && event.propertyName != 'transform') return; screen.removeEventListener('transitionend', onScreenTransitionEnd); if (currentDockMode == DOCK_FLOATING) { iframe.style.boxShadow = '0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22)'; } screen.className = currentDockMode; screen.style.left = currentAnimation.end.left +'px'; screen.style.top = currentAnimation.end.top +'px'; screen.style.transform = ''; screen.style.transition = ''; currentAnimation = null; currentAnimationCancelled = false; } function onMoviePlayerObserve(mutators) { if (mutators[0].addedNodes.length > 0) initializeVideo(); } function onVideoPlay(event) { if (video == null) return; if (!(video.duration > 0 && Math.abs(video.currentTime - video.duration) <= 1)) // See #onIframeVideoEnded() for details video.pause(); } function onVideoSeek(event) { if (video == null) return; if (video.duration > 0 && Math.abs(video.currentTime - video.duration) <= 1) { // See #onIframeVideoEnded() for details video.play(); } else { video.pause(); if (iframeVideo != null) iframeVideo.currentTime = video.currentTime; // Make sure the mini player is visible (in case it was hidden by the user) _updateScreenVisibility(true); } } function onWindowResize() { window.requestAnimationFrame(() => { _updateScreenState(); }); } function onWindowScroll(event) { _updateScreenState(); }})();