NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Vimeo Loop 2018 // @namespace volkan-k // @description Adds a loop button to the video player. // @include https://vimeo.com/* // @copyright 2018, volkan-k // @license MIT // @version 1.6 // @grant unsafeWindow // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js // ==/UserScript== function debugLog(message){ console.log("USER-SCRIPT VIMEO-LOOP : "+message); } var timer1; var timer2; var window_focus; var check_focus = false; // true= check document focus, false= dont check it $(window.top).focus(function() { window_focus = true; }).blur(function() { window_focus = false; }); (function(){ 'use strict'; // helper: find DOM element function find( selector ) { return document.querySelector( selector ); } // wait for player to be ready and set up periodic video check function setup() { // video element available? if ( find('.vp-sidedock') && find( '.player .vp-video video' ) ) { // try to add loop button // (this can easily break if Vimeo updates their object tree) try { // make loop button var button = makeButton(); // regularly check that button is in sidedock and loop is in sync // yes, that's dirty, but Vimeo replaces the player UI somewhen after loading setInterval( function() { // if video changed, loop attribute is lost. recover it. if ($("label.loop-label span:contains('Disable')").length>0 && $("video").length>0 && $("video[loop]").length==0){ toggle_loop(); } // find control bar var sideDock = find( '.vp-sidedock' ) // exit if button exists var oldButton = find( '#loop-button' ); if (oldButton!==null){ return; } // add new button sideDock.appendChild( button ); }, 500 ); } catch ( error ) { // log the error console.error( "[Vimeo Loop] Error appending button:", error ); } } else { // try again later setTimeout( setup, 500 ); } } // create loop button function makeButton() { // create new button var button = document.createElement( 'div' ); button.setAttribute( 'class', "box"); button.setAttribute( 'id', "loop-button"); button.innerHTML = '<label class="rounded-box loop-label invisible hidden" role="presentation" hidden=""><span>Toggle Loop</span></label><button type="button" class="loop-button rounded-box" aria-label="Toggle Loop"><img src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMi4wMTYgNTEyLjAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTEyLjAxNiA1MTIuMDE2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTUwNy4zMzYsMTAwLjY5NmwtOTYtOTZjLTQuNTc2LTQuNTc2LTExLjQ1Ni01Ljk1Mi0xNy40NC0zLjQ1NmMtNS45ODQsMi40OTYtOS44ODgsOC4yODgtOS44ODgsMTQuNzUydjQ4aC0yMDggICAgYy05Ny4yMTYsMC0xNzYsNzguNzg0LTE3NiwxNzZjMCw4LjgzMiw3LjE2OCwxNiwxNiwxNmg2NGM4LjgzMiwwLDE2LTcuMTY4LDE2LTE2YzAtNDQuMTkyLDM1LjgwOC04MCw4MC04MGgyMDh2NDggICAgYzAsNi40NjQsMy45MDQsMTIuMzIsOS44ODgsMTQuNzg0YzUuOTg0LDIuNDk2LDEyLjg2NCwxLjEyLDE3LjQ0LTMuNDU2bDk2LTk2QzUxMy41NzYsMTE3LjA4LDUxMy41NzYsMTA2LjkzNiw1MDcuMzM2LDEwMC42OTZ6IiBmaWxsPSIjRkZGRkZGIi8+Cgk8L2c+CjwvZz4KPGc+Cgk8Zz4KCQk8cGF0aCBkPSJNNDk2LjAwOCwyNTUuOTkyaC02NGMtOC44MzIsMC0xNiw3LjE2OC0xNiwxNmMwLDQ0LjE5Mi0zNS44MDgsODAtODAsODBoLTIwOHYtNDhjMC02LjQ2NC0zLjkwNC0xMi4zMi05Ljg4OC0xNC43ODQgICAgcy0xMi44MzItMS4wODgtMTcuNDQsMy40ODhsLTk2LDk2Yy02LjI0LDYuMjQtNi4yNCwxNi4zODQsMCwyMi42MjRsOTYsOTZjNC41NzYsNC41NzYsMTEuNDU2LDUuOTUyLDE3LjQ0LDMuNDU2ICAgIHM5Ljg4OC04LjMyLDkuODg4LTE0Ljc4NHYtNDhoMjA4Yzk3LjIxNiwwLDE3Ni03OC43ODQsMTc2LTE3NkM1MTIuMDA4LDI2My4xNiw1MDQuODQsMjU1Ljk5Miw0OTYuMDA4LDI1NS45OTJ6IiBmaWxsPSIjRkZGRkZGIi8+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg=="/></button>'; button.addEventListener('click', toggle_loop, false); // apply mouseover effect button.onmouseenter = function() { $("label.loop-label").removeClass("invisible hidden").removeAttr("hidden").addClass("visible"); }; button.onmouseleave = function() { $("label.loop-label").removeClass("visible").addClass("invisible hidden").attr("hidden", ""); }; // return DOM object return button; } function toggle_loop() { var vid=$("video"); if (vid.length===0){ return; } if (vid.attr("loop")==="loop") { vid.removeAttr("loop"); $("label.loop-label span").text("Enable Loop"); clearInterval(timer1); } else { vid.attr("loop", "loop"); $("label.loop-label span").text("Disable Loop"); timer1=setInterval(click_watch_again,500); } } function click_watch_again(){ hide_outro(); var w_a_b=$(".outro-secondary_actions button[data-fatal-attraction*='watch_again']"); // Watch Again Button if (w_a_b.length===0){ return; } if (check_focus!==true || document.hasFocus() || window_focus===true){ w_a_b.click(); } } function hide_outro(){ $(".player").removeClass("player-outroVisible"); $(".vp-controls").removeClass("controls-outro"); $(".vp-sidedock").removeClass("sidedock-outro"); $(".vp-outro").hide(); } $( ".player_outro_area" ).mouseenter(function() { clearTimeout(timer2); show_controls(); timer2=setTimeout(hide_controls,3000); }); $( ".player_outro_area" ).mousemove(function() { clearTimeout(timer2); show_controls(); timer2=setTimeout(hide_controls,3000); }); $( ".player_outro_area" ).mouseleave(function() { hide_controls(); }); function show_controls(){ $(".vp-controls,.vp-sidedock").removeClass("invisible hidden").show(); } function hide_controls(){ $(".vp-controls,.vp-sidedock").addClass("invisible hidden").hide(); } // start looking for video player setup(); })();