YouTube Screenshot

Screenshot the current frame of any YouTube video

  1. // ==UserScript==
  2. // @name YouTube Screenshot
  3. // @description Screenshot the current frame of any YouTube video
  4. // @version 1.4
  5. // @icon https://i.imgur.com/UbC1oPW.png
  6. // @match https://*.youtube.com/*
  7. // @license MIT
  8. // @namespace https://gf.qytechs.cn/users/1207477
  9. // ==/UserScript==
  10.  
  11. let escapeHTMLPolicy = 'trustedTypes' in window
  12. ? trustedTypes.createPolicy('forceInner', { createHTML: html=>html })
  13. : { createHTML: html=>html };
  14.  
  15. function update(){
  16. let $miniplayerBtn = document.querySelector('button.ytp-miniplayer-button');
  17. if($miniplayerBtn && !$miniplayerBtn.previousElementSibling.classList.contains('ytp-screenshot-button')){
  18. const $btn = document.createElement('button');
  19. $btn.id = 'ytp-screenshot-button';
  20. $btn.classList.add('ytp-screenshot-button', 'ytp-button');
  21. $btn.dataset.priority = '5';
  22. $btn.dataset.tooltipTargetId = 'ytp-screenshot-button';
  23. $btn.dataset.titleNoTooltip = 'Screenshot';
  24. $btn.ariaLabel = 'Screenshot';
  25. $btn.title = 'Screenshot';
  26. $btn.innerHTML = escapeHTMLPolicy.createHTML(`<svg height="100%" version="1.1" viewBox="-300 -1260 1560 1560" width="100%">
  27. <use class="ytp-svg-shadow" xlink:href="#ytp-id-screenshot-svg"></use>
  28. <path
  29. d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm40-80h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"
  30. fill="#fff" id="ytp-id-screenshot-svg"></path>
  31. </svg>`);
  32. $btn.addEventListener('click', screenshot);
  33. insertBefore($btn, $miniplayerBtn);
  34. }
  35. requestAnimationFrame(update);
  36. }
  37. requestAnimationFrame(update);
  38.  
  39. function insertBefore($element, $sibling){
  40. $sibling.parentElement.insertBefore($element, $sibling);
  41. }
  42.  
  43. function screenshot(e){
  44. const $video = document.querySelector('#player video');
  45. if(!$video){
  46. console.error('No video found to screenshot!');
  47. return;
  48. }
  49.  
  50. let wasPlaying = !$video.paused;
  51. if(wasPlaying) $video.pause();
  52. const $canvas = document.createElement('canvas');
  53. $canvas.width = $video.videoWidth;
  54. $canvas.height = $video.videoHeight;
  55. let oldCss = $video.style.cssText;
  56. $video.style.width = $video.videoWidth + 'px';
  57. $video.style.height = $video.videoHeight + 'px';
  58. const ctx = $canvas.getContext('2d');
  59. ctx.drawImage($video, 0, 0, $video.videoWidth, $video.videoHeight);
  60. $canvas.toBlob(blob=>{
  61. if(!e.shiftKey){
  62. const item = new ClipboardItem({ 'image/png': blob });
  63. navigator.clipboard.write([ item ]);
  64. }else{
  65. let url = URL.createObjectURL(blob);
  66. let a = document.createElement('a');
  67. a.style.display = 'none';
  68. a.href = url;
  69. a.download = document.title;
  70. document.body.appendChild(a);
  71. a.click();
  72. a.remove();
  73. URL.revokeObjectURL(url);
  74. }
  75. $video.style.cssText = oldCss;
  76. $canvas.remove();
  77. if(wasPlaying) $video.play();
  78. });
  79. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址