您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Sorts the YouTube feed so that all scheduled streams and premieres come before archived videos.
当前为
// ==UserScript== // @name YT Feed Sorter // @namespace YTFeedSorter // @version 0.7 // @description Sorts the YouTube feed so that all scheduled streams and premieres come before archived videos. // @match *://*.youtube.com/* // @author KFP // ==/UserScript== (function() { 'use strict'; const getTime = (id, isList) => { try { const allItems = isList ? ytInitialData.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents : ytInitialData.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.richGridRenderer.contents; const item = allItems.find(i => { try { const data = isList ? i.itemSectionRenderer.contents[0].shelfRenderer.content.expandedShelfContentsRenderer.items[0].videoRenderer : i.richItemRenderer.content.videoRenderer; return data.videoId === id; } catch(e) { return false; } }); if (!item) return 0; const time = isList ? item.itemSectionRenderer.contents[0].shelfRenderer.content.expandedShelfContentsRenderer.items[0].videoRenderer.upcomingEventData.startTime : item.richItemRenderer.content.videoRenderer.upcomingEventData.startTime; return time + id; } catch(e) { console.log('YTFS getTime error', e); return 0; } }; const sortFeed = feed => { try{ const isList = feed.classList.contains('ytd-section-list-renderer'); if (isList) { [...feed.children].sort((a, b) => { const aNotItem = (a.tagName.toLowerCase() !== 'ytd-item-section-renderer') || a.querySelector('#title-container.style-scope.ytd-reel-shelf-renderer'); const bNotItem = (b.tagName.toLowerCase() !== 'ytd-item-section-renderer') || b.querySelector('#title-container.style-scope.ytd-reel-shelf-renderer'); if (aNotItem || bNotItem) return 0; const aInner = a.querySelector('ytd-shelf-renderer').querySelector('ytd-expanded-shelf-contents-renderer').querySelector('ytd-video-renderer'); const bInner = b.querySelector('ytd-shelf-renderer').querySelector('ytd-expanded-shelf-contents-renderer').querySelector('ytd-video-renderer'); const aLive = aInner.querySelector('#badges:not([hidden])')?.querySelector('.badge-style-type-live-now-alternate'); const aSoon = aInner.querySelector('ytd-toggle-button-renderer'); const bLive = bInner.querySelector('#badges:not([hidden])')?.querySelector('.badge-style-type-live-now-alternate'); const bSoon = bInner.querySelector('ytd-toggle-button-renderer'); if (aSoon && bSoon) { const aId = aInner.querySelector('A#thumbnail').href.split('v=')[1]; const bId = bInner.querySelector('A#thumbnail').href.split('v=')[1]; const aTime = getTime(aId, true); const bTime = getTime(bId, true); return (aTime > bTime) ? 1 : -1; } else { const ai = aLive ? 2 : aSoon ? 1 : 0; const bi = bLive ? 2 : bSoon ? 1 : 0; return (ai > bi) ? -1 : (ai < bi) ? 1 : 0; } }).forEach(item => feed.appendChild(item)); } else { const rows = feed.querySelectorAll('ytd-rich-grid-row'); let currentRowI = 0; let currentRow = rows[0].querySelector('#contents'); let rowLength = currentRow.children.length; let items = []; rows.forEach(row => { const rowItems = row.querySelectorAll('ytd-rich-item-renderer'); items = items.concat([...rowItems]); }); items.sort((a, b) => { const aInner = a.querySelector('ytd-rich-grid-media'); const bInner = b.querySelector('ytd-rich-grid-media'); const aLive = aInner.querySelector('.video-badge:not([hidden])')?.querySelector('.badge-style-type-live-now-alternate'); const aSoon = aInner.querySelector('ytd-toggle-button-renderer'); const bLive = bInner.querySelector('.video-badge:not([hidden])')?.querySelector('.badge-style-type-live-now-alternate'); const bSoon = bInner.querySelector('ytd-toggle-button-renderer'); if (aSoon && bSoon) { const aId = aInner.querySelector('A#thumbnail').href.split('v=')[1]; const bId = bInner.querySelector('A#thumbnail').href.split('v=')[1]; const aTime = getTime(aId); const bTime = getTime(bId); return (aTime > bTime) ? 1 : -1; } else { const ai = aLive ? 2 : aSoon ? 1 : 0; const bi = bLive ? 2 : bSoon ? 1 : 0; return (ai > bi) ? -1 : (ai < bi) ? 1 : 0; } }).forEach((item, i) => { currentRow.appendChild(item); if (i && ((i + 1) % rowLength === 0)) { currentRowI++; if (rows[currentRowI]) { currentRow = rows[currentRowI].querySelector('#contents'); } } }); } } catch(e) { console.log('YTFS sortFeed error', e); } }; const gridObserver = new MutationObserver(mutations => { for (const mut of mutations) { if (mut.removedNodes?.length) { setTimeout(() => sortFeed(mut.target), 200); setTimeout(() => sortFeed(mut.target), 2000); setTimeout(() => sortFeed(mut.target), 4000); } } }); let listObserverTimer = 0; const listObserver = new MutationObserver(mutations => { for (const mut of mutations) { if (mut.addedNodes?.length) { const now = Date.now(); if (now - listObserverTimer > 5200) { setTimeout(() => sortFeed(mut.target), 200); setTimeout(() => sortFeed(mut.target), 5000); listObserverTimer = now; } } } }); let feedSorted = false; let pageChanged = false; setInterval(() => { let isList = false; let feed = document.querySelector('ytd-browse[page-subtype="subscriptions"][role="main"] #contents.ytd-rich-grid-renderer'); if (!feed) { feed = document.querySelector('ytd-browse[page-subtype="subscriptions"][role="main"] #contents.ytd-section-list-renderer'); if (feed) isList = true; } if (feed) { if (feedSorted) return; if (isList) { setTimeout(() => sortFeed(feed), 200); setTimeout(() => sortFeed(feed), 2000); listObserver.observe(feed, {childList: true}); } else { if (pageChanged) { setTimeout(() => sortFeed(feed), 1000); setTimeout(() => sortFeed(feed), 3000); setTimeout(() => sortFeed(feed), 6000); } else { setTimeout(() => sortFeed(feed), 1); } gridObserver.observe(feed, {childList: true}); } feedSorted = true; } else if (feedSorted) { gridObserver.disconnect(); listObserver.disconnect(); pageChanged = true; feedSorted = false; } }, 100); })();