Skip to content

Instantly share code, notes, and snippets.

@derekwheee
Last active October 24, 2021 02:45
Show Gist options
  • Save derekwheee/e043c8d7686df7e30862c5a56a8ba6b0 to your computer and use it in GitHub Desktop.
Save derekwheee/e043c8d7686df7e30862c5a56a8ba6b0 to your computer and use it in GitHub Desktop.
Hide watched videos in your YouTube Subscriptions feed
javascript:void%20function(){(function(){function%20e(){var%20t=document.querySelectorAll(%22.ytd-thumbnail-overlay-resume-playback-renderer:not([data-hidden])%22);t.length%26%26(t.forEach(function(e){e.closest(%22td%22),e.closest(%22.ytd-grid-renderer%22).style.display=%22none%22,e.setAttribute(%22data-hidden%22,!0)}),document.querySelectorAll(%22.ytd-section-list-renderer%22).forEach(function(e){e.style.display=%22inline-block%22}),localStorage.UNWATCHEDwatched%26%26JSON.parse(localStorage.UNWATCHEDwatched).forEach(function(e){try{document.querySelector('.ytd-thumbnail[href=%22'+e+'%22]').parentNode.parentNode.parentNode.style.display=%22none%22}catch(t){}}),n(),setTimeout(function(){var%20t=document.getElementById(%22guide-inner-content%22).scrollTop;document.getElementById(%22guide-inner-content%22).scrollTop=t+1,document.getElementById(%22guide-inner-content%22).scrollTop=t,e()},1e3))}function%20t(e,t,n){var%20o;return%20function(){var%20r=this,a=arguments,c=function(){o=null,n||e.apply(r,a)},l=n%26%26!o;clearTimeout(o),o=setTimeout(c,t),l%26%26e.apply(r,a)}}function%20n(){var%20e=document.querySelectorAll(%22ytd-grid-video-renderer:not([data-unwatch])%22),t='%3Cdiv\n%20%20%20%20%20%20%20%20%20%20%20%20class=%22UNWATCH-controls_markWatched%22\n%20%20%20%20%20%20%20%20%20%20%20%20style=%22\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20position:%20absolute;\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20top:%200;\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20left:%200;\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20padding:%200.5em;\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20background:%20rgba(255,%20255,%20255,%200.5);\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20font-size:%201.5em;\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cursor:%20pointer;\n%20%20%20%20%20%20%20%20%20%20%20%20%22%3E⌚%3C/div%3E';e.forEach(function(e){var%20n=document.createElement(%22div%22);n.className=%22UNWATCH-controls%22,n.innerHTML=t,e.style.position=%22relative%22,e.appendChild(n),e.setAttribute(%22data-UNWATCH%22,!0),e.querySelector(%22.UNWATCH-controls_markWatched%22).addEventListener(%22click%22,o,!1)})}function%20o(e){var%20t=e.target,n=t.parentNode.parentNode,o=n.querySelector(%22.ytd-thumbnail%22).getAttribute(%22href%22),r=localStorage.UNWATCHEDwatched%3FJSON.parse(localStorage.UNWATCHEDwatched):[];r.push(o),localStorage.setItem(%22UNWATCHEDwatched%22,JSON.stringify(r)),t.closest(%22.ytd-grid-renderer%22).style.display=%22none%22}var%20r=document.getElementById(%22contents%22),a={childList:!0},c=new%20MutationObserver(function(){setTimeout(e,1e3)});c.observe(r,a),e(),window.onscroll=t(e,250)})()}();
(function() {
const contents = document.getElementById('contents');
const config = { childList: true };
// YouTube will load more videos if the viewport isn't full
// We want to hide new watched videos when that happens
const observer = new MutationObserver(function() {
setTimeout(hideWatchedVideos, 1000);
});
observer.observe(contents, config);
hideWatchedVideos();
window.onscroll = debounce(hideWatchedVideos, 250);
function hideWatchedVideos() {
const resumes = document.querySelectorAll('.ytd-thumbnail-overlay-resume-playback-renderer:not([data-hidden])');
if (!resumes.length) return;
resumes.forEach(function(el) {
el.closest('td')
el.closest('.ytd-grid-renderer').style.display = 'none';
el.setAttribute('data-hidden', true);
});
document.querySelectorAll('.ytd-section-list-renderer').forEach(function(el) {
el.style.display = 'inline-block';
});
if (localStorage.UNWATCHEDwatched) {
JSON.parse(localStorage.UNWATCHEDwatched).forEach((watch) => {
// This just means the thumbnail for the video is on the screen
try {
document.querySelector(`.ytd-thumbnail[href="${watch}"]`).parentNode.parentNode.parentNode.style.display = 'none';
} catch (err) { /* noop */ }
});
}
applyControls();
// YouTube's rendering engine is mad slow
setTimeout(() => {
// YouTube will only load more videos if there's a user interaction
// Scrolling the sidebar kicks off that load event
const sidebarScroll = document.getElementById('guide-inner-content').scrollTop;
document.getElementById('guide-inner-content').scrollTop = sidebarScroll + 1;
document.getElementById('guide-inner-content').scrollTop = sidebarScroll;
// This ensures that all of the watched videos get hidden
hideWatchedVideos();
}, 1000);
}
/* https://davidwalsh.name/javascript-debounce-function */
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
function applyControls() {
const renderers = document.querySelectorAll('ytd-grid-video-renderer:not([data-unwatch])');
const controlsHtml = `<div
class="UNWATCH-controls_markWatched"
style="
position: absolute;
top: 0;
left: 0;
padding: 0.5em;
background: rgba(255, 255, 255, 0.5);
font-size: 1.5em;
cursor: pointer;
">⌚</div>`;
renderers.forEach((renderer) => {
const control = document.createElement('div');
control.className = 'UNWATCH-controls';
control.innerHTML = controlsHtml;
renderer.style.position = 'relative';
renderer.appendChild(control);
renderer.setAttribute('data-UNWATCH', true);
renderer.querySelector('.UNWATCH-controls_markWatched').addEventListener('click', markAsRead, false);
});
}
function markAsRead(e) {
const el = e.target;
const parent = el.parentNode.parentNode;
const href = parent.querySelector('.ytd-thumbnail').getAttribute('href');
const watch = localStorage.UNWATCHEDwatched ? JSON.parse(localStorage.UNWATCHEDwatched) : [];
watch.push(href);
localStorage.setItem('UNWATCHEDwatched', JSON.stringify(watch));
el.closest('.ytd-grid-renderer').style.display = 'none';
}
}());
@highroller
Copy link

Great work. Your bookmarklet is better than the other bookmarklet because it continues to remove watched videos when scrolling. But yours doesn't work on m.youtube.com mobile site. The main reason I use bookmarklet is that chrome android doesn't support extension. Please fix it. Thanks.

@highroller
Copy link

Is it possible to make this bookmarklet to work on the youtube homepage?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment