Youtube compact sidebar+

Add more buttons in compact sidebar/mini guide

目前為 2024-06-11 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Youtube compact sidebar+
  3. // @name:pt-BR Youtube barra lateral+
  4. // @namespace https://gf.qytechs.cn/users/821661
  5. // @match https://www.youtube.com/*
  6. // @grant GM_xmlhttpRequest
  7. // @grant GM_registerMenuCommand
  8. // @grant GM_setValue
  9. // @grant GM_getValue
  10. // @grant GM_addStyle
  11. // @grant GM_addStyle
  12. // @version 0.3
  13. // @author hdyzen
  14. // @description Add more buttons in compact sidebar/mini guide
  15. // @description:pt-BR Adiciona mais botões a barra lateral
  16. // @license GPL-3.0
  17. // ==/UserScript==
  18. 'use strict';
  19.  
  20. let channelId; // Channel ID
  21. const defaultLang = 'en'; // If the lang is not in the labels use this language
  22. const lang = document.documentElement.lang; // Lang of HTML
  23.  
  24. // Labels
  25. const labels = {
  26. 'pt-BR': {
  27. yourChannel: 'Canal',
  28. history: 'Histórico',
  29. download: 'Download',
  30. playlists: 'Playlists',
  31. yourVideos: 'Videos',
  32. watchLater: 'Depois',
  33. liked: 'Gostei',
  34. },
  35. 'en': {
  36. yourChannel: 'Channel',
  37. history: 'History',
  38. playlists: 'Playlists',
  39. download: 'Download',
  40. yourVideos: 'Videos',
  41. watchLater: 'Later',
  42. liked: 'Liked',
  43. },
  44. };
  45.  
  46. // Get channel id
  47. async function getChannelId() {
  48. const urlGet = 'https://studio.youtube.com/';
  49.  
  50. GM_xmlhttpRequest({
  51. url: urlGet,
  52. onload: e => {
  53. if (e.finalUrl !== urlGet) {
  54. console.log('NOT FOUND ID:', e);
  55. }
  56.  
  57. const id = e.responseText.match(/"CHANNEL_ID":"([a-zA-Z0-9]+)"/)?.[1];
  58.  
  59. if (!id) {
  60. console.log('NOT FOUND ID:', e);
  61. }
  62.  
  63. channelId = id;
  64. },
  65. });
  66. }
  67. getChannelId();
  68.  
  69. // Mini guide item template
  70. function getTemplate(path, label, href, id) {
  71. return `
  72. <ytd-mini-guide-entry-renderer id="${id}" class="style-scope ytd-mini-guide-renderer" system-icons="" role="tab" tabindex="0" aria-selected="false" aria-label="${label}">
  73. <a id="endpoint" tabindex="-1" class="yt-simple-endpoint style-scope ytd-mini-guide-entry-renderer" title="Você" href="${href}">
  74. <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" focusable="false" style="color: inherit; fill: currentColor; pointer-events: none; display: inline-flexbox; display: -moz-inline-box; display: inline-flex; -moz-box-align: center; align-items: center; -moz-box-pack: center; justify-content: center; position: relative; vertical-align: middle; fill: var(--iron-icon-fill-color,currentcolor); stroke: var(--iron-icon-stroke-color,none); width: var(--iron-icon-width,24px); height: var(--iron-icon-height,24px); animation: var(--iron-icon-animation); margin-top: var(--iron-icon-margin-top); margin-right: var(--iron-icon-margin-right); margin-bottom: var(--iron-icon-margin-bottom); margin-left: var(--iron-icon-margin-left); padding: var(--iron-icon-padding);">${path}</svg>
  75. <span class="title style-scope ytd-mini-guide-entry-renderer">${label}</span>
  76. </a>
  77. </ytd-mini-guide-entry-renderer>
  78. `;
  79. }
  80.  
  81. // Render item in mini guide
  82. function renderItem(element) {
  83. const items = {
  84. yourChannel: { href: `/channel/${channelId}`, path: '<path d="M4 20h14v1H3V6h1v14zM6 3v15h15V3H6zm2.02 14c.36-2.13 1.93-4.1 5.48-4.1s5.12 1.97 5.48 4.1H8.02zM11 8.5a2.5 2.5 0 015 0 2.5 2.5 0 01-5 0zm3.21 3.43A3.507 3.507 0 0017 8.5C17 6.57 15.43 5 13.5 5S10 6.57 10 8.5c0 1.69 1.2 3.1 2.79 3.43-3.48.26-5.4 2.42-5.78 5.07H7V4h13v13h-.01c-.38-2.65-2.31-4.81-5.78-5.07z"></path>' },
  85. history: { href: '/feed/history', path: '<g><path d="M14.97 16.95 10 13.87V7h2v5.76l4.03 2.49-1.06 1.7zM22 12c0 5.51-4.49 10-10 10S2 17.51 2 12h1c0 4.96 4.04 9 9 9s9-4.04 9-9-4.04-9-9-9C8.81 3 5.92 4.64 4.28 7.38c-.11.18-.22.37-.31.56L3.94 8H8v1H1.96V3h1v4.74c.04-.09.07-.17.11-.25.11-.22.23-.42.35-.63C5.22 3.86 8.51 2 12 2c5.51 0 10 4.49 10 10z"></path></g>' },
  86. playlists: { href: '/feed/playlists', path: '<path d="M22 7H2v1h20V7zm-9 5H2v-1h11v1zm0 4H2v-1h11v1zm2 3v-8l7 4-7 4z"></path>' },
  87. yourVideos: { href: `https://studio.youtube.com/channel/${channelId}/videos`, path: '<path d="m10 8 6 4-6 4V8zm11-5v18H3V3h18zm-1 1H4v16h16V4z"></path>' },
  88. watchLater: { href: '/playlist?list=WL', path: '<path d="M14.97 16.95 10 13.87V7h2v5.76l4.03 2.49-1.06 1.7zM12 3c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9m0-1c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2z"></path>' },
  89. liked: { href: '/playlist?list=LL', path: '<path d="M18.77,11h-4.23l1.52-4.94C16.38,5.03,15.54,4,14.38,4c-0.58,0-1.14,0.24-1.52,0.65L7,11H3v10h4h1h9.43 c1.06,0,1.98-0.67,2.19-1.61l1.34-6C21.23,12.15,20.18,11,18.77,11z M7,20H4v-8h3V20z M19.98,13.17l-1.34,6 C18.54,19.65,18.03,20,17.43,20H8v-8.61l5.6-6.06C13.79,5.12,14.08,5,14.38,5c0.26,0,0.5,0.11,0.63,0.3 c0.07,0.1,0.15,0.26,0.09,0.47l-1.52,4.94L13.18,12h1.35h4.23c0.41,0,0.8,0.17,1.03,0.46C19.92,12.61,20.05,12.86,19.98,13.17z"></path>' },
  90. download: { href: '/feed/downloads', path: '<path d="M17 18v1H6v-1h11zm-.5-6.6-.7-.7-3.8 3.7V4h-1v10.4l-3.8-3.8-.7.7 5 5 5-4.9z"></path>' },
  91. };
  92.  
  93. Object.entries(items).forEach(([key, value]) => {
  94. const template = getTemplate(value.path, labels[lang] ? labels[lang][key] : labels[defaultLang][key], value.href, key);
  95. element[element.length - 1].insertAdjacentHTML('afterend', template);
  96. displayItems(key, getStates(key));
  97. });
  98. }
  99.  
  100. function getStates(key) {
  101. const states = GM_getValue('states');
  102. const statesDefault = {
  103. download: true,
  104. liked: true,
  105. watchLater: true,
  106. yourVideos: true,
  107. playlists: true,
  108. history: true,
  109. yourChannel: true,
  110. };
  111.  
  112. if (key) return states?.[key] !== undefined ? states[key] : statesDefault[key];
  113. else return statesDefault;
  114. }
  115.  
  116. // Render commands menu
  117. function renderMenuCommands() {
  118. const states = GM_getValue('states');
  119.  
  120. Object.entries(labels[lang] ? labels[lang] : labels[defaultLang]).forEach(([key, label]) => {
  121. const state = getStates(key);
  122.  
  123. GM_registerMenuCommand(`${label}: ${state ? '✅' : '❌'}`, e => stateToggle(key, state), { id: key, autoClose: false });
  124. });
  125. }
  126. renderMenuCommands();
  127.  
  128. // States toggle
  129. function stateToggle(key, state) {
  130. const statesNew = getStates();
  131. statesNew[key] = !state;
  132. GM_setValue('states', statesNew);
  133. renderMenuCommands();
  134. displayItems(key, statesNew[key]);
  135. }
  136.  
  137. // Display items
  138. function displayItems(id, state) {
  139. const item = document.getElementById(id);
  140.  
  141. if (state) {
  142. item.style.display = 'unset';
  143. } else {
  144. item.style.display = 'none';
  145. }
  146. }
  147.  
  148. // Wait mini guide childs
  149. const observer = new MutationObserver(mutations => {
  150. const items = document.getElementsByTagName('ytd-mini-guide-renderer')?.[0]?.children?.[0].children;
  151.  
  152. if (items.length) {
  153. renderItem(items);
  154. observer.disconnect();
  155. }
  156. });
  157.  
  158. observer.observe(document.body, { childList: true, subtree: true });
  159.  
  160. // Scroll in mini guide
  161. GM_addStyle(`
  162. ytd-mini-guide-renderer.ytd-app {
  163. overflow: auto;
  164. }
  165. `);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址