Skip to content

Instantly share code, notes, and snippets.

@miyagawa
Created May 31, 2025 08:01
Show Gist options
  • Select an option

  • Save miyagawa/bbf0e1be3cc0dca138aae320d38949c3 to your computer and use it in GitHub Desktop.

Select an option

Save miyagawa/bbf0e1be3cc0dca138aae320d38949c3 to your computer and use it in GitHub Desktop.
automatically remove YT video from Watch Later
let videoMonitor = null;
let currentVideoId = null;
let autoRemovalTriggered = false;
function getCurrentVideoId() {
const location = new URL(window.location.href);
let videoId = location.searchParams.get("v");
if (location.pathname.startsWith("/shorts/")) {
videoId = location.pathname.split("/")[2];
}
return videoId;
}
function removeFromWatchLater(videoId) {
const getRemoveVideoParams = (videoId) => ({
clickTrackingParams: "",
commandMetadata: { webCommandMetadata: { sendPost: true, apiUrl: "/youtubei/v1/browse/edit_playlist" } },
playlistEditEndpoint: { playlistId: "WL", actions: [{ action: "ACTION_REMOVE_VIDEO_BY_VIDEO_ID", removedVideoId: videoId }] }
});
const appElement = document.querySelector("ytd-app");
if (!appElement) return;
const event = new window.CustomEvent('yt-action', {
detail: {
actionName: 'yt-service-request',
returnValue: [],
args: [{ data: {} }, getRemoveVideoParams(videoId)],
optionalAction: false,
}
});
appElement.dispatchEvent(event);
console.log('Auto-removed video from Watch Later:', videoId);
}
function monitorVideoProgress() {
const video = document.querySelector('video');
if (!video) return;
const checkProgress = () => {
if (video.duration && video.currentTime) {
const remainingTime = video.duration - video.currentTime;
// 95% reached, or remaining 60s
const triggerThreshold = Math.max(video.duration * 0.05, 60);
if (remainingTime <= triggerThreshold && remainingTime > 0 && !autoRemovalTriggered) {
const videoId = getCurrentVideoId();
if (videoId) {
autoRemovalTriggered = true;
removeFromWatchLater(videoId);
}
}
}
};
video.addEventListener('timeupdate', checkProgress);
return () => {
video.removeEventListener('timeupdate', checkProgress);
};
}
function startMonitoring() {
const videoId = getCurrentVideoId();
if (videoId !== currentVideoId) {
currentVideoId = videoId;
autoRemovalTriggered = false;
if (videoMonitor) {
videoMonitor();
}
const waitForVideo = () => {
const video = document.querySelector('video');
if (video) {
videoMonitor = monitorVideoProgress();
} else {
setTimeout(waitForVideo, 1000);
}
};
waitForVideo();
}
}
function handleNavigation() {
startMonitoring();
}
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
const addedNodes = Array.from(mutation.addedNodes);
if (addedNodes.some(node =>
node.nodeType === Node.ELEMENT_NODE &&
(node.matches('ytd-watch-flexy') || node.querySelector('ytd-watch-flexy'))
)) {
setTimeout(handleNavigation, 1000);
}
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
window.addEventListener('yt-navigate-finish', handleNavigation);
window.addEventListener('popstate', handleNavigation);
setTimeout(startMonitoring, 2000);
{
"manifest_version": 3,
"name": "YouTube Auto Watch Later",
"version": "1.0",
"description": "Automatically manage YouTube Watch Later playlist",
"permissions": [
"scripting",
"tabs",
"activeTab"
],
"host_permissions": [
"https://www.youtube.com/*"
],
"content_scripts": [
{
"matches": ["https://www.youtube.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment