NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Title Youtube Locations // @namespace TYTLs // @description Puts the video title in the location bar of all YouTube video pages. Now with extra features add scrollbars, animate thumbnails, reduce font sizes and un-float the header. // @version 1.3.0 // @downstreamURL http://userscripts.org/scripts/source/87416.user.js // @include http://*.youtube.*/* // @include http://youtube.*/* // @include https://*.youtube.*/* // @include https://youtube.*/* //// YouTube thumbnails almost certainly appear if you do a video search: // @include https://www.google.co.uk/search?*&tbm=vid&* //// YouTube thumbnails occasionally appear in normal Google search results: // @include https://www.google.co.uk/search* // @grant GM_addStyle // @grant GM_log // ==/UserScript== // TODO: In case for some reason YouTube or another script redirects us to // another URL which has lost the title param we added, we will re-run forever. // To avoid this we should have a fallback. E.g. if the title we want to set // is the same as the last one we did set (via GM_set/getValue) then do not try // again. // This has never happened so far. :) // If you are interested in YouTube images: // Thumbnails url looks like http://img.youtube.com/vi/8aYQ_wjmriQ/2.jpg // Youtube generates 4 thumbnails: 0.jpg at 320x240, and 1.jpg, 2.jpg, 3.jpg // Also large: hqdefault.jpg (480x360) and sometimes but not always hq1.jpg, hq2.jpg, hq3.jpg var addTitleToLocation = false; // Oh dear, the primary feature of this plugin has started causing annoying reloads. TODO Perhaps we can do it with pushState instead... var reduceFontSizes = true; var addScrollbars = true; var scrollDownToVideo = false; // YouTube's header ("masthead") is now floating. Setting this true will un-float it, then scroll down to hide it. But scrolling containers should then be enlarged. var animateThumbnails = true; var unfloatTheHeader = true; if (addTitleToLocation) { // NOTE: This code is deprecated, as I now use the userscripts URLs_Need_Titles setTimeout(function(){ if (document.location.pathname == "/watch") { var title = document.title.replace(/ - YouTube$/,'') || null; if (title) title = title.replace(/ /g,'_').replace(/^[\r\n_]*/,'').replace(/[\r\n_]*$/,''); // "_"s paste better into IRC, since " "s become "%20"s which are hard to read. The second and third parts trim "_"s and newlines from the start and end of the string. if (title) { if (!document.location.hash) { document.location.replace(document.location.href + '#' + title); // Does not alter browser history // document.location.hash = title; // Crashes Chrome less often } } } },5000); // This is what really stops the crashing! } if (reduceFontSizes) { // == Reduce font size of thumbnail titles == GM_addStyle(".yt-tile-default.video-list-item a .title, #watch-sidebar .video-list-item .title { font-size: 11px; line-height: 10px; }"); // Defaults are font-size: 13px; and line-height: 15px; which show only two lines in my browser. } if (addScrollbars) { // == Scrollbars on comments and related vids, to keep the video in view. == setTimeout(function(){ // We could alternatively act on watch-panel but that includes the video navigation buttons! // BUG: Interferes with YouTube's lazy-loading of thumbnails for related video. if (document.location.href.indexOf("/all_comments?") >= 0) { return; // Leave the full comments page alone. } // The top of watch7-content has become very large, so put a scrollbar on the whole lot. var watchDiscussion = /* document.getElementById("watch-discussion") || */ document.getElementById("watch7-content"); if (watchDiscussion) { var toSubtract = 464; // Small video screen //var toSubtract = 544; // Medium size video screen var roomForComments = window.innerHeight - toSubtract; if (roomForComments < 200) { roomForComments = 200; } watchDiscussion.style.overflow = "auto"; watchDiscussion.style.maxHeight = roomForComments+"px"; /* For a video height 360p */ GM_addStyle(" #watch7-content { border: 1px solid; border-color: #c8c8c8 #dddddd #dddddd #c8c8c8; margin-top: 5px; } #watch-header { margin-top: 0px; margin-bottom: 0px; } "); } var watchSidebar = document.getElementById("watch-sidebar") || document.getElementById("watch7-sidebar"); if (watchSidebar) { watchSidebar.style.overflow = "auto"; watchSidebar.style.maxHeight = (window.innerHeight - 69)+"px"; // May 2012 - fixes below no longer needed. // Now the text wraps because of the scrollbar, so we widen the element: // watchSidebar.style.width = (320+24)+"px"; // watchSidebar.style.width = '300px'; // And we must widen its container also: // TODO BUG: Why does this work in the console, but not from the userscript? // document.getElementById("watch-main").style.width = (960+24)+"px"; GM_addStyle(" #watch-sidebar, #watch7-sidebar { border: 1px solid; border-color: #c8c8c8 #dddddd #dddddd #c8c8c8; } "); // Without this, the Google notifications dropdown will appear behind our sidebar. // But with it, the sidebar is not clickable! :-( //GM_addStyle(" #watch-sidebar, #watch7-sidebar { z-index: -20; } "); } if (scrollDownToVideo) { // Un-float the header: GM_addStyle(" #masthead-positioner { position: initial; } #masthead-positioner-height-offset { height: 0px; } "); //// Title text // document.getElementById("eow-title").scrollIntoView(); //// Uploader info and videolist popdown. // document.getElementById("watch-headline-user-info").scrollIntoView(); //// The author's video list (was supposed to be a small gap above the video when collapsed, but it's not) // document.getElementById("watch-more-from-user").scrollIntoView(); //// The video // document.getElementsByTagName("embed")[0].scrollIntoView(); //// The video var watchVideo = document.getElementById("movie_player") || document.getElementById("player-api"); if (watchVideo) { watchVideo.scrollIntoView(); } //// Slight gap above the video (I prefer that) // var watchContainer = document.getElementById("watch7-container"); // if (watchContainer) { // watchContainer.scrollIntoView(); // } } // ~ October 2014 // This feels like a bug in Chrome 38. The element is given position:absolute but it does not move up when its parent does! We can fix it anyway: GM_addStyle(" #watch8-secondary-actions { position: initial; } "); },1000); } if (animateThumbnails) { // == Thumbnail animation == // TODO: This is working fine on "related videos" thumbnails, but not on queue // thumbnails, even if I have the queue open when I load the page. // Perhaps we are responding to a mouseout event from a child element, because // we are not checking the event target like we should do. function initThumbnailAnimator() { // function createThumbnailAnimatorEvent(thumbImage) { var filenameRE = /\/([^/]*)\.(jpg|webp)(\?.*|$)/; var thumbImage = null; var originalHref = null; var timer = null; //var frames = ["1.jpg","2.jpg","3.jpg"]; // "default.jpg", var frames = ["1","2","3"]; var frameI = 0; function changeFrame() { frameI = (frameI + 1) % frames.length; var match = originalHref.match(filenameRE); var extension = match[2]; var filename = frames[frameI] + '.' + extension; thumbImage.src = originalHref.replace(filenameRE, '/' + filename); } function startAnimation() { // Because there was a bug that the running animation would not stop! if (timer) { clearInterval(timer); } originalHref = thumbImage.src; if (originalHref.match(/^data:/)) { return; } // We make this check quite late, due to lazy loading if (originalHref.match(filenameRE)) { // logElem("Starting animation",thumbImage); timer = setInterval(changeFrame,600); } } function stopAnimation() { if (timer) { // logElem("Stopping animation",thumbImage); clearInterval(timer); timer = null; // This isn't really neccessary, except to ensure the check for default\.jpg above works next time! //thumbImage.src = thumbImage.src.replace(/\/[^/]*$/,'') + '/' + "default.jpg"; thumbImage.src = originalHref; } } function logElem(name,elem) { report = "<"+elem.tagName+" id="+elem.id+" class="+elem.className+" src="+elem.src+" />"; GM_log(name+" = "+report); } function check(fn) { return function(evt) { // logElem("["+evt.type+"] evt.target",evt.target); var elemToCheck = evt.target || evt.srcElement; if (elemToCheck.tagName == "IMG") { thumbImage = elemToCheck; return fn(); } else if (elemToCheck.className=='screen') { var seekImg = elemToCheck.parentNode.getElementsByTagName("img")[0]; if (seekImg) { thumbImage = seekImg; return fn(); } // } else { // var imgCount = elemToCheck.getElementsByTagName("img").length; // if (imgCount == 1) { // thumbImage = elemToCheck.getElementsByTagName("img")[0]; // // logElem("["+evt.type+"] checking sub-image",thumbImage); // logElem("Whilst checking",elemToCheck); // logElem(" Animating elem",thumbImage); // logElem(" with parent",thumbImage.parentNode); // logElem(" whilst currentTarget",evt.currentTarget); // logElem(" and srcElement",evt.srcElement); // return fn(); // } } }; } //// Unfortunately these do not fire on any HTMLImageElements when browsing the queue. document.body.addEventListener("mouseover",check(startAnimation),false); document.body.addEventListener("mouseout",check(stopAnimation),false); // var videoList = document.getElementById("watch-sidebar"); // or watch-module or watch-module-body or watch-related or watch-more-related // var videoList = document.getElementsByClassName("video-list")[0]; // can be 4 of these! // var thumbs = document.getElementsByTagName("img"); // for (var i=0;i<thumbs.length;i++) { // createThumbnailAnimatorEvent(thumbs[i]); // } } setTimeout(initThumbnailAnimator,1000); } if (unfloatTheHeader) { GM_addStyle("#masthead-positioner { position: static; }"); var gapElement = document.getElementById("masthead-positioner-height-offset"); if (gapElement) { gapElement.parentNode.removeChild(gapElement); } }