// ==UserScript==
// @name Change YouTube Leftbar Subscription Links To Channel/User Video Page
// @namespace ChangeYouTubeLeftbarSubscriptionLinksToChannelUserVideoPage
// @version 1.1.14
// @license AGPL v3
// @author jcunews
// @description Change YouTube leftbar's subscription links to channel/user video page. This script can optionally also move the links to top of the list if it has new uploaded videos. Both features can be enabled/disabled. For new YouTube layout only.
// @match *://www.youtube.com/*
// @grant none
// @run-at document-start
// ==/UserScript==
(yigd => {
//===== Configuration Start ===
var changeLinksURL = true;
var moveLinksToTop = true;
//===== Configuration End ===
function changeUrl(url, m) {
if (m = url.match(/^\/feed\/subscriptions\/([^\/]+)$/)) {
return "/channel/" + m[1] + "/videos";
} else return url + "/videos";
}
function updateEndpoint(ep) {
ep.commandMetadata.webCommandMetadata.url = changeUrl(ep.commandMetadata.webCommandMetadata.url);
if (ep.webNavigationEndpointData) ep.webNavigationEndpointData.url = changeUrl(ep.webNavigationEndpointData.url);
(function waitGD(gd) {
if ((gd = window["guide-renderer"].__data) && (gd = gd.data) && (gd = gd.items) && (gd = gd.reduce(
(r, v) => {
if (!r && (v = v.guideSubscriptionsSectionRenderer) && (v = v.items)) {
r = v.reduce((r, e) => {
if (e.guideCollapsibleEntryRenderer) {
e.guideCollapsibleEntryRenderer.expandableItems.forEach(t => {
if (!t.guideEntryRenderer.icon || (t.guideEntryRenderer.icon.iconType !== "ADD_CIRCLE")) r.push(t.guideEntryRenderer.navigationEndpoint)
});
} else r.push(e.guideEntryRenderer.navigationEndpoint);
return r;
}, []);
}
return r;
}, null
))) {
fetch(location.protocol + "//" + location.host + ep.commandMetadata.webCommandMetadata.url, {credentials: "omit"}).then(r => r.text().then((h, o) => {
if ((h = h.match(/window\["ytInitialData"\] = (\{.*?)\};\n/)) && (h = JSON.parse(h[1] + "}").contents.twoColumnBrowseResultsRenderer.tabs)) {
h.some(a => {
if ((/^\/[^\/]+\/[^\/]+\/videos$/).test((a = a.tabRenderer.endpoint).commandMetadata.webCommandMetadata.url)) {
ep.browseEndpoint.params = a.browseEndpoint.params;
ep.commandMetadata.webCommandMetadata.url = a.commandMetadata.webCommandMetadata.url;
if (ep.webNavigationEndpointData) ep.webNavigationEndpointData.url = a.commandMetadata.webCommandMetadata.url;
gd.some(d => {
if (d.browseEndpoint.browseId === a.browseEndpoint.browseId) {
d.browseEndpoint.params = a.browseEndpoint.params;
d.commandMetadata.webCommandMetadata.url = a.commandMetadata.webCommandMetadata.url;
if (d.webNavigationEndpointData) d.webNavigationEndpointData.url = a.commandMetadata.webCommandMetadata.url;
return true;
}
});
return true;
}
})
}
}))
} else setTimeout(waitGD, 100)
})()
}
function patchGuide(guide, z) {
if (guide && guide.items && !guide.cysl_done) try {
guide.cysl_done = 1;
guide.items.forEach((v, vc, l, w, c, i) => {
if (v.guideSubscriptionsSectionRenderer) {
//change links' URL
if (changeLinksURL) {
v.guideSubscriptionsSectionRenderer.items.forEach(w => {
if (w.guideCollapsibleEntryRenderer) {
w.guideCollapsibleEntryRenderer.expandableItems.forEach(x => {
if (x.guideEntryRenderer.badges && (x = x.guideEntryRenderer.navigationEndpoint)) updateEndpoint(x);
});
} else if (w = w.guideEntryRenderer.navigationEndpoint) updateEndpoint(w);
});
}
//move links with new uploads to top
if (moveLinksToTop) {
v = v.guideSubscriptionsSectionRenderer.items;
vc = v.length - 1;
w = v[vc].guideCollapsibleEntryRenderer;
l = v.splice(0, vc); //main links to list1
c = -1;
if (w) { //collapsed links to list1
(w = w.expandableItems).some((e, i) => {
if (!e.guideEntryRenderer.badges) {
c = i;
return true;
}
});
l.push.apply(l, w.splice(0, c));
}
c = []; //new links to main links. c = collapsed new links in list2
for (i = l.length - 1; i >= 0; i--) {
if (l[i].guideEntryRenderer.count || (l[i].guideEntryRenderer.presentationStyle === "GUIDE_ENTRY_PRESENTATION_STYLE_NEW_CONTENT")) {
if (vc--) {
v.unshift(l.splice(i, 1)[0]);
} else c.unshift(l.splice(i, 1)[0]);
}
}
c.push.apply(c, l); //list1 to list2
if (vc) { //fill up main links from list2
l = c.splice(0, vc);
l.unshift.apply(l, [v.length - 1, 0]);
v.splice.apply(v, l);
}
if (w) w.unshift.apply(w, c);
}
}
});
return true;
} catch(z) {}
return false;
}
Object.defineProperty(window, "ytInitialGuideData", {
get(v) {
return yigd;
},
set(v) {
delete window.ytInitialGuideData;
patchGuide(v);
return yigd = v;
}
});
JSON.parse_cylslcvp = JSON.parse;
JSON.parse = function(txt) {
var res = JSON.parse_cylslcvp.apply(this, arguments);
if ((/\/youtubei\/v1\/guide\?/).test(JSON.url_cylslcvp)) patchGuide(res);
return res;
};
var fetch_ = window.fetch;
window.fetch = function(opts) {
var fres = fetch_.apply(this, arguments), fthen = fres.then;
if ((/\/youtubei\/v1\/guide\?/).test(opts.url)) {
JSON.url_cylslcvp = opts.url;
fres.then = function(fn) {
var fthenfn = fn;
fn = function(fresp) {
var frjson = fresp.json;
fresp.json = function() {
var jres = frjson.apply(this, arguments), jthen = jres.then;
jres.then = function(jfn) {
var jthenfn = jfn;
jfn = function(jresp) {
patchGuide(jresp);
console.log(jresp);
return jthenfn.apply(this, arguments);
};
return jthen.apply(this, arguments);
};
return jres;
}
return fthenfn.apply(this, arguments);
};
return fthen.apply(this, arguments);
};
} else JSON.url_cylslcvp = "";
return fres;
};
var ht = 0;
(function chkSpf() {
clearTimeout(ht);
if (window.spf && spf.request && !spf.request_cysl) {
spf.request_cysl = spf.request;
spf.request = function(a, b) {
if (b && b.onDone) {
var onDone_ = b.onDone;
b.onDone = function(response) {
if (response && (/\/guide_ajax\?/).test(response.url) && response.response && response.response.response) {
patchGuide(response.response.response);
}
return onDone_.apply(this, arguments);
};
return this.request_cysl.apply(this, arguments);
}
};
return;
}
ht = setTimeout(chkSpf, 0);
})();
addEventListener("click", (ev, a) => {
if ((a = ev.target) && a.matches && a.matches("ytd-guide-section-renderer:nth-child(2) ytd-guide-entry-renderer *")) {
ev.stopImmediatePropagation();
ev.preventDefault();
while (a.tagName !== "A") a = a.parentNode;
a = a.__dataHost.__data.data.navigationEndpoint;
nav.navigate({
commandMetadata: {webCommandMetadata: {url: a.commandMetadata.webCommandMetadata.url, webPageType: "WEB_PAGE_TYPE_CHANNEL"}},
browseEndpoint: a.browseEndpoint
}, false);
}
}, true);
addEventListener("load", () => {
clearTimeout(ht);
});
})();