NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name yt-url-at-time // @namespace mechalynx/yt-url-at-time // @license MIT // @grant none // @description On youtube, use alt+` to set the url to the current timestamp, for easy bookmarking // @include https://www.youtube.com/* // @version 0.2.7 // @copyright 2017, MechaLynx (https://github.com/MechaLynx) // @updateURL https://openuserjs.org/meta/MechaLynx/yt-url-at-time.meta.js // @downloadURL https://openuserjs.org/src/scripts/MechaLynx/yt-url-at-time.user.js // @run-at document-idle // @author MechaLynx // ==/UserScript== // jshint esversion: 6 // Matches time hashes for the purpose of removing them // note that I don't like my regexp here... var re_timehash = /#t=(?:[0-9]*\.?[0-9]*|(?:[0-9]*(?:h|m|s))*)*/g; // `video` element utility var video = { get element() { return document.querySelector('#movie_player video'); }, get timehash() { var secs = this.element.currentTime; return '#t=' + [(h = ~~(secs / 3600)) && h + 'h' || null, (m = ~~(secs % 3600 / 60)) && m + 'm' || null, (s = ~~(secs % 3600 % 60)) && s + 's'].join(''); }, get plaintimehash() { return '#t=' + this.element.currentTime; }, get notimehash() { return window.location.origin + window.location.pathname + window.location.search + window.location.hash.replace(re_timehash, ''); } }; // Keep looking for the time indicator span, until it's found // The `load` event is insufficient var wait_for_page = window.setInterval(function(){ var current_time_element = document.querySelector('.ytp-time-current'); if (current_time_element){ window.clearInterval(wait_for_page); // Add CSS for time indicator span let time_style = document.createElement('style'); time_style.setAttribute('name', "yt-url-at-time"); time_style.innerHTML = ` .url-at-time-element-hover:hover{ cursor: pointer; } .url-at-time-clipboard-helper{ position: absolute; top: 0; left: 0; padding: none; margin: none; border: none; width: 0; height: 0; } `; document.body.appendChild(time_style); // Toggle the class so that it doesn't look clickable // during ads, which would be confusing current_time_element.onmouseover = function(){ if (document.querySelector('.videoAdUi')){ current_time_element.classList.remove('url-at-time-element-hover'); }else{ current_time_element.classList.add('url-at-time-element-hover'); } }; current_time_element.addEventListener('click', function(e){ if (e.altKey){ hashmodifier(true); } else { hashmodifier(false); } if (e.ctrlKey){ copy_url_to_clipboard(); } }); } }, 1000); // Add the timestamp to the URL var hashmodifier = function(precise=false){ if ( location.href.match(/.*watch.*/) && document.querySelector('.videoAdUi') === null){ precise ? history.replaceState(false, false, video.notimehash + video.plaintimehash) : history.replaceState(false, false, video.notimehash + video.timehash); } }; var copy_url_to_clipboard = function(attempt_to_restore=false){ // Current focus and selection cannot be restored // since clicking on the timer causes the movie player to be focused // clearing the selection and changing the active element before we arrive here // However, attempting to restore them is meaningful if called through a hotkey if (attempt_to_restore){ var selection = document.getSelection(); var current_selection = selection.getRangeAt(0); var current_focus = document.activeElement; } // Add invisible textarea to allow copying the generated URL to clipboard let clipboard_helper = document.createElement('textarea'); clipboard_helper.classList.add('url-at-time-clipboard-helper'); document.body.appendChild(clipboard_helper); clipboard_helper.value = window.location.href; clipboard_helper.select(); clipboard_helper.setSelectionRange(0, clipboard_helper.value.length); document.execCommand('copy'); document.body.removeChild(clipboard_helper); if (attempt_to_restore){ current_focus.focus(); // https://gist.github.com/dantaex/543e721be845c18d2f92652c0ebe06aa selection.empty(); selection.addRange(current_selection); } }; var _alt=false; var _q=false; // Listen for the hotkey document.addEventListener('keydown', z => { // if you want to change the hotkey // you can use this: http://mechalynx.github.io/keypress/ // or another tester if you don't like this one if (z.code === 'KeyQ'){ _q=true; } if (z.altKey && z.code === 'Backquote'){ hashmodifier(_alt); _alt=true; } if (_q && _alt){ copy_url_to_clipboard(true); } }); document.addEventListener('keyup', z => { if(!z.altKey){ _alt=false; } if(z.code === "KeyQ"){ _q=false; } });