- // ==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.5
- // @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,
- readyToReload: false,
- 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', '');
- 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() {
- 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');
- this.video.addEventListener('timeupdate', ()=> {
- if (this.video.duration - this.video.currentTime < 1 && !this.readyToReload) {
- this.readyToReload = true;
- setTimeout(() => {if (this.loop && document.querySelector('.ytp-spinner[style=""]')) location.reload()}, 2000);
- }
- });
- }).observe(this.playercontainer, {childList: true, subtree: true, attributes: true, attributeFilter: ['class']});
- },
- findPlayercontainer: function() {
- if (window != window.parent && document.getElementById('chat')) return;
- const mo = 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();
- mo.disconnect();
- });
- mo.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();