Change YouTube logo link to user's subscription feed instead of homepage, when logged in, for 2017 and later YouTube layout.
// ==UserScript==
// @name Youtube Logo - Link to subscriptions feed
// @namespace Youtube Logo - Link to subscriptions feed
// @description Change YouTube logo link to user's subscription feed instead of homepage, when logged in, for 2017 and later YouTube layout.
// @version 2.1.5
// @include *://*.youtube.tld/*
// @supportURL https://greasyfork.org/en/scripts/13582/feedback
// @author aciid
// @license MIT
// @run-at document-start
// ==/UserScript==
(function() {
"use strict";
var SUBSCRIPTIONS_PATH = "/feed/subscriptions";
var SUBSCRIPTIONS_BROWSE_ID = "FEsubscriptions";
var DEFAULT_SUBSCRIPTIONS_NAVIGATION_ENDPOINT = {
clickTrackingParams: "",
commandMetadata: {
webCommandMetadata: {
url: SUBSCRIPTIONS_PATH,
webPageType: "WEB_PAGE_TYPE_BROWSE",
rootVe: 96368,
apiUrl: "/youtubei/v1/browse"
}
},
browseEndpoint: {
browseId: SUBSCRIPTIONS_BROWSE_ID
}
};
var LOGO_SELECTOR = "a#logo[href]";
var SUBSCRIPTIONS_ENDPOINT_SELECTOR = "a#endpoint[href]";
var ORIGINAL_HREF_ATTRIBUTE = "data-ytltsf-original-href";
var updateScheduled = false;
var observedMasthead = null;
var cachedSubscriptionsNavigationEndpoint = null;
var bootstrapObserver = new MutationObserver(ScheduleUpdate);
var mastheadObserver = new MutationObserver(ScheduleUpdate);
function NormalizePath(pathname) {
return pathname.replace(/\/+$/, "") || "/";
}
function IsSubscriptionsUrl(url) {
try {
return NormalizePath(new URL(url, location.origin).pathname) === SUBSCRIPTIONS_PATH;
} catch (error) {
return false;
}
}
function IsLoggedIn() {
return document.querySelector("button#avatar-btn") !== null;
}
function IsTopbarLogo(node) {
return node instanceof HTMLAnchorElement &&
node.id === "logo" &&
node.closest("ytd-masthead, ytd-topbar-logo-renderer, ytd-yoodle-renderer") !== null;
}
function FindLogoFromEvent(event) {
if (typeof event.composedPath === "function") {
var path = event.composedPath();
for (var i = 0; i < path.length; i++) {
var node = path[i];
if (IsTopbarLogo(node)) {
return node;
}
}
}
if (event.target instanceof Element) {
var logo = event.target.closest(LOGO_SELECTOR);
if (IsTopbarLogo(logo)) {
return logo;
}
}
return null;
}
function IsPlainLeftClick(event) {
return event.button === 0 &&
!event.defaultPrevented &&
!event.altKey &&
!event.ctrlKey &&
!event.metaKey &&
!event.shiftKey;
}
function FindSubscriptionsEndpoint() {
var endpoints = document.querySelectorAll(SUBSCRIPTIONS_ENDPOINT_SELECTOR);
for (var i = 0; i < endpoints.length; i++) {
if (endpoints[i] instanceof HTMLAnchorElement && IsSubscriptionsUrl(endpoints[i].href)) {
return endpoints[i];
}
}
return null;
}
function DeepClone(value) {
if (typeof structuredClone === "function") {
return structuredClone(value);
}
return JSON.parse(JSON.stringify(value));
}
function GetRendererData(element) {
if (element === null || element === undefined) {
return null;
}
return element.data || element.polymerController?.data || element.inst?.data || null;
}
function FindSubscriptionsNavigationEndpointInData() {
var renderers = document.querySelectorAll("ytd-guide-entry-renderer, ytd-guide-collapsible-section-entry-renderer, ytd-mini-guide-entry-renderer");
for (var i = 0; i < renderers.length; i++) {
var data = GetRendererData(renderers[i]);
var endpoint = data?.navigationEndpoint || data?.endpoint || null;
if (endpoint?.browseEndpoint?.browseId === SUBSCRIPTIONS_BROWSE_ID ||
NormalizePath(endpoint?.commandMetadata?.webCommandMetadata?.url || "") === SUBSCRIPTIONS_PATH) {
return endpoint;
}
}
return null;
}
function GetSubscriptionsNavigationEndpoint() {
var discoveredEndpoint = FindSubscriptionsNavigationEndpointInData();
if (discoveredEndpoint !== null) {
cachedSubscriptionsNavigationEndpoint = DeepClone(discoveredEndpoint);
}
return DeepClone(cachedSubscriptionsNavigationEndpoint || DEFAULT_SUBSCRIPTIONS_NAVIGATION_ENDPOINT);
}
function GetNavigationTargets() {
var app = document.querySelector("ytd-app");
var targets = [app];
if (app !== null) {
targets.push(app.polymerController, app.inst);
}
return targets.filter(function(target, index) {
return target !== null && target !== undefined && targets.indexOf(target) === index;
});
}
function HandleSubscriptionsNavigationWithApp() {
var targets = GetNavigationTargets();
var endpoint = GetSubscriptionsNavigationEndpoint();
var requestFactories = [
function() {
return {
type: 0,
command: DeepClone(endpoint)
};
},
function() {
return {
type: 0,
endpoint: DeepClone(endpoint),
command: DeepClone(endpoint)
};
}
];
for (var i = 0; i < targets.length; i++) {
var target = targets[i];
for (var j = 0; j < requestFactories.length; j++) {
try {
if (typeof target.handleNavigate === "function") {
target.handleNavigate(requestFactories[j]());
return true;
}
} catch (error) {
// Try the next request shape or target.
}
}
}
return false;
}
function NavigateToSubscriptions(attempt) {
var endpoint = FindSubscriptionsEndpoint();
if (endpoint !== null) {
endpoint.click();
return;
}
if (attempt === 0 || attempt === 10) {
HandleSubscriptionsNavigationWithApp();
}
if (attempt < 20 && NormalizePath(location.pathname) !== SUBSCRIPTIONS_PATH) {
requestAnimationFrame(function() {
NavigateToSubscriptions(attempt + 1);
});
}
}
function UpdateLogoHref(logo, loggedIn) {
if (!logo.hasAttribute(ORIGINAL_HREF_ATTRIBUTE)) {
var originalHref = logo.getAttribute("href");
if (originalHref !== null) {
logo.setAttribute(ORIGINAL_HREF_ATTRIBUTE, originalHref);
}
}
if (loggedIn) {
if (!IsSubscriptionsUrl(logo.getAttribute("href") || "")) {
logo.setAttribute("href", SUBSCRIPTIONS_PATH);
}
return;
}
if (logo.hasAttribute(ORIGINAL_HREF_ATTRIBUTE)) {
logo.setAttribute("href", logo.getAttribute(ORIGINAL_HREF_ATTRIBUTE));
}
}
function UpdateLogos() {
updateScheduled = false;
RefreshObservedMasthead();
GetSubscriptionsNavigationEndpoint();
var logos = Array.from(document.querySelectorAll(LOGO_SELECTOR)).filter(IsTopbarLogo);
if (logos.length === 0) {
return;
}
var loggedIn = IsLoggedIn();
logos.forEach(function(logo) {
UpdateLogoHref(logo, loggedIn);
});
}
function RefreshObservedMasthead() {
var nextMasthead = document.querySelector("#masthead-container") || document.querySelector("ytd-masthead");
if (nextMasthead === observedMasthead) {
return;
}
mastheadObserver.disconnect();
observedMasthead = nextMasthead;
if (observedMasthead !== null) {
bootstrapObserver.disconnect();
mastheadObserver.observe(observedMasthead, {
childList: true,
subtree: true
});
}
}
function ScheduleUpdate() {
if (updateScheduled) {
return;
}
updateScheduled = true;
requestAnimationFrame(UpdateLogos);
}
function OnDocumentClick(event) {
var logo = FindLogoFromEvent(event);
if (logo === null || !IsLoggedIn() || !IsPlainLeftClick(event)) {
return;
}
event.preventDefault();
event.stopImmediatePropagation();
NavigateToSubscriptions(0);
}
document.addEventListener("click", OnDocumentClick, true);
document.addEventListener("DOMContentLoaded", ScheduleUpdate, { once: true });
["yt-navigate-finish", "yt-page-data-updated"].forEach(function(eventName) {
document.addEventListener(eventName, ScheduleUpdate, true);
window.addEventListener(eventName, ScheduleUpdate, true);
});
bootstrapObserver.observe(document.documentElement, {
childList: true,
subtree: true
});
ScheduleUpdate();
})();