YouTube: thumbnail previews

Shows the collection of available thumbnails in the highest possible resolution.

安装此脚本?
作者推荐脚本

您可能也喜欢YouTube: playlists playback tracker

安装此脚本
  1. // ==UserScript==
  2. // @name YouTube: thumbnail previews
  3. // @icon https://youtube.com/favicon.ico
  4. // @namespace andrybak.dev
  5. // @version 4
  6. // @description Shows the collection of available thumbnails in the highest possible resolution.
  7. // @author puzzle, andrybak
  8. // @license MIT
  9. // @match https://www.youtube.com/watch*
  10. // @match https://www.youtube.com/shorts/*
  11. // @grant GM_addStyle
  12. // @run-at document-idle
  13. // @connect i.ytimg.com
  14. // @require https://cdn.jsdelivr.net/gh/rybak/userscript-libs@dc32d5897dcfa40a01c371c8ee0e211162dfd24c/waitForElement.js
  15. // ==/UserScript==
  16.  
  17. /*
  18. * This script is a very simplified fork of the script
  19. * https://greasyfork.org/en/scripts/367855-youtube-com-thumbnail
  20. * by Greasy Fork user "puzzle".
  21. *
  22. * This script is not intended to be used all the time.
  23. * Instead, just turn it on in your userscript manager
  24. * whenever you need access to the thumbnails.
  25. */
  26.  
  27. (function() {
  28. 'use strict';
  29.  
  30. const prefix = 'userscript_youtube-thumbnail-previews__';
  31.  
  32. GM_addStyle(`
  33. /* userscript thumbnail */
  34. .${prefix}img {
  35. margin-inline-start: unset; margin-inline-end: unset;
  36. height: 90px; width: 160px; border-radius: 3px; margin-right: 15px;
  37. background-size: cover;
  38. box-shadow: 2px 2px 5px 0px black;
  39. }
  40. `);
  41.  
  42. async function createImage(parent, filename) {
  43. const video_id = new URLSearchParams(location.search).get('v') || location.pathname.match(/\/shorts\/([\w-_]+)/i)[1];
  44. const linkId = `${prefix}link${filename}`;
  45. const imgId = `${prefix}img${filename}`;
  46. let img = document.getElementById(imgId);
  47. let link = document.getElementById(linkId);
  48. if (img) {
  49. if (img.dataset.video_id === video_id) {
  50. return;
  51. }
  52. } else {
  53. img = document.createElement('figure');
  54. img.classList.add(`${prefix}img`);
  55. link = document.createElement('a');
  56. link.id = linkId;
  57. link.target = "_blank"; // to avoid misclicks ruining the viewing experience
  58. link.rel = "noopener noreferrer";
  59. link.appendChild(img);
  60. parent.appendChild(link);
  61. }
  62. const url = `https://i.ytimg.com/vi/${video_id}/${filename}.jpg`
  63. img.id = imgId;
  64. img.dataset.video_id = video_id;
  65. img.style.backgroundImage = `url(${url})`;
  66. link.href = url;
  67. }
  68.  
  69. function createOrUpdateImage() {
  70. /*
  71. * Middle row seems to not be used on most videos.
  72. * It's empty and is just sitting there.
  73. */
  74. waitForElement('.watch-active-metadata #middle-row').then(p => {
  75. /*
  76. * so that all thumbnails are in rows, not in a column
  77. */
  78. p.style.display = 'flex';
  79. p.style.flexWrap = 'wrap';
  80. /*
  81. * sorted roughtly in popularity/usefulness, from best to worst
  82. * TODO:
  83. * try using <img> with srcset property https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset
  84. * and let browser figure out the best image
  85. */
  86. createImage(p, "maxresdefault");
  87. createImage(p, "maxres1");
  88. createImage(p, "maxres2");
  89. createImage(p, "maxres3");
  90. createImage(p, "hqdefault");
  91. createImage(p, "sddefault");
  92. createImage(p, "mqdefault");
  93. });
  94. }
  95.  
  96. document.addEventListener('readystatechange',function(e) {
  97. if (document.readyState === 'complete') {
  98. createOrUpdateImage();
  99. }
  100. });
  101.  
  102. document.addEventListener('yt-navigate-finish',function(e) {
  103. createOrUpdateImage();
  104. });
  105. })();