easystrapper (for youtube)

Optimizes YouTube loading by preloading thumbnails, lazy loading content, and postponing thumbnail loading during playback.

  1. // ==UserScript==
  2. // @name easystrapper (for youtube)
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-08-13
  5. // @description Optimizes YouTube loading by preloading thumbnails, lazy loading content, and postponing thumbnail loading during playback.
  6. // @license MIT
  7. // @author ondry4k
  8. // @match https://www.youtube.com/
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. function lazyLoadThumbnails() {
  17. const config = {
  18. rootMargin: '200px 0px',
  19. threshold: 0.01
  20. };
  21.  
  22. const observer = new IntersectionObserver((entries, self) => {
  23. entries.forEach(entry => {
  24. if (entry.isIntersecting) {
  25. const img = entry.target;
  26. const dataSrc = img.getAttribute('data-thumb') || img.getAttribute('src');
  27. if (dataSrc && dataSrc !== img.src) {
  28. img.src = dataSrc;
  29. }
  30. self.unobserve(entry.target);
  31. console.log("easystrapper: Thumbnail loaded.");
  32. }
  33. });
  34. }, config);
  35.  
  36. document.querySelectorAll('img[src*="default.jpg"]').forEach(img => {
  37. observer.observe(img);
  38. console.log("easystrapper: Observer attached to thumbnail.");
  39. });
  40. }
  41.  
  42. function preloadInitialThumbnails() {
  43. document.querySelectorAll('img[src*="default.jpg"]').forEach(img => {
  44. const dataSrc = img.getAttribute('data-thumb') || img.getAttribute('src');
  45. if (dataSrc && dataSrc !== img.src) {
  46. img.src = dataSrc;
  47. console.log("easystrapper: Preloaded initial thumbnail.");
  48. }
  49. });
  50. }
  51.  
  52. function postponeThumbnailsOnPlayback() {
  53. const videoPlayer = document.querySelector('video');
  54. if (videoPlayer) {
  55. videoPlayer.addEventListener('playing', () => {
  56. document.querySelectorAll('img[src*="default.jpg"]').forEach(img => {
  57. img.loading = 'lazy';
  58. console.log("easystrapper: Thumbnail loading postponed during playback.");
  59. });
  60. });
  61.  
  62. videoPlayer.addEventListener('pause', () => {
  63. preloadInitialThumbnails();
  64. console.log("easystrapper: Thumbnails reloaded after pause.");
  65. });
  66. }
  67. }
  68.  
  69. function deferNonCriticalScripts() {
  70. document.querySelectorAll('script:not([defer]):not([async])').forEach(script => {
  71. script.setAttribute('defer', '');
  72. console.log("easystrapper: Script deferred for better performance.");
  73. });
  74. }
  75.  
  76. // Eliminate render-blocking resources by deferring offscreen images
  77. function deferOffscreenImages() {
  78. document.querySelectorAll('img').forEach(img => {
  79. if (!img.hasAttribute('loading')) {
  80. img.setAttribute('loading', 'lazy');
  81. console.log("easystrapper: Offscreen image set to lazy load.");
  82. }
  83. });
  84. }
  85.  
  86. function observeDOMChanges() {
  87. const observer = new MutationObserver((mutations) => {
  88. mutations.forEach((mutation) => {
  89. if (mutation.addedNodes.length) {
  90. lazyLoadThumbnails();
  91. deferOffscreenImages();
  92. }
  93. });
  94. console.log("easystrapper: DOM changes observed and optimizations reapplied.");
  95. });
  96.  
  97. observer.observe(document.body, { childList: true, subtree: true });
  98. }
  99.  
  100. window.addEventListener('load', () => {
  101. preloadInitialThumbnails();
  102. postponeThumbnailsOnPlayback();
  103. lazyLoadThumbnails();
  104. deferNonCriticalScripts();
  105. deferOffscreenImages();
  106. observeDOMChanges();
  107. console.log("easystrapper: Initialization complete, optimizations applied.");
  108. });
  109.  
  110. })();