您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Change YouTube leftbar's subscription links to channel/user video page. This script can optionally also move updated links to top of the list (if there's enough space), optionally uncollapse all updated links which may extend the non collapsible links, and optionally display the number of updated & total links following the "SUBSCRIPTION" section label. All features can be enabled/disabled from the script code. For new YouTube layout only.
当前为
- // ==UserScript==
- // @name Change YouTube Leftbar Subscription Links To Channel/User Video Page
- // @namespace ChangeYouTubeLeftbarSubscriptionLinksToChannelUserVideoPage
- // @version 1.3.19
- // @license AGPL v3
- // @author jcunews
- // @description Change YouTube leftbar's subscription links to channel/user video page. This script can optionally also move updated links to top of the list (if there's enough space), optionally uncollapse all updated links which may extend the non collapsible links, and optionally display the number of updated & total links following the "SUBSCRIPTION" section label. All features can be enabled/disabled from the script code. For new YouTube layout only.
- // @website https://greasyfork.org/en/users/85671-jcunews
- // @match *://www.youtube.com/*
- // @grant none
- // @run-at document-start
- // ==/UserScript==
- (yigd => {
- //===== Configuration Start ===
- var changeLinksURL = true; //Change links' URLs from the Home tab, to the Videos tab.
- var moveUpdatedLinksToTop = true; //Move updated links to top of list without uncollapsing them.
- var UncollapseUpdatedLinks = true; //Uncollapse updated links. This may extend the length of uncollapsed links.
- //If enabled, it will implicitly enable moveUpdatedLinksToTop.
- var showLinksCount = true; //Display number of updated links and total links following the "SUBSCRIPTION" section label.
- //===== Configuration End ===
- function changeUrl(url, m) {
- if (m = url.match(/^\/feed\/subscriptions\/([^\/]+)$/)) {
- return "/channel/" + m[1] + "/videos";
- } else return url + "/videos";
- }
- function updateSubscriptionCount(a, u, t) {
- function count(v, w) {
- if ((w = v.guideEntryRenderer) && w.entryData) {
- t++;
- if (w.presentationStyle === "GUIDE_ENTRY_PRESENTATION_STYLE_NEW_CONTENT") u++;
- } else if ((w = v.guideCollapsibleEntryRenderer) && (w = w.expandableItems) && w.forEach) {
- w.forEach(count)
- }
- }
- if (!window.ytlssc_ujs && (a = document.querySelector("ytd-guide-section-renderer:nth-child(2) #guide-section-title"))) {
- a.insertAdjacentHTML("beforeend", ' (<span id=ytlssc_ujs>-/-</span>)')
- }
- if (window.ytlssc_ujs) {
- u = 0; t = 0;
- if (a && (a = a.parentNode.parentNode.__data) && (a = a.shownItems) && a.forEach) a.forEach(count);
- ytlssc_ujs.textContent = u + "/" + t
- } else setTimeout(updateSubscriptionCount, 500)
- }
- 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, u) => {
- 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 (moveUpdatedLinksToTop) {
- v = v.guideSubscriptionsSectionRenderer.items; //v = main links container. includes collapsed links container wrapper at end
- vc = v.length - 1; //vc = main links count
- w = v[vc].guideCollapsibleEntryRenderer; //w = collapsed links container wrapper
- l = v.splice(0, vc); //l = list1. move main links into list1. list1 now: mainLinks
- c = -1;
- if (w) { //has collapsed links container? w = collapsed links container
- (w = w.expandableItems).some((e, i) => { //count collapsed links
- if (!e.guideEntryRenderer.entryData) {
- c = i;
- return true;
- }
- });
- l.push.apply(l, w.splice(0, c)); //append collapsed links to list1. list1 now: mainLinks, collapsedLinks
- }
- c = []; //c = list2 = collapsed new links
- for (i = l.length - 1; i >= 0; i--) { //move new links in list1 into main links container
- u = l[i].guideEntryRenderer.presentationStyle === "GUIDE_ENTRY_PRESENTATION_STYLE_NEW_CONTENT";
- if (l[i].guideEntryRenderer.count || u) {
- if ((u = UncollapseUpdatedLinks && u) || vc--) {
- v.unshift(l.splice(i, 1)[0]);
- if (u) vc--;
- } else c.unshift(l.splice(i, 1)[0]);
- }
- }
- c.push.apply(c, l); //append any remaining list1 links (non updated links) into list2
- if (vc > 0) { //original-length main links container still has free slot?
- l = c.splice(0, vc); //move collapsed links in list2 into list1. same count as main links container free slots
- l.unshift.apply(l, [v.length - 1, 0]); //prepare arguments for below task
- v.splice.apply(v, l); //move collapsed links in list1 into main links container
- }
- if (w) w.unshift.apply(w, c); //if has collapsed links container, move remaining collapsed links in list2 into it
- }
- }
- });
- updateSubscriptionCount();
- 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 frtext = fresp.text;
- fresp.text = function() {
- var tres = frtext.apply(this, arguments), tthen = tres.then;
- tres.then = function(tfn) {
- var tthenfn = tfn;
- tfn = function(tresp) {
- var z, tr;
- try {
- tr = JSON.parse_cylslcvp(tresp);
- patchGuide(tr);
- tresp = JSON.stringify(tr);
- } catch(z) {}
- return tthenfn.apply(this, arguments);
- };
- return tthen.apply(this, arguments);
- };
- return tres;
- }
- 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);
- 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;
- };
- addEventListener("click", (ev, a) => {
- if ((a = ev.target) && a.matches && a.matches("ytd-guide-section-renderer:nth-child(2) ytd-guide-entry-renderer *")) {
- while (a.tagName !== "A") a = a.parentNode;
- if (/^\/(c|channel|u|user)\//.test(a.pathname)) {
- a = a.__dataHost.__data.data.navigationEndpoint;
- ev.stopImmediatePropagation();
- ev.preventDefault();
- a.browseEndpoint.params = encodeURIComponent(btoa("\x12\x06videos"));
- a.browseEndpoint.canonicalBaseUrl = a.commandMetadata.webCommandMetadata.url.substring(0, a.commandMetadata.webCommandMetadata.url.lastIndexOf("/"));
- nav.navigate(a, false, undefined, {});
- }
- }
- }, true);
- })();