Space-efficient Youtube

AKA: "Wide Youtube", AKA: "Wide video container" - Uses the page space on youtube more efficiently (especially good for high resolutions)

Per 23-08-2020. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name        Space-efficient Youtube
// @namespace   1N07
// @author      1N07
// @icon        https://i.imgur.com/VgEiyi3.png
// @description AKA: "Wide Youtube", AKA: "Wide video container" - Uses the page space on youtube more efficiently (especially good for high resolutions)
// @include     https://www.youtube.com/*
// @version     1.9
// @grant       GM_registerMenuCommand
// @grant       GM_unregisterMenuCommand
// @grant       GM_getValue
// @grant       GM_setValue
// ==/UserScript==

(function() {

    var FPPHandle;
    var FPPCompOn = GM_getValue("FPPCompOn", false);
    SetFPPHandle();

	var ForceCSSHandle;
    var ForceCSSOn = GM_getValue("ForceCSSOn", false);
    SetForceCSSHandle();

    //var HQTNHandle;
    //var HQTN = GM_getValue("HQTN", false);

    var HomeVideoContainerWidthHandle;
    var HomeVideoContainerWidth = GM_getValue("HomeVideoContainerWidth", "360px");
    SetHomeVideoContainerWidthHandle();

    var SubVideoContainerWidthHandle;
    var SubVideoContainerWidth = GM_getValue("SubVideoContainerWidth", "210px");
    //SetHQTNHandle();
    SetSubVideoContainerWidthHandle();

    var TrendingVideoContainerWidthHandle;
    var TrendingVideoContainerWidth = GM_getValue("TrendingVideoContainerWidth", "600px");
    SetTrendingVideoContainerWidthHandle();

    var VideoContainerHorMarginHandle;
    var VideoContainerHorMargin = GM_getValue("VideoContainerHorMargin", "4px");
    SetVideoContainerHorMarginHandle();

    var VideoContainerVerMarginHandle;
    var VideoContainerVerMargin = GM_getValue("VideoContainerVerMargin", "24px");
    SetVideoContainerVerMarginHandle();

    var ShowChannelIconNextToVideosOnHomePageHandle;
    var ShowChannelIconNextToVideosOnHomePage = GM_getValue("ShowChannelIconNextToVideosOnHomePage", true);
    SetShowChannelIconNextToVideosOnHomePageHandle();

    var LessenVideoTitleTopMarginOnHomePage = true;

    var screenWidth = screen.width;

    if(!!document.getElementById("early-body")) { //if old youtube
        document.getElementById("content").setAttribute("style", "width: 99%;");
    } else { //new youtube
        addGlobalStyle(`
			ytd-app #page-manager > ytd-browse:not([page-subtype="playlist"]):not([page-subtype="home"]) ytd-two-column-browse-results-renderer.ytd-browse
			{
				box-sizing: border-box`+(ForceCSSOn ? `!important`:``)+`;
				width: calc(100% - 25px) !important;
				margin: 10px`+(ForceCSSOn ? `!important`:``)+`;
				max-width: none;
			}

			#items.ytd-grid-renderer > ytd-grid-video-renderer.ytd-grid-renderer,
			#items.ytd-grid-renderer > ytd-grid-radio-renderer.ytd-grid-renderer,
			#items.ytd-grid-renderer > ytd-grid-channel-renderer.ytd-grid-renderer,
			#items.ytd-grid-renderer > ytd-grid-playlist-renderer.ytd-grid-renderer,
			#items.ytd-grid-renderer > ytd-grid-movie-playlist-renderer.ytd-grid-renderer,
			#items.ytd-grid-renderer > ytd-grid-movie-renderer.ytd-grid-renderer,
			#items.ytd-grid-renderer > ytd-grid-show-renderer.ytd-grid-renderer,
			#items.ytd-grid-renderer > ytd-game-card-renderer.ytd-grid-renderer,
			ytd-grid-video-renderer
			{
				width: `+SubVideoContainerWidth+`;
				margin-left: 0;
				margin-top: 0;
				margin-right: `+VideoContainerHorMargin+`;
				margin-bottom: `+VideoContainerVerMargin+`;
			}
			ytd-thumbnail.ytd-grid-video-renderer
			{
				width: `+SubVideoContainerWidth+`;
				height: calc(`+SubVideoContainerWidth+` * 0.5625);
			}
			ytd-thumbnail #thumbnail.ytd-thumbnail yt-img-shadow.ytd-thumbnail
			{
				width: 100%;
				height: 100%;
			}
			img.yt-img-shadow
			{
				max-width: 100%;
				max-height: 100%;
				margin-left: 0;
				margin-right: 0;
				width: 100%;
				height: 100%;
			}

			/*channels page rules*/
			ytd-app #page-manager > ytd-browse[page-subtype="channels"] ytd-two-column-browse-results-renderer.ytd-browse ytd-item-section-renderer.ytd-section-list-renderer
			{
				width: calc(100% - 20px);
				max-width: none;
			}
			/*for some reason the arrows sometimes dont show up, so now forcing them to*/
			ytd-app #page-manager > ytd-browse[page-subtype="channels"] ytd-two-column-browse-results-renderer.ytd-browse ytd-item-section-renderer.ytd-section-list-renderer ytd-button-renderer.yt-horizontal-list-renderer.arrow
			{
				opacity: 1;
				display: flex;
			}

			/*trending page rules*/
			ytd-app #page-manager > ytd-browse:not([page-subtype]) #grid-container.ytd-expanded-shelf-contents-renderer > .ytd-expanded-shelf-contents-renderer,
			ytd-app #page-manager > ytd-browse:not([page-subtype]) #grid-container.ytd-expanded-shelf-contents-renderer > .ytd-expanded-shelf-contents-renderer > .ytd-video-renderer
			{
				max-width: `+TrendingVideoContainerWidth+`;
			}
			ytd-app #page-manager > ytd-browse:not([page-subtype]) ytd-video-renderer.ytd-expanded-shelf-contents-renderer,
			ytd-app #page-manager > ytd-browse:not([page-subtype]) ytd-video-renderer.ytd-expanded-shelf-contents-renderer:not(:last-child)
			{
				padding-right: 3px;
				margin-left: 0;
				margin-top: 0;
				margin-right: `+VideoContainerHorMargin+`;
				margin-bottom: `+VideoContainerVerMargin+`;
			}
			ytd-app #page-manager > ytd-browse:not([page-subtype]) ytd-thumbnail.ytd-video-renderer
			{
				margin-right: 10px;
			}

			/*Playlist page rules*/
			ytd-browse[page-subtype="playlist"] ytd-two-column-browse-results-renderer.ytd-browse
			{
				max-width: none;
			}

			/*home page rules*/
			ytd-rich-item-renderer {
			width: `+HomeVideoContainerWidth+`;
				margin-left: 0;
				margin-top: 0;
				margin-right: `+VideoContainerHorMargin+`;
				margin-bottom: `+VideoContainerVerMargin+`;
			}
		`);
        console.log("Youtube Wide video container style added to DOM");

        if(!ShowChannelIconNextToVideosOnHomePage)
        {
            addGlobalStyle(`
				#avatar-link.ytd-rich-grid-video-renderer
				{
					display: none;
				}
			`);
        }

        if(LessenVideoTitleTopMarginOnHomePage)
        {
            addGlobalStyle(`
				h3.ytd-rich-grid-video-renderer
				{
					margin-top: 6px;
				}
			`);
        }

        if(FPPCompOn) {
			addGlobalStyle(`
				/*========== Fade++ Compatibility ==========*/
				ytd-app #page-manager > ytd-browse:not([page-subtype="playlist"]) {
					display: block;
				}
				ytd-app[guide-persistent-and-visible] #page-manager > ytd-browse:not([page-subtype="playlist"]) ytd-two-column-browse-results-renderer.ytd-browse
				{
					margin-left: 250px !important;
				}
			`);
            console.log("Youtube Wide video container Fade++ compatibilty style added to DOM");
        }
    }

    /*=== load more videos for channel sections (experimental) ===*/
    var lastCheckedURL = window.location.href;
    URLChanged();
    setInterval(function(){
        if(lastCheckedURL != window.location.href)
        {
            lastCheckedURL = window.location.href;
            //lol why am I doing this 3 times at different times here??? Should have probably commented this better huh?
            //leaving it as is for now. Should probably figure out something better, this is a mess...
            setTimeout(URLChanged, 500);
            setTimeout(URLChanged, 2000);
            setTimeout(URLChanged, 5000);
        }
    }, 200);

    var waitForArrows;
    function URLChanged()
    {
        //console.log("-urlchanged-");
        clearInterval(waitForArrows);
        let expandedEls = document.getElementsByClassName("expanded-wwc");
        //console.log("expanded els found: " + expandedEls.length);
        let numRemoved = 0;

        //seems to always remove exactly half of them only, for some reason. So I guess do this until all have been removed
        while(expandedEls.length > 0)
        {
            for(let x = 0; x < expandedEls.length; x++)
            {
                if(!!expandedEls[x])
                {
                    expandedEls[x].classList.remove("expanded-wwc");
                    //console.log(++numRemoved + " cleared");
                }
            }
            expandedEls = document.getElementsByClassName("expanded-wwc");
        }
        numRemoved = 0;
        let clickedArrows = document.getElementsByClassName("clicked");
        //console.log("clicked found: " + clickedArrows.length);
        while(clickedArrows.length > 0)
        {
            for(let x = 0; x < clickedArrows.length; x++)
            {
                if(!!clickedArrows[x])
                {
                    clickedArrows[x].classList.remove("clicked");
                    //console.log(++numRemoved + " cleared");
                }
            }
            clickedArrows = document.getElementsByClassName("clicked");
        }

        //console.log("-expandedclear-");
        if(lastCheckedURL.includes("/user/") || lastCheckedURL.includes("/channel/"))
        {
            waitForArrows = setInterval(function(){
                //console.log("-searching...-");
                let arrowsRight = document.querySelectorAll("yt-horizontal-list-renderer:not(.expanded-wwc) > #right-arrow > ytd-button-renderer.arrow");
                let arrowsLeft = document.querySelectorAll("yt-horizontal-list-renderer:not(.expanded-wwc) > #left-arrow > ytd-button-renderer.arrow");
                if(!!arrowsRight && arrowsRight.length > 0 && !!arrowsLeft && arrowsLeft.length > 0)
                {
                    //console.log("-found-");
                    for(let i = 0; i < arrowsRight.length; i++)
                    {
                        if(!!arrowsRight[i] && arrowsRight[i].offsetParent !== null && !!arrowsLeft[i] && arrowsLeft[i].offsetParent !== null)
                        {
                            arrowsRight[i].parentElement.parentElement.classList.add("expanded-wwc");
                            arrowsRight[i].click();
                            //console.log("simulated click on right arrow");
                            arrowsRight[i].classList.add("clicked");
                            arrowsLeft[i].click();
                            //console.log("simulated click on left arrow");
                            arrowsLeft[i].classList.add("clicked");
                        }
                    }
                }
            }, 250);
        }
    }

    /*============================================================*/

    function SetFPPHandle() {
        GM_unregisterMenuCommand(FPPHandle);

        FPPHandle = GM_registerMenuCommand("Fade++ Compatibility mode (" + (FPPCompOn ? "On" : "Off") + ") -click to change-", function(){
            FPPCompOn = !FPPCompOn;
            GM_setValue("FPPCompOn", FPPCompOn);
            SetFPPHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }
	function SetForceCSSHandle() {
        GM_unregisterMenuCommand(ForceCSSHandle);

        ForceCSSHandle = GM_registerMenuCommand("CSS important rule mode (" + (ForceCSSOn ? "On" : "Off") + ") -click to change-", function(){
            ForceCSSOn = !ForceCSSOn;
            GM_setValue("ForceCSSOn", ForceCSSOn);
            SetForceCSSHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }
    function SetHomeVideoContainerWidthHandle() {
        GM_unregisterMenuCommand(HomeVideoContainerWidthHandle);

        HomeVideoContainerWidthHandle = GM_registerMenuCommand("[home-page] Video-renderer width (" + HomeVideoContainerWidth + ") -click to change-", function(){
            HomeVideoContainerWidth = CleanCSSValue(prompt("Set the width of a single video renderer on the page (use CSS units)\nThe current value is: '" + HomeVideoContainerWidth + "'"));
            GM_setValue("HomeVideoContainerWidth", HomeVideoContainerWidth);
            SetHomeVideoContainerWidthHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }
    /*function SetHQTNHandle() {
        GM_unregisterMenuCommand(HQTNHandle);

        HQTNHandle = GM_registerMenuCommand("[subs-page] Load HQ thumbnails (" + (HQTN ? "On" : "Off") + ") -click to change-", function(){
            HQTN = !HQTN;
            GM_setValue("HQTN", HQTN);
            SetHQTNHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }*/
    function SetSubVideoContainerWidthHandle() {
        GM_unregisterMenuCommand(SubVideoContainerWidthHandle);

        SubVideoContainerWidthHandle = GM_registerMenuCommand("[subs-page] Video-renderer width (" + SubVideoContainerWidth + ") -click to change-", function(){
            SubVideoContainerWidth = CleanCSSValue(prompt("Set the width of a single video renderer on the page (use CSS units)\nNote: Currently values larger than the default (210px) make the thumbnails blurry, as youtube only loads 210px resolution images. I might make a workaround for this later.\nThe current value is: '" + SubVideoContainerWidth + "'"));
            GM_setValue("SubVideoContainerWidth", SubVideoContainerWidth);
            SetSubVideoContainerWidthHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }
    function SetTrendingVideoContainerWidthHandle() {
        GM_unregisterMenuCommand(TrendingVideoContainerWidthHandle);

        TrendingVideoContainerWidthHandle = GM_registerMenuCommand("[trending-page] Video-renderer width (" + TrendingVideoContainerWidth + ") -click to change-", function(){
            TrendingVideoContainerWidth = CleanCSSValue(prompt("Set the width of a single video renderer on the page (use CSS units)\nThe current value is: '" + TrendingVideoContainerWidth + "'"));
            GM_setValue("TrendingVideoContainerWidth", TrendingVideoContainerWidth);
            SetTrendingVideoContainerWidthHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }
    function SetVideoContainerHorMarginHandle() {
        GM_unregisterMenuCommand(VideoContainerHorMarginHandle);

        VideoContainerHorMarginHandle = GM_registerMenuCommand("[all pages] Video-renderer horizontal margin (" + VideoContainerHorMargin + ") -click to change-", function(){
            VideoContainerHorMargin = CleanCSSValue(prompt("Set the horizontal margin of a single video renderer on the page. i.e. the space between videos (use CSS units)\nThe current value is: '" + VideoContainerHorMargin + "'"));
            GM_setValue("VideoContainerHorMargin", VideoContainerHorMargin);
            SetVideoContainerHorMarginHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }
    function SetVideoContainerVerMarginHandle() {
        GM_unregisterMenuCommand(VideoContainerVerMarginHandle);

        VideoContainerVerMarginHandle = GM_registerMenuCommand("[all pages] Video-renderer vertical margin (" + VideoContainerVerMargin + ") -click to change-", function(){
            VideoContainerVerMargin = CleanCSSValue(prompt("Set the vertical margin of a single video renderer on the page. i.e. the space between videos (use CSS units)\nThe current value is: '" + VideoContainerVerMargin + "'"));
            GM_setValue("VideoContainerVerMargin", VideoContainerVerMargin);
            SetVideoContainerVerMarginHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }
    function SetShowChannelIconNextToVideosOnHomePageHandle() {
        GM_unregisterMenuCommand(ShowChannelIconNextToVideosOnHomePageHandle);

        ShowChannelIconNextToVideosOnHomePageHandle = GM_registerMenuCommand("[homepage] Show channel icons next to videos (" + (ShowChannelIconNextToVideosOnHomePage ? "Yes" : "No") + ") -click to change-", function(){
            ShowChannelIconNextToVideosOnHomePage = !ShowChannelIconNextToVideosOnHomePage;
            GM_setValue("ShowChannelIconNextToVideosOnHomePage", ShowChannelIconNextToVideosOnHomePage);
            SetShowChannelIconNextToVideosOnHomePageHandle();

            if(confirm('Press "OK" to refresh the page to apply new settings'))
                location.reload();
        });
    }

    function CleanCSSValue(val)
    {
        val = val.trim();

        //if only numbers...
        if(/^\d+$/.test(val))
            val += "px"; //...add px

        return val;
    }

    function addGlobalStyle(css) {
        var head, style;
        head = document.getElementsByTagName('head')[0];
        if (!head) { return; }
        style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = css;
        head.appendChild(style);
    }
})();