Greasy Fork 支持简体中文。

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