YouTube: Search results in Grid view

See more results in one go, without irrelevant results, such as 'People also watched' and 'For you', in the way.

目前为 2023-12-05 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube: Search results in Grid view
  3. // @namespace https://greasyfork.org/users/1166888-pedro
  4. // @match https://www.youtube.com/*
  5. // @version 2023.12.5
  6. // @author Pedro
  7. // @description See more results in one go, without irrelevant results, such as 'People also watched' and 'For you', in the way.
  8. // @grant GM_registerMenuCommand
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @grant GM_listValues
  12. // @compatible Chrome
  13. // @compatible Safari
  14. // @compatible Firefox
  15. // @license MIT
  16. // ==/UserScript==
  17. GM_registerMenuCommand('Settings', settingsMenu)
  18.  
  19. function settingsMenu() {
  20. const menu = document.createElement('settings-menu');
  21. document.documentElement.appendChild(menu);
  22. menu.insertAdjacentHTML('afterbegin', `
  23. <div>
  24. <header>
  25. <span>Settings</span>
  26. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
  27. <path d="M0 0h24v24H0V0z" fill="none" opacity=".87"></path>
  28. <path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm4.3 14.3c-.39.39-1.02.39-1.41 0L12 13.41 9.11 16.3c-.39.39-1.02.39-1.41 0-.39-.39-.39-1.02 0-1.41L10.59 12 7.7 9.11c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0L12 10.59l2.89-2.89c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41L13.41 12l2.89 2.89c.38.38.38 1.02 0 1.41z"></path>
  29. </svg>
  30. </header>
  31. <label>
  32. <span>Videos per row</span>
  33. <input type="number" id="videosPerRow">
  34. </label>
  35. <label>
  36. <span>Hide right sidebar</span>
  37. <input type="checkbox" id="hideRightSidebar">
  38. </label>
  39. <style>
  40. settings-menu svg {
  41. color: var(--background-3);
  42. width: 24px;
  43. height: 24px;
  44. transition: 0.4s;
  45. }
  46.  
  47. settings-menu svg:hover {
  48. color: inherit;
  49. transform: rotate(-90deg);
  50. }
  51.  
  52. settings-menu svg:active {
  53. transform: scale(1.5);
  54. }
  55.  
  56. settings-menu div {
  57. margin: 10px;
  58. }
  59.  
  60. settings-menu {
  61. width: 225px;
  62. position: fixed;
  63. background: var(--background);
  64. top: 20px;
  65. right: 20px;
  66. z-index: 9999;
  67. color: var(--text);
  68. font-size: 1.4rem;
  69. border-radius: 12px;
  70. box-shadow: 0 4px 32px 0 rgba(0, 0, 0, 0.1);
  71. font-weight: 400;
  72. border: 1px solid var(--border);
  73. }
  74.  
  75. settings-menu header {
  76. font-size: 1.7rem;
  77. font-weight: 500
  78. }
  79.  
  80. settings-menu header,
  81. settings-menu label {
  82. padding: 6px;
  83. display: flex;
  84. align-items: center;
  85. }
  86.  
  87. settings-menu svg,
  88. settings-menu label {
  89. cursor: pointer;
  90. }
  91.  
  92. settings-menu span {
  93. flex-grow: 1;
  94. }
  95.  
  96. settings-menu [type="number"] {
  97. font-family: "Roboto";
  98. font-size: inherit;
  99. box-sizing: border-box;
  100. border: none;
  101. width: 60px;
  102. height: 30px;
  103. background: var(--background-2);
  104. color: inherit;
  105. outline: none;
  106. border-radius: 5px;
  107. padding: 0 3px;
  108. }
  109.  
  110. settings-menu [type="number"]::-webkit-inner-spin-button {
  111. height: 30px;
  112. }
  113.  
  114. settings-menu [type="checkbox"] {
  115. appearance: none;
  116. -webkit-tap-highlight-color: transparent;
  117. position: relative;
  118. border: 0;
  119. outline: 0;
  120. width: 37.5px;
  121. height: 24px;
  122. cursor: pointer;
  123. }
  124.  
  125. settings-menu [type="checkbox"]:after {
  126. content: "";
  127. width: 100%;
  128. height: 100%;
  129. display: inline-block;
  130. border-radius: 100px;
  131. clear: both;
  132. background: var(--toggle-button-track);
  133. transition: background-color linear 0.08s;
  134. }
  135.  
  136. settings-menu [type="checkbox"]:checked:after {
  137. background: #1db954;
  138. }
  139.  
  140. settings-menu [type="checkbox"]:before {
  141. content: "";
  142. height: 19.5px;
  143. width: 19.5px;
  144. display: block;
  145. position: absolute;
  146. left: 0;
  147. top: 2px;
  148. border-radius: 50%;
  149. background: #fff;
  150. box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1), 0 2px 3px 0 rgba(0, 0, 0, 0.2);
  151. transform: translateX(2px);
  152. transition: transform linear 0.1s, background-color linear 0.08s;
  153. }
  154.  
  155. settings-menu [type="checkbox"]:checked:before {
  156. transform: translateX(16px);
  157. transition: transform linear 0.1s, background-color linear 0.08s;
  158. }
  159.  
  160. @media (prefers-color-scheme: dark) {
  161. :root {
  162. color-scheme: dark;
  163. --text: #f1f1f1;
  164. --background: #121212;
  165. --background-2: #272727;
  166. --background-3: #999;
  167. --toggle-button-track: rgba(113, 113, 113, 0.4);
  168. --border: rgba(255, 255, 255, 0.2)
  169. }
  170. }
  171.  
  172. @media (prefers-color-scheme: light) {
  173. :root {
  174. --text: #030303;
  175. --background: #fff;
  176. --background-2: #e5e5e5;
  177. --background-3: #666;
  178. --toggle-button-track: rgba(144, 144, 144, 0.4);
  179. }
  180. }
  181. </style>
  182. </div>`)
  183.  
  184. menu.addEventListener('change', function(event) {
  185. if (event.target.type === 'number') {
  186. GM_setValue('videosPerRow', event.target.value);
  187. document.documentElement.style.setProperty('--videos-per-row', event.target.value);
  188. } else {
  189. GM_setValue('hideRightSidebar', event.target.checked);
  190. document.documentElement.classList.toggle('hideRightSidebar');
  191. }
  192. })
  193.  
  194. menu.querySelector('#videosPerRow').value = GM_getValue('videosPerRow') ? GM_getValue('videosPerRow') : 5;
  195. menu.querySelector('#hideRightSidebar').checked = GM_getValue('hideRightSidebar');
  196. menu.querySelector('svg').addEventListener('click', function() {
  197. menu.remove();
  198. })
  199. }
  200.  
  201. const storedKeys = GM_listValues();
  202.  
  203. if (!storedKeys) document.documentElement.style.setProperty('--videos-per-row', '5');
  204. else {
  205. let userSettings = {};
  206. for (const key of storedKeys) userSettings[key] = GM_getValue(key);
  207.  
  208. applySettings({
  209. videosPerRow: 5,
  210. ...userSettings
  211. })
  212. }
  213.  
  214. function applySettings(settings) {
  215. document.documentElement.style.setProperty('--videos-per-row', settings.videosPerRow);
  216. if (settings.hideRightSidebar) document.documentElement.classList.add('hideRightSidebar');
  217. }
  218.  
  219. document.documentElement.insertAdjacentHTML('beforeend', `
  220. <style>
  221. ytd-search ytd-search-pyv-renderer,
  222. ytd-search ytd-ad-slot-renderer,
  223. ytd-search .metadata-snippet-container-one-line.ytd-video-renderer,
  224. ytd-search .metadata-snippet-container.ytd-video-renderer,
  225. ytd-search #description-text.ytd-video-renderer,
  226. ytd-search #expandable-metadata.ytd-video-renderer:not(:empty),
  227. ytd-search ytd-exploratory-results-renderer.ytd-item-section-renderer,
  228. ytd-search ytd-horizontal-card-list-renderer.ytd-item-section-renderer:not(:first-child),
  229. ytd-search ytd-reel-shelf-renderer.ytd-item-section-renderer,
  230. ytd-search ytd-shelf-renderer.ytd-item-section-renderer,
  231. .hideRightSidebar ytd-search #secondary.ytd-two-column-search-results-renderer {
  232. display: none !important;
  233. }
  234.  
  235. ytd-search #container.ytd-search {
  236. width: 100%;
  237. max-width: calc(var(--videos-per-row) * (var(--ytd-rich-grid-item-max-width) + var(--ytd-rich-grid-item-margin)));
  238. }
  239.  
  240. ytd-search #header.ytd-search,
  241. ytd-search ytd-two-column-search-results-renderer,
  242. ytd-search #primary.ytd-two-column-search-results-renderer {
  243. max-width: 100% !important;
  244. }
  245.  
  246. ytd-search #contents>ytd-item-section-renderer,
  247. ytd-search #contents>ytd-item-section-renderer>#contents {
  248. display: contents;
  249. }
  250.  
  251. ytd-search #contents.ytd-section-list-renderer {
  252. display: flex;
  253. flex-wrap: wrap;
  254. }
  255.  
  256. ytd-search ytd-video-renderer,
  257. ytd-search ytd-radio-renderer,
  258. ytd-search ytd-playlist-renderer,
  259. ytd-search ytd-show-renderer,
  260. ytd-search ytd-channel-renderer {
  261. margin-left: calc(var(--ytd-rich-grid-item-margin) / 2);
  262. margin-right: calc(var(--ytd-rich-grid-item-margin) / 2);
  263. width: calc(100% / var(--videos-per-row) - var(--ytd-rich-grid-item-margin) - 0.01px);
  264. margin-bottom: 24px;
  265. }
  266.  
  267. ytd-search ytd-movie-renderer {
  268. margin-left: calc(var(--ytd-rich-grid-item-margin) / 2);
  269. margin-right: calc(var(--ytd-rich-grid-item-margin) / 2);
  270. width: calc(200% / var(--videos-per-row) - var(--ytd-rich-grid-item-margin) - 0.01px);
  271. margin-bottom: 24px;
  272. }
  273.  
  274. ytd-search yt-did-you-mean-renderer,
  275. ytd-search yt-showing-results-for-renderer,
  276. ytd-search ytd-thumbnail.ytd-video-renderer,
  277. ytd-search ytd-playlist-thumbnail.ytd-radio-renderer,
  278. ytd-search ytd-playlist-thumbnail.ytd-playlist-renderer,
  279. ytd-search ytd-playlist-thumbnail.ytd-show-renderer {
  280. min-width: 100% !important;
  281. }
  282.  
  283. ytd-search .thumbnail-container.ytd-movie-renderer {
  284. min-width: 0 !important;
  285. }
  286.  
  287. ytd-search ytd-item-section-renderer[top-spacing-zero]:first-child #contents.ytd-item-section-renderer .ytd-item-section-renderer:first-child:not(yt-did-you-mean-renderer):not(yt-showing-results-for-renderer) {
  288. margin-top: 16px;
  289. }
  290.  
  291. ytd-search #dismissible.ytd-video-renderer,
  292. ytd-search ytd-radio-renderer,
  293. ytd-search ytd-playlist-renderer,
  294. ytd-search #content-section.ytd-channel-renderer,
  295. ytd-search #info-section.ytd-channel-renderer,
  296. ytd-search ytd-show-renderer {
  297. flex-direction: column;
  298. }
  299.  
  300. ytd-search .ytd-channel-renderer {
  301. align-self: center;
  302. text-align: center;
  303. }
  304.  
  305. ytd-search #video-title:is(.ytd-video-renderer, .ytd-radio-renderer, .ytd-playlist-renderer, .ytd-show-renderer, .ytd-movie-renderer),
  306. ytd-search #channel-title.ytd-channel-renderer {
  307. font-size: 1.4rem;
  308. line-height: 2rem;
  309. font-weight: 500;
  310. }
  311.  
  312. ytd-search .title-and-badge.ytd-video-renderer,
  313. ytd-search #channel-title.ytd-channel-renderer,
  314. ytd-search #content.ytd-radio-renderer,
  315. ytd-search #video-title.ytd-playlist-renderer,
  316. ytd-search h3.ytd-show-renderer {
  317. margin: 12px 0 4px 0 !important;
  318. }
  319.  
  320. ytd-search ytd-menu-renderer.ytd-video-renderer {
  321. margin: 4px 0 0 0;
  322. }
  323.  
  324. ytd-search .text-wrapper.ytd-video-renderer {
  325. max-width: 100% !important;
  326. }
  327.  
  328. ytd-search #content.ytd-playlist-renderer,
  329. ytd-search #content.ytd-radio-renderer,
  330. ytd-search #info-section.ytd-channel-renderer {
  331. flex-basis: 100%;
  332. }
  333.  
  334. ytd-search ytd-playlist-thumbnail.ytd-show-renderer {
  335. flex: 0 !important;
  336. }
  337.  
  338. ytd-search #info.ytd-channel-renderer {
  339. padding: 0 0px 16px 0;
  340. }
  341.  
  342. ytd-search #avatar-section.ytd-channel-renderer,
  343. ytd-search #buttons.ytd-channel-renderer,
  344. ytd-search #badges.ytd-video-renderer,
  345. ytd-search ytd-playlist-thumbnail.ytd-playlist-renderer {
  346. margin: 0 !important;
  347. }
  348.  
  349. ytd-search ytd-channel-renderer:not([dual-buttons]) #buttons.ytd-channel-renderer,
  350. ytd-search #purchase-button.ytd-channel-renderer,
  351. ytd-search #subscribe-button.ytd-channel-renderer {
  352. padding: 0;
  353. }
  354.  
  355. body[dir="ltr"] ytd-search ytd-menu-renderer.ytd-video-renderer {
  356. position: absolute;
  357. right: -12px;
  358. }
  359.  
  360. body[dir="ltr"] ytd-search #meta.ytd-video-renderer {
  361. padding-right: 24px;
  362. }
  363.  
  364. body[dir="rtl"] ytd-search ytd-menu-renderer.ytd-video-renderer {
  365. position: absolute;
  366. left: -12px;
  367. }
  368.  
  369. body[dir="rtl"] ytd-search #meta.ytd-video-renderer {
  370. padding-left: 24px;
  371. }
  372. </style>`)