YouTube Channel Playlist Button

Adds a button linking to the channel's playlist on YouTube channel pages

  1. // ==UserScript==
  2. // @name YouTube Channel Playlist Button
  3. // @namespace https://greasyfork.org/en/users/703184-floriegl
  4. // @version 1.2
  5. // @description Adds a button linking to the channel's playlist on YouTube channel pages
  6. // @author floriegl
  7. // @match https://www.youtube.com/*
  8. // @license MIT
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14.  
  15. let actionModel = undefined;
  16. let channelId = undefined;
  17.  
  18. const readdObserver = new MutationObserver(() => {
  19. if (!document.getElementById('channel-playlist-link')) {
  20. addPlaylistButton();
  21. }
  22. });
  23.  
  24. function addPlaylistButton() {
  25. readdObserver.disconnect();
  26. readdObserver.observe(actionModel, { childList: true, subtree: false });
  27.  
  28. if (!document.getElementById('channel-playlist-link')) {
  29. const link = document.createElement("a");
  30. link.className = "yt-flexible-actions-view-model-wiz__action";
  31. link.id = "channel-playlist-link";
  32. link.style.textDecoration = "none";
  33.  
  34. const buttonView = document.createElement("button-view-model");
  35. buttonView.className = "yt-spec-button-view-model";
  36. link.appendChild(buttonView);
  37.  
  38. const button = document.createElement("button");
  39. button.className = "yt-spec-button-shape-next yt-spec-button-shape-next--outline yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m yt-spec-button-shape-next--enable-backdrop-filter-experiment";
  40. button.setAttribute("aria-label", "Go to Channel Playlist");
  41. buttonView.appendChild(button);
  42.  
  43. const buttonText = document.createElement("div");
  44. buttonText.className = "yt-spec-button-shape-next__button-text-content";
  45. buttonText.textContent = "Channel Playlist";
  46. button.appendChild(buttonText);
  47.  
  48. actionModel.appendChild(link);
  49. }
  50.  
  51. document.getElementById('channel-playlist-link').href = `/playlist?list=UU${channelId.substring(2)}`;
  52. }
  53.  
  54. window.addEventListener('yt-navigate-finish', async (e) => {
  55. if (e.detail && e.detail.pageType === 'channel' && e.detail.endpoint && e.detail.endpoint.browseEndpoint && typeof e.detail.endpoint.browseEndpoint.browseId === 'string' && e.detail.endpoint.browseEndpoint.browseId.startsWith('UC')) {
  56. channelId = e.detail.endpoint.browseEndpoint.browseId;
  57. actionModel = document.querySelector('.yt-flexible-actions-view-model-wiz');
  58. if (!actionModel) {
  59. actionModel = await new Promise((resolve) => {
  60. new MutationObserver((mutations, obs) => {
  61. const item = document.querySelector('.yt-flexible-actions-view-model-wiz');
  62. if (item) {
  63. resolve(item);
  64. obs.disconnect();
  65. }
  66. }).observe(document, { childList: true, subtree: true });
  67. });
  68. }
  69. addPlaylistButton();
  70. } else {
  71. channelId = null;
  72. readdObserver.disconnect();
  73. }
  74. });
  75. window.addEventListener("resize", () => {
  76. if (actionModel !== document.querySelector('.yt-flexible-actions-view-model-wiz')) {
  77. actionModel = document.querySelector('.yt-flexible-actions-view-model-wiz');
  78. if (channelId && actionModel) {
  79. addPlaylistButton();
  80. }
  81. }
  82. });
  83. })();