const targetRootId = 'contents'; const lastVideoSelector = 'ytd-rich-grid-row:last-of-type ytd-rich-item-renderer:last-child'; const allVideosSelector = `.ytd-rich-grid-renderer#${targetRootId} ytd-rich-grid-media`; const rootNodeSelector = `ytd-rich-grid-renderer > #${targetRootId}`; const delay = (ms = 100) => new Promise((resolve) => { setTimeout(() => { resolve(true); }, ms) }); (async function() { let neededVideoId = prompt('Enter id of the last video', ''); if (!neededVideoId) { alert('The last vidoe id can not be empty'); return; } const checkWatched = async () => { await delay(3e3); console.log('find video by id:', neededVideoId); const found = document.querySelector(`a[href^="/watch?v=${neededVideoId}"]`); if (found) { console.log('found, stop observer'); observer.disconnect(); stageSecond(); return; } const lastVideo = document.querySelector(lastVideoSelector); console.log('last video:', lastVideo); if (lastVideo) { lastVideo.scrollIntoView(); } }; let timer; const runWaiter = () => { if (timer) { clearTimeout(timer); } timer = setTimeout(checkWatched, 2e3); }; const stageSecond = async () => { let videos = Array.from(document.querySelectorAll(allVideosSelector)); if (neededVideoId) { const index = videos.findIndex(item => item.data.videoId === neededVideoId); videos.splice(index, videos.length); } videos = videos .filter(item => !item.data.isWatched) .reverse() .map(video => `${video.data.videoId}`) ; document.body.innerHTML = `
            ${videos.join('\n')}
          
`; }; const itemsContainer = document.querySelector(rootNodeSelector); const mutationListener = (mutationList, observer) => { const list = mutationList.filter(item => item.target.id === targetRootId); if (list.length > 0) { runWaiter(); } }; const observer = new MutationObserver(mutationListener); observer.observe(itemsContainer, { childList: true, subtree: true }); await checkWatched(); })();