Greasy Fork is available in English.

youtube HTML5 Auto Loop

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

От 28.05.2024. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name           youtube HTML5 Auto Loop
// @namespace      youtube HTML5 Auto Loop
// @grant          none
// @description    youtube再生時に自動ループする
// @author         TNB
// @match          https://www.youtube.com/*
// @version        1.5.4
// @run-at         document-start
// ==/UserScript==

/********************  SETTING **************************/
const loop_off = {
  when_enable_next_video_autoplay: false,
  when_playlist: false,
  with_embedded_video: false
};
/********************************************************/


'use strict';

const YoutubeHTML5AutoLoop = {
  loop: true,
  playercontainer: null,
  video: null,
  prevSrc: null,
  button: null,
  eventcache: {},
  cancelInit: false,
  ele: {
    when_enable_next_video_autoplay: '.ytp-right-controls > button[style]:not([style *= "display: none"]) .ytp-autonav-toggle-button[aria-checked="true"]',
    when_playlist: '#secondary-inner #playlist:not([hidden])',
    with_embedded_video: 'html[data-cast-api-enabled="true"]'
  },
  init: function() {
    this.addListener();
  },
  isLoop: function() {
    return !Object.keys(loop_off).some(a => loop_off[a] && document.querySelector(this.ele[a]));
  },
  goLoop: function() {
    this.video.currentTime = 0;
    this.video.play;
  },
  enableLoop: function() {
    if (this.loop) this.video.setAttribute('loop', this.loop);
    else this.video.removeAttribute('loop');
  },
  initLoop: function() {
    this.loop = this.isLoop();
    this.enableLoop();
  },
  loopToggle: function() {
    this.loop = !this.loop;
    this.enableLoop();
  },
  displayLoopStatus: function() {
    const video = document.querySelector('video:hover');
    if (!video) return;
    const checkBox = document.querySelector('.ytp-contextmenu [aria-checked]');
    checkBox.setAttribute('aria-checked', this.loop);
    if (!this.eventcache.checkBox) {
      checkBox.addEventListener('click', this, true);
      this.eventcache.checkBox = true;
    }
  },
  isEnded: function(m) {
    return m.classList.contains('ended-mode') || m.querySelector('.ytp-autonav-endscreen-button-container:not([style*="display"]),.html5-endscreen:not([style*="display"])');
  },
  toggleNextVideoAutoplay: function() {
    if ((!loop_off.when_enable_next_video_autoplay && this.loop) || loop_off.when_enable_next_video_autoplay) {
      setTimeout(() => {
        if (document.querySelector(`.ytp-right-controls > button[style]:not([style *= "display: none"]) .ytp-autonav-toggle-button[aria-checked="${this.loop}"]`)) this.button.click();
      }, 1000);
    }
  },
  observeVideo: function() {
    const mo = new MutationObserver(() => {
      if (this.video.src != this.prevSrc) this.cancelInit = false;
      if (!this.cancelInit) this.initLoop();
      if (this.loop && this.isEnded(this.playercontainer)) this.goLoop();
      if (!this.eventcache.toggleAutoPlay) this.addToggleEvent();
      this.toggleNextVideoAutoplay();
      this.prevSrc = this.video.src;
      this.video = this.playercontainer.querySelector('video');
    });
    mo.observe(this.playercontainer, {attributes: true, attributeFilter: ['class']});
  },
  findPlayercontainer: function() {
    if (window != window.parent && document.getElementById('chat')) return;
    const mm = new MutationObserver(() => {
      this.playercontainer = document.getElementById('movie_player');
      if (!this.playercontainer) return;
      this.video = this.playercontainer.querySelector('video');
      this.observeVideo();
      this.initLoop();
      this.addToggleEvent();
      this.toggleNextVideoAutoplay();
      mm.disconnect();
    });
    mm.observe(document.body, {childList: true, subtree: true});
  },
  addToggleEvent: function() {
    this.button = document.querySelector('.ytp-right-controls > button[style]:not([style *= "display: none"]) .ytp-autonav-toggle-button');
    if (!loop_off.when_enable_next_video_autoplay || !this.button) return;
    this.button.addEventListener('click', () => {
      this.loop = JSON.parse(this.button.getAttribute('aria-checked'));
      this.enableLoop();
      this.cancelInit = true;
      this.eventcache.toggleAutoPlay = true;
    });
  },
  addListener: function() {
    window.addEventListener('DOMContentLoaded', this);
    window.addEventListener('contextmenu', this);
  },
  handleEvent: function(e) {
    switch (e.type) {
      case 'DOMContentLoaded':
        this.findPlayercontainer();
        break;
      case 'contextmenu':
        this.displayLoopStatus();
        break;
      case 'click':
        this.loopToggle();
        document.body.click();
        this.toggleNextVideoAutoplay();
        this.cancelInit = true;
        e.stopPropagation();
        break;
    }
  }
};

YoutubeHTML5AutoLoop.init();