Youtube Subscription List Quick Playlist

Quickly create playlists from your Youtube subscription feed

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name        Youtube Subscription List Quick Playlist
// @namespace   https://greasyfork.org/en/users/13981-chk1
// @description Quickly create playlists from your Youtube subscription feed
// @include     https://www.youtube.com/feed/subscriptions*
// @version     1.0.3
// @grant       GM_addStyle
// @run-at      document-end
// ==/UserScript==

GM_addStyle("#subs2playlistContainer { position:fixed; bottom: 10px; right: 10px; width: 180px; } ");
GM_addStyle("#subs2playlistLink { display: block; clear: both; } ");
GM_addStyle("#subs2playlistClear { float: right; font-size: 12px; padding-top: 5px; padding-bottom: 5px; display: block; color: #bb0000; }");
GM_addStyle("#subs2playlistCopy { float: left; font-size: 12px; padding-top: 5px; padding-bottom: 5px; display: block; color: #000; }");
GM_addStyle("#subs2playlistCopyTemp { width:1px; height:1px; position:fixed; }");
GM_addStyle("#subs2playlistContainer input { font-size: 12px; display:block; color: #767676; clear:both; width: 100%; border: 1px solid #d5d5d5; border-radius: 2px; font-family: monospace }");
GM_addStyle("#subs2playlistContainer span { font-size: 12px; display:block; clear:both; transition: color 0.2s; color: #767676; }");
GM_addStyle("#subs2playlistContainer span.warning { color: #bb0000; }");
GM_addStyle(".subs2playlist.subs2playlist-add span::after { content: 'Add to playlist' }");
GM_addStyle(".subs2playlist.subs2playlist-remove span::after { content: 'Remove from playlist' }");

var videoIds = [];
var playlistElements;

// create DOM nodes
function createPlaylistContainer() {
	var playlistLinkContainer       = document.createElement('div'); 
	playlistLinkContainer.className = "yt-card yt-card-has-padding";
	playlistLinkContainer.id        = "subs2playlistContainer";

	var playlistLinkH3              = document.createElement('h3'); 
	playlistLinkH3.className        = "yt-lockup-title";

	var playlistLink                = document.createElement('a'); 
	playlistLink.href               = "https://www.youtube.com/watch_videos?video_ids=";
	playlistLink.id                 = "subs2playlistLink";

	var playlistLinkText            = document.createTextNode("Your playlist link"); 
	var playlistLinkCount           = document.createElement("span"); 
	playlistLinkCount.innerHTML     = "0 videos"; 
	
	var clearLink                   = document.createElement("a"); 
	clearLink.id                    = "subs2playlistClear";
	clearLink.onclick               = function() { clearPlaylist() };

	var clearText                   = document.createTextNode("Clear"); 
	var copyLink                    = document.createElement("a"); 
	copyLink.id                     = "subs2playlistCopy";

	var copyText = document.createTextNode('Copy IDs'); 
	var copyLinkInput = document.createElement('input'); 
	copyLinkInput.type = "text"; 
	copyLinkInput.placeholder = "video IDs..."; 
	copyLinkInput.className = "yt-uix-form-input-bidi"; 
	copyLink.onclick = function() { 
		html5Copy(copyLinkInput);
	};

	var playlistLinkFunctionContainer = document.createElement('div'); 
	var introText                   = document.createElement("span"); 
	introText.innerHTML             = "Add videos from your subscription box to create a quick playlist."; 

	copyLink.appendChild(copyText);
	clearLink.appendChild(clearText);
	playlistLink.appendChild(playlistLinkText);
	playlistLinkH3.appendChild(playlistLink);
	playlistLinkContainer.appendChild(playlistLinkH3);

	playlistLinkFunctionContainer.appendChild(playlistLinkCount);
	playlistLinkFunctionContainer.appendChild(clearLink);
	playlistLinkFunctionContainer.appendChild(copyLink);
	playlistLinkFunctionContainer.appendChild(copyLinkInput);
	playlistLinkFunctionContainer.style.display = "none";
	
	playlistLinkContainer.appendChild(playlistLinkH3);
	playlistLinkContainer.appendChild(introText);
	playlistLinkContainer.appendChild(playlistLinkFunctionContainer);

	return {
		'container': playlistLinkContainer, 
		'link': playlistLink, 
		'text': playlistLinkText,
		'plain': copyLinkInput,
		'count': playlistLinkCount,
		'intro': introText,
		'functioncontainer': playlistLinkFunctionContainer
	};
}

// copy video IDs to clipboard
function html5Copy(inputnode){
	var node = document.createElement('pre');
	node.className = 'subs2playlistCopyTemp';
    node.textContent = inputnode.value;
	document.body.appendChild(node);

	var selection = getSelection();
	selection.removeAllRanges()
	var range = document.createRange();
	range.selectNodeContents(node);
	console.log(range);
	selection.addRange(range);
	document.execCommand('copy');

	document.body.removeChild(node);
}

// update playlist link, run after adding/removing videos
function updatePlaylistLink() {
	// toggle intro text visibility off
	playlistElements.functioncontainer.style.display = "block";
	playlistElements.intro.style.display = "none";

	// check playlist length for known limits
	if(videoIds.length > 20) {
		playlistElements.count.classList.add("warning");
		playlistElements.count.textContent = ""+videoIds.length+" videos - The playlist link will only play the first 20 videos.";
	} else if(playlistElements.text.textContent.length > 2000){
		playlistElements.count.classList.add("warning");
		playlistElements.count.textContent = ""+videoIds.length+" videos - Too many videos, URL is too long. Some videos in the playlist link may not work.";
	} else {
		playlistElements.count.classList.remove("warning");
		playlistElements.link.href="https://www.youtube.com/watch_videos?video_ids="+videoIds.join(',');
		playlistElements.plain.value=videoIds.join(',');
		playlistElements.count.textContent = ""+videoIds.length+" videos";
	}
}

// clear all video IDs, reset buttons
function clearPlaylist() {
	videoIds = [];
	var buttons = document.querySelectorAll('#browse-items-primary > .section-list .subs2playlist');
	for (var i = 0; i < buttons.length; ++i) {
		buttons[i].classList.remove("subs2playlist-remove");
		//buttons[i].classList.remove("yt-uix-button-subscribed-branded");
		buttons[i].classList.remove("c4-module-editor-delete");
		buttons[i].classList.add("subs2playlist-add");
		buttons[i].classList.add("c4-editor-plus");
	}
	updatePlaylistLink();
}

// only add videos that aren't duplicates
function toggleVideoId(videoId){
	var alreadyIn = videoIds.indexOf(videoId);
	if(alreadyIn === -1){
		videoIds.push(videoId);
		return true;
	} else {
		videoIds.splice(alreadyIn, 1);
		return false;
	}
}

// add a button to a subscription list item
function createPlusButton(videoId){
	var container = document.createElement('button'); 
	container.setAttribute('onclick', 'toggleVideoId(\''+videoId+'\');');
	container.className = "subs2playlist subs2playlist-add yt-uix-button yt-uix-button-size-default yt-uix-button-default yt-uix-button-has-icon no-icon-markup yt-uix-inlineedit-edit c4-editor-plus";
	container.onclick = function(){ 
		toggleVideoId(videoId);
		container.classList.toggle("c4-module-editor-delete");
		container.classList.toggle("c4-editor-plus");
		container.classList.toggle("subs2playlist-remove");
		container.classList.toggle("subs2playlist-add");
		updatePlaylistLink();
	};
	container.innerHTML = "<span class=\"yt-uix-button-content\"></span>";
	return container;
}

// iterate subscription list items, then add buttons
function appendAllTheThings(node) {
	var videoId = node.getAttribute("data-context-item-id");
	var plusButtonNode = createPlusButton(videoId);
	node.appendChild(plusButtonNode);
}

var observerConfig = { 
  childList: true,
  attributes: true, 
  subtree: false,
  attributeOldValue: false
};

// add buttons to items loaded after clicking "load more" or endless scroll, which are dynamically added 
var listObserver = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    if(mutation.type == "childList" && mutation.addedNodes.length >= 1) {
      console.log(mutation);
      for (var i = 0; i < mutation.addedNodes.length; ++i) {
      	var node = mutation.addedNodes[i];
      	if(node.nodeType === 1){
      		var videoLinkContainer = node.querySelector('.yt-lockup');
      		appendAllTheThings(videoLinkContainer);
      	}
      }
    }
  });    
});
var subListContainer = document.querySelector('#browse-items-primary > .section-list');
listObserver.observe(subListContainer, observerConfig);

// first run: create our container, add buttons to playlist items
function firstRun(){
	var videoLinkNodes = document.querySelectorAll('div.yt-lockup');
	for (var i = 0; i < videoLinkNodes.length; ++i) {
		var node = videoLinkNodes[i];
		appendAllTheThings(node);
	}
	playlistElements = createPlaylistContainer();
	document.body.appendChild(playlistElements.container);
}

firstRun();