::YouTube Gameplay Skipper::

Force skip 2 minutes for videos containing the words "gameplay", "longplay" or "no commentary" in the title.

  1. // ==UserScript==
  2. // @name ::YouTube Gameplay Skipper::
  3. // @namespace masterofobzene-gameplay skipper
  4. // @version 3.2
  5. // @description Force skip 2 minutes for videos containing the words "gameplay", "longplay" or "no commentary" in the title.
  6. // @author masterofobzene
  7. // @homepage https://github.com/masterofobzene/UserScriptRepo
  8. // @match https://www.youtube.com/*
  9. // @icon https://www.youtube.com/favicon.ico
  10. // @grant GM_addStyle
  11. // @license GNU GPLv3
  12. // @run-at document-start
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. const DEBUG = true;
  19. const SKIP_TIME = 120; // 2 minutes in seconds
  20. const TARGET_WORDS = ['gameplay', 'longplay', 'no commentary']; // Words to detect in title
  21. let currentVideoId = null;
  22. let skipPerformed = false;
  23.  
  24. function log(...args) {
  25. if(DEBUG) console.log('[YT Skip]', ...args);
  26. }
  27.  
  28. function getVideoTitle() {
  29. const selectors = [
  30. '#title h1 yt-formatted-string',
  31. 'h1.title.style-scope',
  32. '#container > h1',
  33. '[aria-label="Video title"]'
  34. ];
  35.  
  36. for(const selector of selectors) {
  37. const el = document.querySelector(selector);
  38. if(el && el.textContent) {
  39. log('Found title using selector:', selector);
  40. return el.textContent.toLowerCase();
  41. }
  42. }
  43. return null;
  44. }
  45.  
  46. function checkVideo() {
  47. const urlParams = new URLSearchParams(window.location.search);
  48. const newVideoId = urlParams.get('v');
  49.  
  50. if(newVideoId && newVideoId !== currentVideoId) {
  51. log('New video detected:', newVideoId);
  52. currentVideoId = newVideoId;
  53. skipPerformed = false;
  54. }
  55. }
  56.  
  57. function shouldSkip(title) {
  58. return TARGET_WORDS.some(word => title.includes(word));
  59. }
  60.  
  61. function performSkip() {
  62. const video = document.querySelector('video');
  63. if(!video) {
  64. log('Video element not found');
  65. return false;
  66. }
  67.  
  68. if(video.duration < SKIP_TIME) {
  69. log('Video too short:', video.duration);
  70. return false;
  71. }
  72.  
  73. if(video.currentTime < SKIP_TIME) {
  74. log(`Skipping from ${video.currentTime} to ${SKIP_TIME}`);
  75. video.currentTime = SKIP_TIME;
  76. return true;
  77. }
  78. return false;
  79. }
  80.  
  81. function mainChecker(attempt = 0) {
  82. if(skipPerformed) return;
  83.  
  84. checkVideo();
  85.  
  86. const title = getVideoTitle();
  87. if(!title) {
  88. if(attempt < 5) {
  89. log(`Title not found (attempt ${attempt}), retrying...`);
  90. setTimeout(() => mainChecker(attempt + 1), 500 * (attempt + 1));
  91. }
  92. return;
  93. }
  94.  
  95. if(shouldSkip(title)) {
  96. log(`"${TARGET_WORDS.join('", "')}" found in title:`, title);
  97.  
  98. const videoCheck = setInterval(() => {
  99. if(performSkip()) {
  100. log('Skip successful!');
  101. clearInterval(videoCheck);
  102. skipPerformed = true;
  103. }
  104. else if(attempt < 10) {
  105. log(`Skip attempt ${attempt}`);
  106. attempt++;
  107. }
  108. else {
  109. clearInterval(videoCheck);
  110. log('Max attempts reached');
  111. }
  112. }, 1000);
  113. }
  114. }
  115.  
  116. const observer = new MutationObserver(mutations => {
  117. if(document.querySelector('#movie_player, #player-container')) {
  118. mainChecker();
  119. }
  120. });
  121.  
  122. observer.observe(document, {
  123. subtree: true,
  124. childList: true,
  125. attributes: false,
  126. characterData: false
  127. });
  128.  
  129. document.addEventListener('yt-navigate-start', mainChecker);
  130. document.addEventListener('yt-page-data-updated', mainChecker);
  131. window.addEventListener('spfdone', mainChecker);
  132.  
  133. setTimeout(mainChecker, 1000);
  134. setTimeout(mainChecker, 3000);
  135. setTimeout(mainChecker, 5000);
  136.  
  137. GM_addStyle(`
  138. .ytp-autoskip-message {
  139. background: rgba(0,0,0,0.8) !important;
  140. color: #fff !important;
  141. padding: 8px !important;
  142. border-radius: 4px !important;
  143. font-size: 14px !important;
  144. }
  145. `);
  146. })();