diff --git a/beta/goodtube-embed.user.js b/beta/goodtube-embed.user.js
index 76759d0..80510e1 100644
--- a/beta/goodtube-embed.user.js
+++ b/beta/goodtube-embed.user.js
@@ -1,7 +1,7 @@
// ==UserScript==
// @name GoodTube
// @namespace http://tampermonkey.net/
-// @version 2.013
+// @version 3.000
// @description Removes 100% of Youtube ads.
// @author GoodTube
// @updateURL https://github.com/goodtube4u/goodtube/raw/refs/heads/main/goodtube.user.js
@@ -14,12 +14,13 @@
// @run-at document-start
// ==/UserScript==
+// This now automatically loads the latest version. This means that you will never need to update manually again :)
+// To view the full source code go here: https://github.com/goodtube4u/goodtube/blob/main/goodtube.js
+
(function () {
'use strict';
-
- /* Bypass CSP restrictions (introduced by the latest Chrome updates)
- ------------------------------------------------------------------------------------------ */
+ // Bypass CSP restrictions, introduced by the latest Chrome updates
if (window.trustedTypes && window.trustedTypes.createPolicy && !window.trustedTypes.defaultPolicy) {
window.trustedTypes.createPolicy('default', {
createHTML: string => string,
@@ -28,2678 +29,52 @@
});
}
-
- /* Welcome console message
- ------------------------------------------------------------------------------------------ */
- if (window.top === window.self) {
- console.log('\n==================================================\n ______ ________ __\n / ____/___ ____ ____/ /_ __/_ __/ /_ ___\n / / __/ __ \\/ __ \\/ __ / / / / / / / __ \\/ _ \\\n / /_/ / /_/ / /_/ / /_/ / / / / /_/ / /_/ / __/\n \\____/\\____/\\____/\\____/ /_/ \\____/_____/\\___/\n\n==================================================');
- console.log('[GoodTube] Initiating...');
- }
-
-
- /* Helper functions
- ------------------------------------------------------------------------------------------ */
- // Setup GET parameters
- function goodTube_helper_setupGetParams() {
- let getParams = {};
-
- document.location.search.replace(/\??(?:([^=]+)=([^&]*)&?)/g, function () {
- function decode(s) {
- return decodeURIComponent(s.split("+").join(" "));
- }
-
- getParams[decode(arguments[1])] = decode(arguments[2]);
- });
-
- // If we're on a playlist, but we don't have a video id in the URL - then get it from the page api
- if (typeof getParams['list'] !== 'undefined' && typeof getParams['v'] === 'undefined') {
- if (goodTube_page_api && typeof goodTube_page_api.getVideoData === 'function') {
- let videoData = goodTube_page_api.getVideoData();
-
- if (typeof videoData['video_id'] !== 'undefined' && videoData['video_id']) {
- getParams['v'] = videoData['video_id'];
- }
- }
- }
-
- return getParams;
- }
-
- // Set a cookie
- function goodTube_helper_setCookie(name, value) {
- // 399 days
- document.cookie = name + "=" + encodeURIComponent(value) + ";max-age=" + (399 * 24 * 60 * 60);
- }
-
- // Get a cookie
- function goodTube_helper_getCookie(name) {
- // Split the cookie string and get all individual name=value pairs in an array
- let cookies = document.cookie.split(";");
-
- // Loop through the array elements
- for (let i = 0; i < cookies.length; i++) {
- let cookie = cookies[i].split("=");
-
- // Removing whitespace at the beginning of the cookie name and compare it with the given string
- if (name == cookie[0].trim()) {
- // Decode the cookie value and return
- return decodeURIComponent(cookie[1]);
- }
- }
-
- // Return null if not found
- return null;
- }
-
- // Add CSS classes to show or hide elements / the Youtube player
- function goodTube_helper_showHide_init() {
- let style = document.createElement('style');
- style.textContent = `
- .goodTube_hidden {
- position: fixed !important;
- top: -9999px !important;
- left: -9999px !important;
- transform: scale(0) !important;
- pointer-events: none !important;
- }
-
- .goodTube_hiddenPlayer {
- position: relative;
- overflow: hidden;
- z-index: 1;
- }
-
- .goodTube_hiddenPlayer::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: #ffffff;
- z-index: 998;
- }
-
- html[dark] .goodTube_hiddenPlayer::before {
- background: #0f0f0f;
- }
- `;
- document.head.appendChild(style);
- }
-
- // Hide an element
- function goodTube_helper_hideElement(element) {
- if (element && !element.classList.contains('goodTube_hidden')) {
- element.classList.add('goodTube_hidden');
- }
- }
-
- // Show an element
- function goodTube_helper_showElement(element) {
- if (element && element.classList.contains('goodTube_hidden')) {
- element.classList.remove('goodTube_hidden');
- }
- }
-
- // Hide the Youtube player
- function goodTube_helper_hideYoutubePlayer(element) {
- // Add a wrapping div to help avoid detection
- if (!element.closest('.goodTube_hiddenPlayer')) {
- let parent = element.parentNode;
- let wrapper = document.createElement('div');
- wrapper.classList.add('goodTube_hiddenPlayer');
- parent.replaceChild(wrapper, element);
- wrapper.appendChild(element);
- }
- }
-
-
- /* Global variables
- ------------------------------------------------------------------------------------------ */
- // Stores the GET params
- let goodTube_getParams = goodTube_helper_setupGetParams();
-
- // Are we on mobile?
- let goodTube_mobile = false;
- if (window.location.href.indexOf('m.youtube') !== -1 || (typeof goodTube_getParams['mobile'] !== 'undefined' && goodTube_getParams['mobile'] === 'true')) {
- goodTube_mobile = true;
- }
-
- // A reference to our player's wrapper
- let goodTube_playerWrapper = false;
-
- // A reference to our player's iframe
- let goodTube_player = false;
-
- // The page api
- let goodTube_page_api = false;
-
- // The iframe api
- let goodTube_iframe_api = false;
-
- // Are we in picture in picture?
- let goodTube_pip = false;
-
- // Are shorts enabled
- let goodTube_shorts = 'false';
- if (window.top === window.self) {
- goodTube_shorts = goodTube_helper_getCookie('goodTube_shorts');
-
- if (!goodTube_shorts) {
- goodTube_helper_setCookie('goodTube_shorts', 'false');
- }
- }
-
- // Is autoplay turned on?
- let goodTube_autoplay = goodTube_helper_getCookie('goodTube_autoplay');
- if (window.top === window.self) {
- if (!goodTube_autoplay) {
- goodTube_helper_setCookie('goodTube_autoplay', 'true');
- goodTube_autoplay = 'true';
- }
- }
-
-
- /* Youtube functions
- ------------------------------------------------------------------------------------------ */
- // Hide ads, shorts, etc using CSS
- function goodTube_youtube_hideAdsShortsEtc() {
- let style = document.createElement('style');
- style.textContent = `
- .ytd-search ytd-shelf-renderer,
- ytd-reel-shelf-renderer,
- ytd-merch-shelf-renderer,
- ytd-action-companion-ad-renderer,
- ytd-display-ad-renderer,
-
- ytd-video-masthead-ad-advertiser-info-renderer,
- ytd-video-masthead-ad-primary-video-renderer,
- ytd-in-feed-ad-layout-renderer,
- ytd-ad-slot-renderer,
- ytd-statement-banner-renderer,
- ytd-banner-promo-renderer-background
- ytd-ad-slot-renderer,
- ytd-in-feed-ad-layout-renderer,
- ytd-engagement-panel-section-list-renderer:not(.ytd-popup-container):not([target-id='engagement-panel-clip-create']),
- ytd-compact-video-renderer:has(.goodTube_hidden),
- ytd-rich-item-renderer:has(> #content > ytd-ad-slot-renderer)
- .ytd-video-masthead-ad-v3-renderer,
- div#root.style-scope.ytd-display-ad-renderer.yt-simple-endpoint,
- div#sparkles-container.style-scope.ytd-promoted-sparkles-web-renderer,
- div#main-container.style-scope.ytd-promoted-video-renderer,
- div#player-ads.style-scope.ytd-watch-flexy,
- #clarify-box,
-
- ytm-rich-shelf-renderer,
- ytm-search ytm-shelf-renderer,
- ytm-button-renderer.icon-avatar_logged_out,
- ytm-companion-slot,
- ytm-reel-shelf-renderer,
- ytm-merch-shelf-renderer,
- ytm-action-companion-ad-renderer,
- ytm-display-ad-renderer,
- ytm-rich-section-renderer,
- ytm-video-masthead-ad-advertiser-info-renderer,
- ytm-video-masthead-ad-primary-video-renderer,
- ytm-in-feed-ad-layout-renderer,
- ytm-ad-slot-renderer,
- ytm-statement-banner-renderer,
- ytm-banner-promo-renderer-background
- ytm-ad-slot-renderer,
- ytm-in-feed-ad-layout-renderer,
- ytm-compact-video-renderer:has(.goodTube_hidden),
- ytm-rich-item-renderer:has(> #content > ytm-ad-slot-renderer)
- .ytm-video-masthead-ad-v3-renderer,
- div#root.style-scope.ytm-display-ad-renderer.yt-simple-endpoint,
- div#sparkles-container.style-scope.ytm-promoted-sparkles-web-renderer,
- div#main-container.style-scope.ytm-promoted-video-renderer,
- div#player-ads.style-scope.ytm-watch-flexy,
- ytd-compact-movie-renderer,
-
- yt-about-this-ad-renderer,
- masthead-ad,
- ad-slot-renderer,
- yt-mealbar-promo-renderer,
- statement-banner-style-type-compact,
- ytm-promoted-sparkles-web-renderer,
- tp-yt-iron-overlay-backdrop,
- #masthead-ad
- {
- display: none !important;
- }
-
- .style-scope[page-subtype='channels'] ytd-shelf-renderer,
- .style-scope[page-subtype='channels'] ytm-shelf-renderer {
- display: block !important;
- }
- `;
- document.head.appendChild(style);
-
- // Hide shorts if they're not enabled
- if (goodTube_shorts === 'false') {
- let shortsStyle = document.createElement('style');
- shortsStyle.textContent = `
- ytm-pivot-bar-item-renderer:has(> .pivot-shorts),
- ytd-rich-section-renderer {
- display: none !important;
- }
- `;
- document.head.appendChild(shortsStyle);
- }
-
- // Debug message
- console.log('[GoodTube] Ads removed');
- }
-
- // Hide shorts (realtime)
- function goodTube_youtube_hideShorts() {
- // Don't do this if shorts are enabled
- if (goodTube_shorts === 'true') {
- return;
- }
-
- // If we're on a channel page, don't hide shorts
- if (window.location.href.indexOf('@') !== -1) {
- return;
- }
-
- // Hide shorts links
- let shortsLinks = document.querySelectorAll('a:not(.goodTube_hidden)');
- shortsLinks.forEach((element) => {
- if (element.href.indexOf('shorts/') !== -1) {
- goodTube_helper_hideElement(element);
- goodTube_helper_hideElement(element.closest('ytd-video-renderer'));
- goodTube_helper_hideElement(element.closest('ytd-compact-video-renderer'));
- goodTube_helper_hideElement(element.closest('ytd-rich-grid-media'));
- }
- });
- }
-
- // Support timestamp links in comments
- function goodTube_youtube_timestampLinks() {
- // Links in video description and comments
- let timestampLinks = document.querySelectorAll('#description a, ytd-comments .yt-core-attributed-string a, ytm-expandable-video-description-body-renderer a, .comment-content a');
-
- // For each link
- timestampLinks.forEach((element) => {
- // Make sure we've not touched it yet, this stops doubling up on event listeners
- if (!element.classList.contains('goodTube_timestampLink') && element.getAttribute('href') && element.getAttribute('href').indexOf(goodTube_getParams['v']) !== -1 && element.getAttribute('href').indexOf('t=') !== -1) {
- element.classList.add('goodTube_timestampLink');
-
- // Add the event listener to send our player to the correct time
- element.addEventListener('click', function () {
- let bits = element.getAttribute('href').split('t=');
- if (typeof bits[1] !== 'undefined') {
- let time = bits[1].replace('s', '');
- goodTube_player_skipTo(time);
- }
- });
- }
- });
- }
-
- // Hide all Youtube players
- let goodTube_redirectHappened = false;
- function goodTube_youtube_hidePlayers() {
- // Don't do this if shorts are enabled
- if (goodTube_shorts === 'true' && window.location.href.indexOf('/shorts') !== -1) {
- return;
- }
-
- if (window.location.href.indexOf('/shorts') !== -1 && !goodTube_redirectHappened) {
- window.location.href = 'https://youtube.com';
- goodTube_redirectHappened = true;
- }
-
- // Hide the normal Youtube player
- let regularPlayers = document.querySelectorAll('#player');
- regularPlayers.forEach((element) => {
- goodTube_helper_hideYoutubePlayer(element);
- });
-
- // Remove the full screen and theater Youtube player
- let fullscreenPlayers = document.querySelectorAll('#full-bleed-container');
- fullscreenPlayers.forEach((element) => {
- goodTube_helper_hideYoutubePlayer(element);
- });
-
- // Hide the mobile controls
- let mobileControls = document.querySelectorAll('#player-control-container');
- mobileControls.forEach((element) => {
- goodTube_helper_hideElement(element);
- });
-
- // Hide the Youtube miniplayer
- let miniPlayers = document.querySelectorAll('ytd-miniplayer');
- miniPlayers.forEach((element) => {
- goodTube_helper_hideElement(element);
- });
- }
-
- // Mute, pause and skip ads on all Youtube videos
- function goodTube_youtube_mutePauseSkipAds() {
- // Don't do this if shorts are enabled
- if (goodTube_shorts === 'true' && window.location.href.indexOf('/shorts') !== -1) {
- return;
- }
-
- // Pause and mute all HTML videos on the page
- let youtubeVideos = document.querySelectorAll('video');
- youtubeVideos.forEach((element) => {
- // Don't touch the thumbnail hover player
- if (!element.closest('#inline-player')) {
- element.muted = true;
- element.volume = 0;
- element.pause();
- }
- });
- }
-
-
- /* Player functions
- ------------------------------------------------------------------------------------------ */
- // Init player
- let goodTube_proxyIframeLoaded = false;
- function goodTube_player_init() {
- // Get the page API
- goodTube_page_api = document.getElementById('movie_player');
-
- // Get the video data to check loading state
- let videoData = false;
- if (goodTube_page_api && typeof goodTube_page_api.getVideoData === 'function') {
- videoData = goodTube_page_api.getVideoData();
- }
-
- // Keep trying to get the frame API until it exists
- if (!videoData) {
- setTimeout(goodTube_player_init, 100);
- return;
- }
-
- // Add CSS styles for the player
- let style = document.createElement('style');
- style.textContent = `
- /* Desktop */
- #goodTube_playerWrapper {
- border-radius: 12px;
- background: transparent;
- position: absolute;
- top: 0;
- left: 0;
- z-index: 999;
- overflow: hidden;
- }
-
- html[dark] #goodTube_playerWrapper {
- background: #0f0f0f;
- }
-
- /* Mobile */
- #goodTube_playerWrapper.goodTube_mobile {
- position: fixed;
- background: #000000;
- border-radius: 0;
- z-index: 3;
- }
-
- /* Theater mode */
- #goodTube_playerWrapper.goodTube_theater {
- background: #000000;
- border-radius: 0;
- }
- `;
- document.head.appendChild(style);
-
- // Setup player layout
- let playerWrapper = document.createElement('div');
- playerWrapper.id = 'goodTube_playerWrapper';
-
- // Add a mobile class
- if (goodTube_mobile) {
- playerWrapper.classList.add('goodTube_mobile');
- }
-
- // Add player to the page
- document.body.appendChild(playerWrapper);
-
- // Add video iframe embed (via proxy iframe)
- playerWrapper.innerHTML = `
-
- `;
-
- // Expose the player and wrapper globally
- goodTube_playerWrapper = document.querySelector('#goodTube_playerWrapper');
- goodTube_player = goodTube_playerWrapper.querySelector('iframe');
-
- // Setup player dynamic positioning and sizing
- goodTube_player_positionAndSize();
-
- // Run the actions
- goodTube_actions();
- }
-
- // Position and size the player
- let goodTube_loadTimeout = setTimeout(() => {}, 0);
- function goodTube_player_positionAndSize() {
- // If we're viewing a video
- if (window.location.href.indexOf('.com/watch') !== -1) {
- // Show the GoodTube player
- goodTube_helper_showElement(goodTube_playerWrapper);
-
- // This is used to position and size the player
- let positionElement = false;
-
- // Desktop
- if (!goodTube_mobile) {
- // Theater mode
- if (document.querySelector('ytd-watch-flexy[theater]')) {
- positionElement = document.getElementById('full-bleed-container');
-
- if (!goodTube_playerWrapper.classList.contains('goodTube_theater')) {
- goodTube_playerWrapper.classList.add('goodTube_theater');
- }
- }
- // Regular mode
- else {
- positionElement = document.getElementById('player');
-
- if (goodTube_playerWrapper.classList.contains('goodTube_theater')) {
- goodTube_playerWrapper.classList.remove('goodTube_theater');
- }
- }
-
- // Position the player
- if (positionElement && positionElement.offsetHeight > 0) {
- // Our wrapper has "position: absolute" so take into account the window scroll
- let rect = positionElement.getBoundingClientRect();
- goodTube_playerWrapper.style.top = (rect.top + window.scrollY) + 'px';
- goodTube_playerWrapper.style.left = (rect.left + window.scrollX) + 'px';
-
- // Match the size of the position element
- goodTube_playerWrapper.style.width = positionElement.offsetWidth + 'px';
- goodTube_playerWrapper.style.height = positionElement.offsetHeight + 'px';
- }
- }
-
- // Mobile
- else {
- positionElement = document.getElementById('player');
-
- // Position the player
- if (positionElement && positionElement.offsetHeight > 0) {
- // Our wrapper has "position: absolute" so don't take into account the window scroll
- let rect = positionElement.getBoundingClientRect();
- goodTube_playerWrapper.style.top = rect.top + 'px';
- goodTube_playerWrapper.style.left = rect.left + 'px';
-
- // Match the size of the position element
- goodTube_playerWrapper.style.width = positionElement.offsetWidth + 'px';
- goodTube_playerWrapper.style.height = positionElement.offsetHeight + 'px';
- }
- }
- }
-
- // Call this function again on next draw frame
- window.requestAnimationFrame(function () {
- goodTube_player_positionAndSize();
- });
- }
-
- // Load a video
- function goodTube_player_load() {
- // Pause the video first (this helps to prevent audio flashes)
- goodTube_player_pause();
-
- // Make sure the proxy iframe has loaded
- if (!goodTube_proxyIframeLoaded) {
- clearTimeout(goodTube_loadTimeout);
- goodTube_loadTimeout = setTimeout(goodTube_player_load, 100);
- return;
- }
-
- // If we're not in picture in picture mode
- if (!goodTube_pip) {
- // Ensure we're still viewing a video (sometimes you can browse to another page before the iframe loads)
- if (window.location.href.indexOf('.com/watch') !== -1) {
- // If a restore time exists, skip to it
- if (typeof goodTube_getParams['t'] !== 'undefined') {
- goodTube_player_skipTo(goodTube_getParams['t'].replace('s', ''));
- }
- }
- // If we're not still viewing a video
- else {
- // Clear and hide the player
- goodTube_player_clear();
- }
-
-
- // Set the video source
- // This also tells the embed if it's mobile or not
- let mobileText = 'false';
- if (goodTube_mobile) {
- mobileText = 'true';
- }
- goodTube_player.contentWindow.postMessage('goodTube_src_https://www.youtube.com/embed/' + goodTube_getParams['v'] + '?autoplay=1&mobile=' + mobileText + '&goodTube_autoplay=' + goodTube_autoplay, '*');
- }
- // If we are in picture in picture mode
- else {
- // Load the video via the iframe api
- goodTube_player.contentWindow.postMessage('goodTube_load_' + goodTube_getParams['v'], '*');
- }
-
-
- // Show the player
- goodTube_helper_showElement(goodTube_playerWrapper);
- }
-
- // Clear and hide the player
- function goodTube_player_clear() {
- // Stop the video via the iframe api (but not if we're in picture in picture)
- if (!goodTube_pip) {
- goodTube_player.contentWindow.postMessage('goodTube_stopVideo', '*');
- }
-
- // Hide the player
- goodTube_helper_hideElement(goodTube_playerWrapper);
- }
-
- // Skip to time
- function goodTube_player_skipTo(time) {
- goodTube_player.contentWindow.postMessage('goodTube_skipTo_' + time, '*');
- }
-
- // Pause
- function goodTube_player_pause() {
- goodTube_player.contentWindow.postMessage('goodTube_pause', '*');
- }
-
- // Play
- function goodTube_player_play() {
- goodTube_player.contentWindow.postMessage('goodTube_play', '*');
- }
-
-
- /* Keyboard shortcuts
- ------------------------------------------------------------------------------------------ */
- // Add keyboard shortcuts
- function goodTube_shortcuts_init() {
- document.addEventListener('keydown', function (event) {
- // Don't do anything if we're holding control
- if (event.ctrlKey) {
- return;
- }
-
- // Get the key pressed in lower case
- let keyPressed = event.key.toLowerCase();
-
- // If we're not focused on a HTML form element
- let focusedElement = event.srcElement;
- let focusedElement_tag = false;
- let focusedElement_id = false;
- if (focusedElement) {
- if (typeof focusedElement.nodeName !== 'undefined') {
- focusedElement_tag = focusedElement.nodeName.toLowerCase();
- }
-
- if (typeof focusedElement.getAttribute !== 'undefined') {
- focusedElement_id = focusedElement.getAttribute('id');
- }
- }
-
- if (
- !focusedElement ||
- (
- focusedElement_tag.indexOf('input') === -1 &&
- focusedElement_tag.indexOf('label') === -1 &&
- focusedElement_tag.indexOf('select') === -1 &&
- focusedElement_tag.indexOf('textarea') === -1 &&
- focusedElement_tag.indexOf('fieldset') === -1 &&
- focusedElement_tag.indexOf('legend') === -1 &&
- focusedElement_tag.indexOf('datalist') === -1 &&
- focusedElement_tag.indexOf('output') === -1 &&
- focusedElement_tag.indexOf('option') === -1 &&
- focusedElement_tag.indexOf('optgroup') === -1 &&
- focusedElement_id !== 'contenteditable-root'
- )
- ) {
- if (
- // Fullscreen
- keyPressed === 'f' ||
- // Speed up playback
- keyPressed === '>' ||
- // Slow down playback
- keyPressed === '<'
- ) {
- event.preventDefault();
- event.stopImmediatePropagation();
-
- // Pass the keyboard shortcut to the iframe
- goodTube_player.contentWindow.postMessage('goodTube_shortcut_' + keyPressed, '*');
- }
-
- // If we're not holding down the shift key
- if (!event.shiftKey) {
- // If we're focused on the video element
- if (focusedElement && typeof focusedElement.closest !== 'undefined' && focusedElement.closest('#goodTube_player')) {
- // Theater mode (focus the body, this makes the default youtube shortcut work)
- if (keyPressed === 't') {
- document.querySelector('body').focus();
- }
- }
-
- if (
- // Prev frame (24fps calculation)
- keyPressed === ',' ||
- // Next frame (24fps calculation)
- keyPressed === '.' ||
- // Prev 5 seconds
- keyPressed === 'arrowleft' ||
- // Next 5 seconds
- keyPressed === 'arrowright' ||
- // Toggle play/pause
- keyPressed === ' ' || keyPressed === 'k' ||
- // Toggle mute
- keyPressed === 'm' ||
- // Toggle fullscreen
- keyPressed === 'f' ||
- // Prev 10 seconds
- keyPressed === 'j' ||
- // Next 10 seconds
- keyPressed === 'l' ||
- // Start of video
- keyPressed === 'home' ||
- // End of video
- keyPressed === 'end' ||
- // Skip to percentage
- keyPressed === '0' ||
- keyPressed === '1' ||
- keyPressed === '2' ||
- keyPressed === '3' ||
- keyPressed === '4' ||
- keyPressed === '5' ||
- keyPressed === '6' ||
- keyPressed === '7' ||
- keyPressed === '8' ||
- keyPressed === '9'
- ) {
- event.preventDefault();
- event.stopImmediatePropagation();
-
- // Pass the keyboard shortcut to the iframe
- goodTube_player.contentWindow.postMessage('goodTube_shortcut_' + keyPressed, '*');
- }
-
- // Toggle picture in picture
- if (keyPressed === 'i') {
- event.preventDefault();
- event.stopImmediatePropagation();
-
- // Tell the iframe to toggle pip
- goodTube_player.contentWindow.postMessage('goodTube_pip', '*');
- }
- }
- }
- }, true);
- }
-
- // Trigger a keyboard shortcut
- function goodTube_shortcuts_trigger(shortcut) {
- // Focus the body first
- document.querySelector('body').focus();
-
- // Setup the keyboard shortcut
- let theKey = false;
- let keyCode = false;
- let shiftKey = false;
-
- if (shortcut === 'theater') {
- theKey = 't';
- keyCode = 84;
- shiftKey = false;
- }
- else {
- return;
- }
-
- // Trigger the keyboard shortcut
- let e = false;
- e = new window.KeyboardEvent('focus', {
- bubbles: true,
- key: theKey,
- keyCode: keyCode,
- shiftKey: shiftKey,
- charCode: 0,
- });
- document.dispatchEvent(e);
-
- e = new window.KeyboardEvent('keydown', {
- bubbles: true,
- key: theKey,
- keyCode: keyCode,
- shiftKey: shiftKey,
- charCode: 0,
- });
- document.dispatchEvent(e);
-
- e = new window.KeyboardEvent('beforeinput', {
- bubbles: true,
- key: theKey,
- keyCode: keyCode,
- shiftKey: shiftKey,
- charCode: 0,
- });
- document.dispatchEvent(e);
-
- e = new window.KeyboardEvent('keypress', {
- bubbles: true,
- key: theKey,
- keyCode: keyCode,
- shiftKey: shiftKey,
- charCode: 0,
- });
- document.dispatchEvent(e);
-
- e = new window.KeyboardEvent('input', {
- bubbles: true,
- key: theKey,
- keyCode: keyCode,
- shiftKey: shiftKey,
- charCode: 0,
- });
- document.dispatchEvent(e);
-
- e = new window.KeyboardEvent('change', {
- bubbles: true,
- key: theKey,
- keyCode: keyCode,
- shiftKey: shiftKey,
- charCode: 0,
- });
- document.dispatchEvent(e);
-
- e = new window.KeyboardEvent('keyup', {
- bubbles: true,
- key: theKey,
- keyCode: keyCode,
- shiftKey: shiftKey,
- charCode: 0,
- });
- document.dispatchEvent(e);
- }
-
-
- /* Navigation (playlists and autoplay)
- ------------------------------------------------------------------------------------------ */
- // Have we opened the playlist (mobile)
- let goodTube_nav_clickedPlaylistOpen = false;
-
- // A reference to the previous video
- let goodTube_nav_prevVideo = [];
-
- // Are the next and previous buttons enabled?
- let goodTube_nav_nextButton = true;
- let goodTube_nav_prevButton = false;
-
- // Generate playlist links (these are internally used to help us navigate through playlists and use autoplay)
- function goodTube_nav_generatePlaylistLinks() {
- // If we're not viewing a playlist, just return.
- if (typeof goodTube_getParams['i'] === 'undefined' && typeof goodTube_getParams['index'] === 'undefined' && typeof goodTube_getParams['list'] === 'undefined') {
- return;
- }
-
- // Get the playlist items
- let playlistLinks = false;
- let playlistTitles = false;
-
- // Desktop
- if (!goodTube_mobile) {
- playlistLinks = document.querySelectorAll('#playlist-items > a');
- playlistTitles = document.querySelectorAll('#playlist-items #video-title');
- }
- // Mobile
- else {
- playlistLinks = document.querySelectorAll('ytm-playlist-panel-renderer a.compact-media-item-image');
- playlistTitles = document.querySelectorAll('ytm-playlist-panel-renderer .compact-media-item-headline span');
- }
-
- // If the playlist links exist
- if (playlistLinks.length > 0) {
- // Target the playlist container
- let playlistContainer = document.getElementById('goodTube_playlistContainer');
-
- // Add the playlist container if we don't have it
- if (!playlistContainer) {
- playlistContainer = document.createElement('div');
- playlistContainer.setAttribute('id', 'goodTube_playlistContainer');
- playlistContainer.style.display = 'none';
- document.body.appendChild(playlistContainer);
- }
-
- // Empty the playlist container
- playlistContainer.innerHTML = '';
-
- // For each playlist item
- let i = 0;
- playlistLinks.forEach((playlistItem) => {
- // Create a link element
- let playlistItemElement = document.createElement('a');
-
- // Set the href
- playlistItemElement.href = playlistItem.href;
-
- // Set the title
- playlistItemElement.innerHTML = playlistTitles[i].innerHTML.trim();
-
- // If we're currently on this item, set the selected class
- if (playlistItem.href.indexOf('v=' + goodTube_getParams['v']) !== -1) {
- playlistItemElement.classList.add('goodTube_selected');
- }
-
- // Add the item to the playlist container
- playlistContainer.appendChild(playlistItemElement);
-
- i++;
- });
- }
- }
-
- // Play the previous video
- function goodTube_nav_prev() {
- // Check if we clicked a playlist item
- let clickedPlaylistItem = false;
-
- // If we are viewing a playlist
- if (typeof goodTube_getParams['i'] !== 'undefined' || typeof goodTube_getParams['index'] !== 'undefined' || typeof goodTube_getParams['list'] !== 'undefined') {
- // Get the playlist items
- let playlistItems = document.querySelectorAll('#goodTube_playlistContainer a');
-
- // For each playlist item
- let clickNext = false;
-
- // Loop in reverse
- for (let i = (playlistItems.length - 1); i >= 0; i--) {
- let playlistItem = playlistItems[i];
-
- if (clickNext) {
- // Find the matching playlist item on the page and click it
- let bits = playlistItem.href.split('/watch');
- let findUrl = '/watch' + bits[1];
-
- // Desktop
- if (!goodTube_mobile) {
- clickedPlaylistItem = true;
- document.querySelector('#playlist-items > a[href="' + findUrl + '"]')?.click();
- }
- // Mobile
- else {
- clickedPlaylistItem = true;
- document.querySelector('ytm-playlist-panel-renderer a.compact-media-item-image[href="' + findUrl + '"]')?.click();
- }
-
- if (clickedPlaylistItem) {
- clickedPlaylistItem = true;
-
- // Double check that the playlist is open, if not - open it.
- let playlistContainer = document.querySelector('ytm-playlist-panel-renderer');
- if (!playlistContainer) {
- let openButton = document.querySelector('ytm-playlist-panel-entry-point');
-
- if (openButton && !goodTube_nav_clickedPlaylistOpen) {
- goodTube_nav_clickedPlaylistOpen = true;
- openButton.click();
- setTimeout(goodTube_nav_prev, 500);
- }
-
- return;
- }
-
- goodTube_nav_clickedPlaylistOpen = false;
-
- // Click the matching playlist item
- document.querySelector('ytm-playlist-panel-renderer a.compact-media-item-image[href="' + findUrl + '"]')?.click();
- }
- }
-
- if (playlistItem.classList.contains('goodTube_selected')) {
- clickNext = true;
- }
- else {
- clickNext = false;
- }
- }
- }
-
- // If we didn't click a playlist item, play previous video (if it exists in our history)
- if (!clickedPlaylistItem && goodTube_nav_prevVideo[goodTube_nav_prevVideo.length - 2] && goodTube_nav_prevVideo[goodTube_nav_prevVideo.length - 2] !== window.location.href) {
+ // Define load function
+ function goodTube_load(loadAttempts) {
+ // If it's the first load attempt
+ if (loadAttempts === 0) {
// Debug message
- console.log('[GoodTube] Playing previous video...');
-
- // Go back to the previous video
- goodTube_helper_setCookie('goodTube_previous', 'true');
- window.history.go(-1);
- }
- }
-
- // Play the next video
- function goodTube_nav_next(pressedButton = false) {
- // Check if we clicked a playlist item
- let clickedPlaylistItem = false;
-
- // If we are viewing a playlist
- if (typeof goodTube_getParams['i'] !== 'undefined' || typeof goodTube_getParams['index'] !== 'undefined' || typeof goodTube_getParams['list'] !== 'undefined') {
- // Get the playlist items
- let playlistItems = document.querySelectorAll('#goodTube_playlistContainer a');
-
- // For each playlist item
- let clickNext = false;
-
- playlistItems.forEach((playlistItem) => {
- if (clickNext) {
- // Find the matching playlist item on the page and click it
- let bits = playlistItem.href.split('/watch');
- let findUrl = '/watch' + bits[1];
-
- // Desktop
- if (!goodTube_mobile) {
- clickedPlaylistItem = true;
- document.querySelector('#playlist-items > a[href="' + findUrl + '"]')?.click();
- }
- // Mobile
- else {
- clickedPlaylistItem = true;
-
- // Double check that the playlist is open, if not - open it.
- let playlistContainer = document.querySelector('ytm-playlist-panel-renderer');
- if (!playlistContainer) {
- let openButton = document.querySelector('ytm-playlist-panel-entry-point');
-
- if (openButton && !goodTube_nav_clickedPlaylistOpen) {
- goodTube_nav_clickedPlaylistOpen = true;
- openButton.click();
- setTimeout(goodTube_nav_next, 500);
- }
-
- return;
- }
-
- goodTube_nav_clickedPlaylistOpen = false;
-
- // Click the matching playlist item
- document.querySelector('ytm-playlist-panel-renderer a.compact-media-item-image[href="' + findUrl + '"]')?.click();
- }
-
- if (clickedPlaylistItem) {
- // Debug message
- console.log('[GoodTube] Playing next video in playlist...');
- }
- }
-
- if (playlistItem.classList.contains('goodTube_selected')) {
- clickNext = true;
- }
- else {
- clickNext = false;
- }
- });
- }
-
- // If we didn't click a playlist item, autoplay next video (only if they pressed the next button or autoplay is on)
- if (!clickedPlaylistItem && (goodTube_autoplay === 'true' || pressedButton)) {
- // Re fetch the page API (this fixes issues on mobile)
- goodTube_page_api = document.getElementById('movie_player');
-
- // Make sure it exists
- if (goodTube_page_api && typeof goodTube_page_api.nextVideo === 'function') {
- // Play the next video
- goodTube_page_api.nextVideo();
- }
-
- // Debug message
- console.log('[GoodTube] Autoplaying next video...');
- }
- }
-
- // Setup the previous button history
- function goodTube_nav_setupPrevHistory() {
- // If we've hit the previous button
- if (goodTube_helper_getCookie('goodTube_previous') === 'true') {
- // Remove the last item from the previous video array
- goodTube_nav_prevVideo.pop();
-
- goodTube_helper_setCookie('goodTube_previous', 'false');
- }
- // Otherwise it's a normal video load
- else {
- // Add this page to the previous video array
- goodTube_nav_prevVideo.push(window.location.href);
- }
- }
-
- // Show or hide the next and previous button
- function goodTube_nav_showHideNextPrevButtons() {
- let prevButton = false;
- let nextButton = true;
-
- // Don't show next / prev unless we're viewing a video
- if (typeof goodTube_getParams['v'] === 'undefined') {
- prevButton = false;
- nextButton = false;
- }
- // For the regular player
- else {
- // If we're viewing a playlist
- if (typeof goodTube_getParams['i'] !== 'undefined' || typeof goodTube_getParams['index'] !== 'undefined' || typeof goodTube_getParams['list'] !== 'undefined') {
- let playlist = document.querySelectorAll('#goodTube_playlistContainer a');
-
- if (!playlist || !playlist.length) {
- return;
- }
-
- // If the first video is NOT selected
- if (!playlist[0].classList.contains('goodTube_selected')) {
- // Enable the previous button
- prevButton = true;
- }
- }
- // Otherwise we're not in a playlist, so if a previous video exists
- else if (goodTube_nav_prevVideo[goodTube_nav_prevVideo.length - 2] && goodTube_nav_prevVideo[goodTube_nav_prevVideo.length - 2] !== window.location.href) {
- // Enable the previous button
- prevButton = true;
+ if (window.top === window.self) {
+ console.log('\n==================================================\n ______ ________ __\n / ____/___ ____ ____/ /_ __/_ __/ /_ ___\n / / __/ __ \\/ __ \\/ __ / / / / / / / __ \\/ _ \\\n / /_/ / /_/ / /_/ / /_/ / / / / /_/ / /_/ / __/\n \\____/\\____/\\____/\\____/ /_/ \\____/_____/\\___/\n\n==================================================');
+ console.log('[GoodTube] Initiating...');
}
}
- // Tell the iframe to show or hide the previous button
- if (prevButton) {
- goodTube_nav_prevButton = true;
- goodTube_player.contentWindow.postMessage('goodTube_prevButton_show', '*');
- }
- else {
- goodTube_nav_prevButton = false;
- goodTube_player.contentWindow.postMessage('goodTube_prevButton_hide', '*');
- }
+ // Only re-attempt to load the GoodTube 10 times
+ if (loadAttempts >= 9) {
+ if (window.top === window.self) {
+ // Debug message
+ console.log('[GoodTube] GoodTube could not be loaded');
- // Tell the iframe to show or hide the next button
- if (nextButton) {
- goodTube_nav_nextButton = true;
- goodTube_player.contentWindow.postMessage('goodTube_nextButton_show', '*');
- }
- else {
- goodTube_nav_nextButton = false;
- goodTube_player.contentWindow.postMessage('goodTube_nextButton_hide', '*');
- }
- }
+ // Show prompt
+ alert('GoodTube could not be loaded! Please refresh the page to try again.');
+ }
-
- /* Usage stats
- ------------------------------------------------------------------------------------------ */
- // Don't worry everyone - this is just a counter that totals unique users / how many videos were played with GoodTube.
- // It's only in here so I can have some fun and see how many people use this thing I made - no private info is tracked.
-
- // Count unique users
- function goodTube_stats_user() {
- // If there's no cookie
- if (!goodTube_helper_getCookie('goodTube_uniqueUserStat')) {
- // Count a unique user
- fetch('\x68\x74\x74\x70\x73\x3a\x2f\x2f\x6a\x61\x6d\x65\x6e\x6c\x79\x6e\x64\x6f\x6e\x2e\x63\x6f\x6d\x2f\x5f\x6f\x74\x68\x65\x72\x2f\x73\x74\x61\x74\x73\x2f\x75\x73\x65\x72\x2e\x70\x68\x70');
-
- // Set a cookie to only count unique users once
- goodTube_helper_setCookie('goodTube_uniqueUserStat', 'true');
- }
- }
-
- // Count videos
- function goodTube_stats_video() {
- fetch('\x68\x74\x74\x70\x73\x3a\x2f\x2f\x6a\x61\x6d\x65\x6e\x6c\x79\x6e\x64\x6f\x6e\x2e\x63\x6f\x6d\x2f\x5f\x6f\x74\x68\x65\x72\x2f\x73\x74\x61\x74\x73\x2f\x76\x69\x64\x65\x6f\x2e\x70\x68\x70');
- }
-
-
- /* Core functions
- ------------------------------------------------------------------------------------------ */
- // Init
- function goodTube_init() {
- /* Disable Youtube
- -------------------------------------------------- */
- // Mute, pause and skip ads
- goodTube_youtube_mutePauseSkipAds();
- setInterval(goodTube_youtube_mutePauseSkipAds, 1);
-
- // Add CSS classes to hide elements (without Youtube knowing)
- goodTube_helper_showHide_init();
-
- // Hide the youtube players
- goodTube_youtube_hidePlayers();
- setInterval(goodTube_youtube_hidePlayers, 100);
-
- // Add CSS to hide ads, shorts, etc
- goodTube_youtube_hideAdsShortsEtc();
-
- // Hide shorts that popup as you use the site (like video results)
- setInterval(goodTube_youtube_hideShorts, 100);
-
-
- /* Load GoodTube
- -------------------------------------------------- */
- // Init our player (after DOM is loaded)
- document.addEventListener('DOMContentLoaded', goodTube_player_init);
-
- // Also check if the DOM is already loaded, as if it is, the above event listener will not trigger
- if (document.readyState === 'interactive' || document.readyState === 'complete') {
- goodTube_player_init();
- }
-
- // Usage stats
- goodTube_stats_user();
-
- // Keyboard shortcuts (desktop only)
- if (!goodTube_mobile) {
- goodTube_shortcuts_init();
- }
-
- // Listen for messages from the iframe
- window.addEventListener('message', goodTube_receiveMessage);
-
- // Init the menu
- document.addEventListener('DOMContentLoaded', goodTube_menu);
-
- // Also check if the DOM is already loaded, as if it is, the above event listener will not trigger
- if (document.readyState === 'interactive' || document.readyState === 'complete') {
- goodTube_menu();
- }
- }
-
- // Listen for messages from the iframe
- function goodTube_receiveMessage(event) {
- // Make sure some data exists
- if (typeof event.data !== 'string') {
return;
}
- // Proxy iframe has loaded
- else if (event.data === 'goodTube_proxyIframe_loaded') {
- goodTube_proxyIframeLoaded = true;
- }
-
- // Player iframe has loaded
- else if (event.data === 'goodTube_playerIframe_loaded') {
- goodTube_player.style.display = 'block';
- }
-
- // Picture in picture
- if (event.data.indexOf('goodTube_pip_') !== -1) {
- let pipEnabled = event.data.replace('goodTube_pip_', '');
-
- if (pipEnabled === 'true') {
- goodTube_pip = true;
- }
- else {
- goodTube_pip = false;
-
- // If we're not viewing a video
- if (typeof goodTube_getParams['v'] === 'undefined') {
- // Clear the player
- goodTube_player_clear();
- }
- }
- }
-
- // Previous video
- else if (event.data === 'goodTube_prevVideo') {
- goodTube_nav_prev();
- }
-
- // Next video
- else if (event.data === 'goodTube_nextVideo') {
- goodTube_nav_next();
- }
-
- // Theater mode (toggle)
- else if (event.data === 'goodTube_theater') {
- goodTube_shortcuts_trigger('theater');
- }
-
- // Autoplay
- else if (event.data === 'goodTube_autoplay_false') {
- goodTube_helper_setCookie('goodTube_autoplay', 'false');
- goodTube_autoplay = 'false';
- }
- else if (event.data === 'goodTube_autoplay_true') {
- goodTube_helper_setCookie('goodTube_autoplay', 'true');
- goodTube_autoplay = 'true';
- }
- }
-
- // Actions
- let goodTube_previousUrl = false;
- function goodTube_actions() {
- // Get the previous and current URL
-
- // Remove hashes, these mess with things sometimes
- // Also remove "index="
- let previousUrl = goodTube_previousUrl;
- if (previousUrl) {
- previousUrl = previousUrl.split('#')[0];
- previousUrl = previousUrl.split('index=')[0];
- }
-
- let currentUrl = window.location.href;
- if (currentUrl) {
- currentUrl = currentUrl.split('#')[0];
- currentUrl = currentUrl.split('index=')[0];
- }
-
- // If the URL has changed (this will always fire on first page load)
- if (previousUrl !== currentUrl) {
- // The URL has changed, so setup our player
- // ----------------------------------------------------------------------------------------------------
- // Setup GET parameters
- goodTube_getParams = goodTube_helper_setupGetParams();
-
- // If we're viewing a video
- if (window.location.href.indexOf('.com/watch') !== -1) {
- // Setup the previous button history
- goodTube_nav_setupPrevHistory();
-
- // Load the video
- goodTube_player_load();
-
- // Usage stats
- goodTube_stats_video();
- }
- // Otherwise if we're not viewing a video
- else {
- // Clear the player
- goodTube_player_clear();
- }
-
- // Set the previous URL (which pauses this function until the URL changes again)
- goodTube_previousUrl = window.location.href;
- }
-
- // Generate the playlist links (used to navigate playlists correctly)
- goodTube_nav_generatePlaylistLinks();
-
- // Show or hide the next / prev buttons
- goodTube_nav_showHideNextPrevButtons();
-
- // Support timestamp links
- goodTube_youtube_timestampLinks();
-
- // Run actions again in 100ms to loop this function
- setTimeout(goodTube_actions, 100);
- }
-
- // Init menu
- function goodTube_menu() {
- // Create the menu container
- let menuContainer = document.createElement('div');
-
- // Add the menu container to the page
- document.body.appendChild(menuContainer);
-
- // Configure the settings to show their actual values
- let shortsEnabled = ' checked';
- if (goodTube_shorts === 'true') {
- shortsEnabled = '';
- }
-
- // Add content to the menu container
- menuContainer.innerHTML = `
-
-
-
-
- ✖
-
-
-
-