youtube HTML5 Auto Loop

youtube再生時に自動ループする

2023-04-29 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

  1. // ==UserScript==
  2. // @name youtube HTML5 Auto Loop
  3. // @namespace youtube HTML5 Auto Loop
  4. // @grant none
  5. // @description youtube再生時に自動ループする
  6. // @author TNB
  7. // @match https://www.youtube.com/*
  8. // @version 1.5.3
  9. // @run-at document-start
  10. // ==/UserScript==
  11.  
  12.  
  13. /******************** SETTING **************************/
  14. const loop_off = {
  15. when_enable_next_video_autoplay: false,
  16. when_playlist: false,
  17. with_embedded_video: false
  18. };
  19. /********************************************************/
  20.  
  21. 'use strict';
  22.  
  23. const YoutubeHTML5AutoLoop = {
  24. loop: true,
  25. playercontainer: null,
  26. video: null,
  27. prevSrc: null,
  28. button: null,
  29. eventcache: {},
  30. cancelInit: false,
  31. ele: {
  32. when_enable_next_video_autoplay: '.ytp-right-controls > button[style]:not([style *= "display: none"]) .ytp-autonav-toggle-button[aria-checked="true"]',
  33. when_playlist: '#secondary-inner > #playlist:not([hidden])',
  34. with_embedded_video: 'html[data-cast-api-enabled="true"]'
  35. },
  36. init: function() {
  37. this.addListener();
  38. },
  39. isLoop: function() {
  40. return !Object.keys(loop_off).some(a => loop_off[a] && document.querySelector(this.ele[a]));
  41. },
  42. goLoop: function() {
  43. this.video.currentTime = 0;
  44. this.video.play;
  45. },
  46. enableLoop: function() {
  47. if (this.loop) this.video.setAttribute('loop', this.loop);
  48. else this.video.removeAttribute('loop');
  49. },
  50. initLoop: function() {
  51. this.loop = this.isLoop();
  52. this.enableLoop();
  53. },
  54. loopToggle: function() {
  55. this.loop = !this.loop;
  56. this.enableLoop();
  57. },
  58. displayLoopStatus: function() {
  59. const video = document.querySelector('video:hover');
  60. if (!video) return;
  61. const checkBox = document.querySelector('.ytp-contextmenu [aria-checked]');
  62. checkBox.setAttribute('aria-checked', this.loop);
  63. if (!this.eventcache.checkBox) {
  64. checkBox.addEventListener('click', this, true);
  65. this.eventcache.checkBox = true;
  66. }
  67. },
  68. isEnded: function(m) {
  69. return m.classList.contains('ended-mode') || m.querySelector('.ytp-autonav-endscreen-button-container:not([style*="display"]),.html5-endscreen:not([style*="display"])');
  70. },
  71. toggleNextVideoAutoplay: function() {
  72. if ((!loop_off.when_enable_next_video_autoplay && this.loop) || loop_off.when_enable_next_video_autoplay) {
  73. setTimeout(() => {
  74. if (document.querySelector(`.ytp-right-controls > button[style]:not([style *= "display: none"]) .ytp-autonav-toggle-button[aria-checked="${this.loop}"]`)) this.button.click();
  75. }, 1000);
  76. }
  77. },
  78. observeVideo: function() {
  79. const mo = new MutationObserver(() => {
  80. if (this.video.src != this.prevSrc) this.cancelInit = false;
  81. if (!this.cancelInit) this.initLoop();
  82. if (this.loop && this.isEnded(this.playercontainer)) this.goLoop();
  83. if (!this.eventcache.toggleAutoPlay) this.addToggleEvent();
  84. this.toggleNextVideoAutoplay();
  85. this.prevSrc = this.video.src;
  86. this.video = this.playercontainer.querySelector('video');
  87. });
  88. mo.observe(this.playercontainer, {attributes: true, attributeFilter: ['class']});
  89. },
  90. findPlayercontainer: function() {
  91. if (window != window.parent && document.getElementById('chat')) return;
  92. const mm = new MutationObserver(() => {
  93. this.playercontainer = document.getElementById('movie_player');
  94. if (!this.playercontainer) return;
  95. this.video = this.playercontainer.querySelector('video');
  96. this.observeVideo();
  97. this.initLoop();
  98. this.addToggleEvent();
  99. this.toggleNextVideoAutoplay();
  100. mm.disconnect();
  101. });
  102. mm.observe(document.body, {childList: true, subtree: true});
  103. },
  104. addToggleEvent: function() {
  105. this.button = document.querySelector('.ytp-right-controls > button[style]:not([style *= "display: none"]) .ytp-autonav-toggle-button');
  106. if (!loop_off.when_enable_next_video_autoplay || !this.button) return;
  107. this.button.addEventListener('click', () => {
  108. this.loop = JSON.parse(this.button.getAttribute('aria-checked'));
  109. this.enableLoop();
  110. this.cancelInit = true;
  111. this.eventcache.toggleAutoPlay = true;
  112. });
  113. },
  114. addListener: function() {
  115. window.addEventListener('DOMContentLoaded', this);
  116. window.addEventListener('contextmenu', this);
  117. },
  118. handleEvent: function(e) {
  119. switch (e.type) {
  120. case 'DOMContentLoaded':
  121. this.findPlayercontainer();
  122. break;
  123. case 'contextmenu':
  124. this.displayLoopStatus();
  125. break;
  126. case 'click':
  127. this.loopToggle();
  128. document.body.click();
  129. this.toggleNextVideoAutoplay();
  130. this.cancelInit = true;
  131. e.stopPropagation();
  132. break;
  133. }
  134. }
  135. };
  136.  
  137. YoutubeHTML5AutoLoop.init();