返回首頁 

Greasy Fork is available in English.

YouTube: Subscriptions Watch Later

Adds unwatched videos from your YouTube subscriptions page to the "Watch later" playlist

  1. // ==UserScript==// @name YouTube: Subscriptions Watch Later// @namespace https://github.com/ackoujens// @version 1.2// @description Adds unwatched videos from your YouTube subscriptions page to the "Watch later" playlist// @author Jens Ackou// @grant GM_addStyle// @include http://*.youtube.com/*// @include http://youtube.com/*// @include https://*.youtube.com/*// @include https://youtube.com/*// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js// ==/UserScript==// To submit bugs or submit revisions please see visit the repository at:// https://github.com/ackoujens/youtube-subcriptions-watch-later// You can open new issues at:// https://github.com/ackoujens/youtube-subcriptions-watch-later/issues// I highly recommend using the "YouTube: Hide Watched Videos" script// created by Evguani Naverniouk. This is a flow that I use and find very useful.// This script also helped me getting an idea of how to add a button to the YouTube AI.// Probably the reason why I chose to make it look like the icons are part of eachother.(function(undefined) {// Enable for debuggingvar __DEV__ = true;// Set defaultslocalStorage.YTSP = localStorage.YTSP || 'false';GM_addStyle(`.YT-SP-BUTTON {background: transparent;border: 0;color: #888888;cursor: pointer;height: 40px;outline: 0;margin-right: 8px;padding: 0 8px;width: 40px;}.YT-SP-BUTTON svg {margin-top: 8px;height: 34px;width: 34px;}.YT-SP-BUTTON:focus,.YT-SP-BUTTON:hover {color: #FFF;}.YT-SP-MENU {background: #F8F8F8;border: 1px solid #D3D3D3;box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05);display: none;font-size: 12px;margin-top: -1px;padding: 10px;position: absolute;right: 0;text-align: center;top: 100%;white-space: normal;z-index: 9999;}.YT-SP-MENU-ON { display: block; }.YT-SP-MENUBUTTON-ON span { transform: rotate(180deg) }`);var counter = 0;var watched = 0;var unwatched = 0;var watchlaterIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><g fill="currentColor"><path d="M32 4h-4v-4h-4v4h-4v4h4v4h4v-4h4z"></path><path d="M26.996 13.938c0.576 0.64 1.1 1.329 1.563 2.062-1.197 1.891-2.79 3.498-4.67 4.697-2.362 1.507-5.090 2.303-7.889 2.303s-5.527-0.796-7.889-2.303c-1.88-1.199-3.473-2.805-4.67-4.697 1.197-1.891 2.79-3.498 4.67-4.697 0.122-0.078 0.246-0.154 0.371-0.228-0.311 0.854-0.482 1.776-0.482 2.737 0 4.418 3.582 8 8 8s8-3.582 8-8c0-0.022-0.001-0.043-0.001-0.065-3.415-0.879-5.947-3.957-5.998-7.635-0.657-0.074-1.325-0.113-2.001-0.113-6.979 0-13.028 4.064-16 10 2.972 5.936 9.021 10 16 10s13.027-4.064 16-10c-0.551-1.101-1.209-2.137-1.958-3.095-0.915 0.537-1.946 0.897-3.046 1.034zM13 10c1.657 0 3 1.343 3 3s-1.343 3-3 3-3-1.343-3-3 1.343-3 3-3z"></path></g></svg>';// ===========================================================// Returns a function, that, as long as it continues to be invoked, will not// be triggered. The function will be called after it stops being called for// N milliseconds. If `immediate` is passed, trigger the function on the// leading edge, instead of the trailing.var debounce = function(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);};};// ===========================================================// Button will be injected into the main header menuvar findButtonTarget = function() {return $('#container #end #buttons');};// ===========================================================// Check if button is already presentvar isButtonAlreadyThere = function() {return $('.YT-SP-BUTTON').length > 0;};// ===========================================================// Pausing the script in millisecondsfunction sleep(milliseconds) {var start = new Date().getTime();for (var i = 0; i < 1e7; i++) {if ((new Date().getTime() - start) > milliseconds) {break;}}}// ===========================================================// Add button to pagevar addButton = function() {if (isButtonAlreadyThere()) return;// Find button targetvar target = findButtonTarget();if (!target) return;// Generate button DOM//var icon = localStorage.SP === 'true' ? visibilityIcon : visibilityOffIcon;var icon = localStorage.YTSP = watchlaterIcon;var button = $('<button class="YT-SP-BUTTON" title="Add Videos to Watch Later">' + icon + '</button>');// Attach button eventbutton.on("click", function() {var allItems = $('ytd-thumbnail-overlay-toggle-button-renderer').not('[aria-label="Added"]');for (i = 0; i < allItems.length; i++) {if (!$(allItems[i]).parent().find('ytd-thumbnail-overlay-r###me-playback-renderer').length) {$(allItems[i]).click();// Executing this too fast seems to drop videos being addedsleep(100);}}});// Insert button into DOMtarget.prepend(button);};var run = debounce(function() {if (__DEV__) console.log('[YT-SP] Running check for subcription videos');addButton();}, 500);// ===========================================================// Hijack all XHR callsvar send = XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send = function(data) {this.addEventListener("readystatechange", function() {if (// Anytime more videos are fetched -- re-run scriptthis.responseURL.indexOf('browse_ajax?action_continuation') > 0) {setTimeout(function() {run();}, 0);}}, false);send.call(this, data);};// ===========================================================var observeDOM = (function() {var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;var eventListenerSupported = window.addEventListener;return function(obj, callback) {if (__DEV__) console.log('[YT-SP] Attaching DOM listener');// Invalid `obj` givenif (!obj) return;if (MutationObserver) {var obs = new MutationObserver(function(mutations, observer) {if (mutations[0].addedNodes.length || mutations[0].removedNodes.length) {callback(mutations);}});obs.observe(obj, {childList: true,subtree: true});} else if (eventListenerSupported) {obj.addEventListener('DOMNodeInserted', callback, false);obj.addEventListener('DOMNodeRemoved', callback, false);}};})();// ===========================================================if (__DEV__) console.log('[YT-SP] Starting Script');// YouTube does navigation via history and also does a bunch// of AJAX video loading. In order to ensure we're always up// to date, we have to listen for ANY DOM change event, and// re-run our script.observeDOM(document.body, run);run();})();