Youtube Repeat button

Youtube動画プレイヤーにリピートボタンをつける(html5プレイヤーのみ動作)

  1. // ==UserScript==
  2. // @name Youtube Repeat button
  3. // @namespace
  4. // @version 0.1.6
  5. // @description Youtube動画プレイヤーにリピートボタンをつける(html5プレイヤーのみ動作)
  6. // @author Nobby
  7. // @include https://youtube.com/*
  8. // @include https://www.youtube.com/*
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // ==/UserScript==
  12.  
  13. (function(){
  14. var addRepeatButton = function(){
  15. var player = document.getElementsByClassName('html5-video-player')[0];
  16. var video = document.querySelector('video');
  17. var repeat = GM_getValue('rpt',false);
  18. var control = document.getElementsByClassName('ytp-chrome-controls')[0];
  19. var yrb = document.createElement('button');
  20. var title = 'リピート';
  21. yrb.setAttribute('class', 'ytp-button ytp-repeat-button');
  22. yrb.setAttribute('style', 'float:right;');
  23.  
  24. var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  25. svg.setAttribute('width', '100%');
  26. svg.setAttribute('height', '100%');
  27. svg.setAttribute('viewBox', '-128 -128 768 768');
  28.  
  29. var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
  30.  
  31. var from = 'M363 363v0h0v0h0v0l0-0 0-0v0h0zM149 149v0h0v0h0v0l0 0-0 0v0h0zM469 256l-85 85v-64H64v-42h320v-64l85 85z';
  32. var to = 'M363 363v-86h42v128H149v64l-85-85 85-85v64h214zM149 149v86h-42V107h256V43l85 85-85 85v-64H149zM256 256l-0 0v-0H256v-0h0v-0l0 0z';
  33.  
  34. if(repeat){
  35. from = [to, to = from][0];
  36. video.setAttribute('loop', '');
  37. title = 'リピート解除';
  38. }
  39. yrb.setAttribute('title', title);
  40.  
  41. var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
  42. path.setAttribute('id', 'ytp-repeat');
  43. path.setAttribute('d', from);
  44.  
  45. var animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
  46. animate.setAttribute('attributeType', 'XML');
  47. animate.setAttribute('attributeName', 'd');
  48. animate.setAttribute('fill', 'freeze');
  49. animate.setAttribute('dur', '0.2s');
  50. animate.setAttribute('keySplines', '.4 0 1 1');
  51. animate.setAttribute('repeatCount', '1');
  52. animate.setAttribute('begin', 'indefinite');
  53.  
  54. var use1 = document.createElementNS('http://www.w3.org/2000/svg', 'use');
  55. use1.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '#ytp-repeat');
  56. use1.setAttribute('class', 'ytp-svg-shadow');
  57.  
  58. var use2 = document.createElementNS('http://www.w3.org/2000/svg', 'use');
  59. use2.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '#ytp-repeat');
  60. use2.setAttribute('class', 'ytp-svg-fill');
  61.  
  62. path.appendChild(animate);
  63. defs.appendChild(path);
  64. svg.appendChild(defs);
  65. svg.appendChild(use1);
  66. svg.appendChild(use2);
  67. yrb.appendChild(svg);
  68.  
  69. control.appendChild(yrb);
  70.  
  71. var tooltext = document.getElementsByClassName('ytp-tooltip-text')[0];
  72. var tooltip = tooltext.parentNode.parentNode;
  73.  
  74. var delay = null;
  75.  
  76. yrb.addEventListener('mouseover', function(){
  77. yrb.removeAttribute('title');
  78. tooltip.setAttribute('aria-hidden', 'true');
  79. tooltip.setAttribute('class', 'ytp-tooltip ytp-bottom');
  80. delay = setTimeout(function() {
  81. tooltip.style.display = '';
  82. tooltext.textContent = title;
  83. var parentRect = player.getBoundingClientRect();
  84. var childRect = yrb.getBoundingClientRect();
  85. var tipRect = tooltip.getBoundingClientRect();
  86. var x_left = childRect.left - parentRect.left + (childRect.width - tipRect.width)/2;
  87. tooltip.setAttribute('style', 'left:'+x_left+'px');
  88. tooltip.removeAttribute('aria-hidden');
  89. }, 500);
  90. }, false);
  91.  
  92. yrb.addEventListener('mouseleave', function(){
  93. clearTimeout(delay);
  94. tooltip.style.display = 'none';
  95. tooltip.setAttribute('aria-hidden', 'true');
  96. yrb.setAttribute('title', title);
  97. }, false);
  98.  
  99. yrb.addEventListener('click', function(){
  100. if(repeat){
  101. video.removeAttribute('loop');
  102. title = 'リピート';
  103. }else{
  104. video.setAttribute('loop', '');
  105. title = 'リピート解除';
  106. }
  107. repeat = !repeat;
  108. tooltext.textContent = title;
  109. var parentRect = player.getBoundingClientRect();
  110. var childRect = yrb.getBoundingClientRect();
  111. var tipRect = tooltip.getBoundingClientRect();
  112. var x_left = childRect.left - parentRect.left + (childRect.width - tipRect.width)/2;
  113. tooltip.setAttribute('style', 'left:'+x_left+'px');
  114. setTimeout(function() {
  115. animate.setAttribute('from', from);
  116. animate.setAttribute('to', to);
  117. animate.beginElement();
  118. path.setAttribute('d', to);
  119. from = [to, to = from][0];
  120. }, 50);
  121. }, false);
  122.  
  123. window.onbeforeunload = function(){
  124. GM_setValue('rpt',repeat);
  125. };
  126. };
  127. var observer = new MutationObserver(function(mutations){
  128. if(location.href.match(/youtube\.com\/(?:watch|embed)/)){
  129. addRepeatButton();
  130. observer.disconnect();
  131. }
  132. });
  133.  
  134. var target = document.body;
  135. observer.observe(target, {childList:true});
  136. })();