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.

当前为 2024-08-14 提交的版本,查看 最新版本

  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 2024.8.14
  6. // @author Pedro
  7. // @icon https://i.imgur.com/DWVSLcD.png
  8. // @description See more results in one go, without irrelevant results, such as 'People also watched' and 'For you', in the way.
  9. // @contributionURL https://www.paypal.com/donate/?hosted_button_id=AF464CBPNVYB8
  10. // @grant GM_registerMenuCommand
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @compatible Chrome
  14. // @compatible Safari
  15. // @compatible Firefox
  16. // @license MIT
  17. // ==/UserScript==
  18. GM_registerMenuCommand('Settings', settingsMenu);
  19.  
  20. function settingsMenu() {
  21. try { trustedTypes.createPolicy('default', {createHTML: (string, sink) => string}); }
  22. catch {}
  23.  
  24. const menu = document.createElement('settings-menu');
  25. document.documentElement.appendChild(menu);
  26. menu.insertAdjacentHTML('afterbegin', `
  27. <div>
  28. <header>
  29. <span>Settings</span>
  30. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
  31. <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>
  32. </svg>
  33. </header>
  34. <label>
  35. <span>Maximum number of columns </span>
  36. <input type="number" id="maxNumOfColumns" value="${maxNumOfColumns}">
  37. </label>
  38. <label>
  39. <span>Minimum column width (px)</span>
  40. <input type="number" id="columnMinWidth" value="${columnMinWidth}">
  41. </label>
  42. <label>
  43. <span>Hide right sidebar</span>
  44. <input type="checkbox" id="hideRightSidebar" ${GM_getValue('hideRightSidebar') ? 'checked' : ''}>
  45. </label>
  46. <style>
  47. settings-menu svg {
  48. color: var(--background-3);
  49. width: 24px;
  50. height: 24px;
  51. transition: 0.4s;
  52. color: var(--text);
  53. opacity: .7;
  54. }
  55.  
  56. settings-menu svg:hover {
  57. transform: rotate(-90deg);
  58. opacity: 1;
  59. }
  60.  
  61. settings-menu svg:active {
  62. transform: scale(1.5);
  63. }
  64.  
  65. settings-menu div {
  66. margin: 10px;
  67. }
  68.  
  69. settings-menu {
  70. width: 310px;
  71. position: fixed;
  72. background: var(--background);
  73. top: 20px;
  74. right: 20px;
  75. z-index: 9999;
  76. color: var(--text);
  77. font-size: 1.4rem;
  78. border-radius: 20px;
  79. box-shadow: 0 4px 32px 0 rgba(0, 0, 0, 0.3);
  80. font-weight: 400;
  81. border: 1px solid var(--border);
  82. }
  83.  
  84. settings-menu header {
  85. font-size: 1.7rem;
  86. font-weight: 500;
  87. }
  88.  
  89. settings-menu header,
  90. settings-menu label {
  91. padding: 6px;
  92. display: flex;
  93. align-items: center;
  94. }
  95.  
  96. settings-menu svg,
  97. settings-menu label {
  98. cursor: pointer;
  99. }
  100.  
  101. settings-menu span {
  102. flex-grow: 1;
  103. }
  104.  
  105. settings-menu [type="number"] {
  106. font-family: "Roboto";
  107. font-size: inherit;
  108. box-sizing: border-box;
  109. border: none;
  110. width: 60px;
  111. height: 30px;
  112. background: var(--background-2);
  113. color: inherit;
  114. outline: none;
  115. border-radius: 6px;
  116. padding: 0 3px;
  117. }
  118.  
  119. settings-menu [type="number"]::-webkit-inner-spin-button {
  120. height: 30px;
  121. }
  122.  
  123. settings-menu [type="checkbox"] {
  124. appearance: none;
  125. -webkit-tap-highlight-color: transparent;
  126. position: relative;
  127. border: 0;
  128. outline: 0;
  129. width: 37.5px;
  130. height: 24px;
  131. cursor: pointer;
  132. }
  133.  
  134. settings-menu [type="checkbox"]:after {
  135. content: "";
  136. width: 100%;
  137. height: 100%;
  138. display: inline-block;
  139. border-radius: 100px;
  140. clear: both;
  141. background: var(--background-2);
  142. transition: background-color linear 0.08s;
  143. }
  144.  
  145. settings-menu [type="checkbox"]:checked:after {
  146. background: #1db954;
  147. }
  148.  
  149. settings-menu [type="checkbox"]:before {
  150. content: "";
  151. height: 19.5px;
  152. width: 19.5px;
  153. display: block;
  154. position: absolute;
  155. left: 0;
  156. top: 2px;
  157. border-radius: 50%;
  158. background: #fff;
  159. box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1), 0 2px 3px 0 rgba(0, 0, 0, 0.2);
  160. transform: translateX(2px);
  161. transition: transform linear 0.1s, background-color linear 0.08s;
  162. }
  163.  
  164. settings-menu [type="checkbox"]:checked:before {
  165. transform: translateX(80%);
  166. transition: transform linear 0.1s, background-color linear 0.08s;
  167. }
  168.  
  169. @media (prefers-color-scheme: dark) {
  170. :root {
  171. color-scheme: dark;
  172. --text: #f1f1f1;
  173. --background: #000;
  174. --background-2: rgba(255, 255, 255, .1);
  175. --border: rgba(255, 255, 255, .2);
  176. }
  177. }
  178.  
  179. @media (prefers-color-scheme: light) {
  180. :root {
  181. --text: #030303;
  182. --background: #fff;
  183. --background-2: rgba(0, 0, 0, .1);
  184. }
  185. }
  186. </style>
  187. </div>`);
  188.  
  189. menu.addEventListener('change', function(e) {
  190. if (e.target.type === 'number') {
  191. GM_setValue(e.target.id, e.target.value);
  192. if (e.target.id == 'maxNumOfColumns') maxNumOfColumns = GM_getValue('maxNumOfColumns');
  193. else columnMinWidth = GM_getValue('columnMinWidth');
  194. } else {
  195. GM_setValue(e.target.id, e.target.checked);
  196. }
  197.  
  198. handleResize();
  199. });
  200.  
  201. menu.querySelector('svg').addEventListener('click', function() {
  202. menu.remove();
  203. });
  204. }
  205.  
  206. let maxNumOfColumns = GM_getValue('maxNumOfColumns') || 6;
  207. let columnMinWidth = GM_getValue('columnMinWidth') || 326;
  208. const div = document.createElement('div');
  209. div.style = 'position: absolute; width: calc(100% - 32px - var(--sidebarWidth, 0px))';
  210.  
  211. document.addEventListener('yt-navigate-finish', function() {
  212. const pm = document.getElementById('page-manager');
  213. pm.style.position = 'relative';
  214. pm.appendChild(div);
  215.  
  216. new ResizeObserver(function() {
  217. handleResize();
  218. }).observe(div);
  219. }, {once: true});
  220.  
  221. document.addEventListener('yt-navigate-finish', function() {
  222. if (/(@|\/c\/).*search/.test(window.location.href)) {
  223. document.documentElement.classList.add('channelSearch');
  224. } else {
  225. document.documentElement.classList.remove('channelSearch');
  226. }
  227. });
  228.  
  229. function handleResize() {
  230. let hasRun;
  231.  
  232. for (let i = maxNumOfColumns; i > 0; i--) {
  233. if (div.clientWidth / i >= columnMinWidth) {
  234. document.documentElement.style.setProperty('--numOfColumns', i);
  235. hasRun = 1;
  236. break;
  237. }
  238. }
  239.  
  240. if (!hasRun) document.documentElement.style.setProperty('--numOfColumns', 1);
  241. }
  242.  
  243. const sheet = document.createElement('style');
  244. document.documentElement.appendChild(sheet);
  245. sheet.textContent = `
  246. ytd-search {
  247. padding-left: 16px !important;
  248. padding-right: 16px !important;
  249. }
  250. ytd-search ytd-search-pyv-renderer,
  251. ytd-search ytd-ad-slot-renderer,
  252. ytd-search .metadata-snippet-container-one-line.ytd-video-renderer,
  253. ytd-search .metadata-snippet-container.ytd-video-renderer,
  254. ytd-search #description-text.ytd-video-renderer,
  255. ytd-search #description.ytd-channel-renderer,
  256. ytd-search #list,
  257. ytd-search #expandable-metadata.ytd-video-renderer:not(:empty),
  258. ytd-search ytd-exploratory-results-renderer.ytd-item-section-renderer,
  259. ytd-search ytd-horizontal-card-list-renderer.ytd-item-section-renderer:not(:first-child),
  260. ytd-search ytd-reel-shelf-renderer.ytd-item-section-renderer,
  261. ytd-search ytd-shelf-renderer.ytd-item-section-renderer,
  262. ytd-search #channel-name.ytd-video-renderer,
  263. ytd-search #separator.ytd-video-meta-block {
  264. display: none !important;
  265. }
  266. ytd-search #view-more.ytd-playlist-renderer,
  267. ytd-search #metadata.ytd-video-meta-block,
  268. ytd-search #byline-container[hidden] {
  269. display: block !important;
  270. }
  271. ytd-search #container.ytd-search {
  272. max-width: calc(var(--numOfColumns) * (var(--ytd-rich-grid-item-max-width) + var(--ytd-rich-grid-item-margin)) + var(--sidebarWidth, 0px));
  273. min-width: calc(var(--sidebarWidth) + var(--ytd-rich-grid-item-max-width));
  274. }
  275. ytd-search #header.ytd-search,
  276. ytd-search ytd-two-column-search-results-renderer,
  277. ytd-search #primary.ytd-two-column-search-results-renderer {
  278. max-width: 100% !important;
  279. }
  280. ytd-search #contents > ytd-item-section-renderer,
  281. ytd-search #contents > ytd-item-section-renderer > #contents {
  282. display: contents;
  283. }
  284. ytd-search ytd-item-section-renderer[can-show-more]:after {
  285. /* Fix for elements appearing in wrong spots */
  286. content: "";
  287. width: 100%;
  288. min-height: 7000px;
  289. }
  290. ytd-search #contents.ytd-section-list-renderer {
  291. display: flex;
  292. flex-wrap: wrap;
  293. }
  294. ytd-search #contents > .ytd-item-section-renderer {
  295. margin-left: calc(var(--ytd-rich-grid-item-margin)/2);
  296. margin-right: calc(var(--ytd-rich-grid-item-margin)/2);
  297. width: calc(100%/var(--numOfColumns) - var(--ytd-rich-grid-item-margin) - 0.01px);
  298. margin-bottom: 24px;
  299. }
  300. ytd-search ytd-movie-renderer {
  301. width: calc(200%/var(--numOfColumns) - var(--ytd-rich-grid-item-margin) - 0.01px) !important;
  302. }
  303. ytd-search yt-did-you-mean-renderer,
  304. ytd-search yt-showing-results-for-renderer,
  305. ytd-search ytd-thumbnail.ytd-video-renderer,
  306. ytd-search ytd-playlist-thumbnail.ytd-radio-renderer,
  307. ytd-search ytd-playlist-thumbnail.ytd-playlist-renderer,
  308. ytd-search ytd-playlist-thumbnail.ytd-show-renderer {
  309. min-width: 100% !important;
  310. }
  311. ytd-search ytd-playlist-thumbnail.ytd-show-renderer {
  312. flex: 0 !important;
  313. }
  314. ytd-search ytd-playlist-thumbnail.ytd-playlist-renderer {
  315. margin: 0 !important;
  316. }
  317. ytd-search .thumbnail-container.ytd-movie-renderer {
  318. min-width: 0 !important;
  319. }
  320. 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) {
  321. margin-top: 16px;
  322. }
  323. ytd-search #dismissible.ytd-video-renderer,
  324. ytd-search ytd-radio-renderer,
  325. ytd-search ytd-playlist-renderer,
  326. ytd-search #content-section.ytd-channel-renderer,
  327. ytd-search #info-section.ytd-channel-renderer,
  328. ytd-search ytd-show-renderer {
  329. flex-direction: column;
  330. }
  331. ytd-search #video-title {
  332. font-size: 1.6rem !important;
  333. line-height: 2.2rem !important;
  334. font-weight: 500 !important;
  335. }
  336. ytd-search h3:not(.ytd-movie-renderer) {
  337. margin: 12px 0 4px 0 !important;
  338. }
  339. ytd-search #channel-title.ytd-channel-renderer {
  340. font-size: 1.6rem;
  341. line-height: 2.2rem;
  342. font-weight: 500;
  343. margin: 12px 0 4px 0;
  344. align-self: center;
  345. }
  346. ytd-search ytd-video-meta-block:not([rich-meta]) .ytd-video-meta-block:is(#metadata-line, #byline-container),
  347. ytd-search #metadata.ytd-channel-renderer {
  348. font-size: 1.4rem;
  349. line-height: 2rem;
  350. max-height: 3.6rem;
  351. }
  352. ytd-search #content.ytd-playlist-renderer,
  353. ytd-search #content.ytd-radio-renderer,
  354. ytd-search #info-section.ytd-channel-renderer {
  355. flex-basis: 100%;
  356. }
  357. ytd-search #info.ytd-channel-renderer {
  358. padding: 0 0px 16px 0;
  359. text-align: center;
  360. }
  361. ytd-search .ytd-channel-renderer yt-formatted-string {
  362. text-align: center !important;
  363. }
  364. ytd-search #avatar-section.ytd-channel-renderer {
  365. min-width: 100% !important;
  366. }
  367. ytd-search .ytd-channel-renderer:is(#buttons, #purchase-button, #subscribe-button) {
  368. padding: 0 !important;
  369. align-self: center;
  370. }
  371. ytd-search #badges.ytd-video-renderer {
  372. margin: 4px 0 0 0;
  373. }
  374. ytd-search .text-wrapper.ytd-video-renderer {
  375. position: relative;
  376. width: 100%;
  377. }
  378. ytd-search #channel-thumbnail.ytd-video-renderer {
  379. position: absolute;
  380. top: 12px;
  381. }
  382. ytd-search yt-img-shadow.ytd-video-renderer img.yt-img-shadow {
  383. width: 36px;
  384. height: 36px;
  385. }
  386. ytd-search ytd-video-renderer[use-search-ui] #channel-info.ytd-video-renderer {
  387. padding: 0;
  388. }
  389. body[dir="ltr"] ytd-search ytd-menu-renderer.ytd-video-renderer {
  390. position: absolute;
  391. right: -12px;
  392. margin: 4px 0 0 0;
  393. }
  394. body[dir="ltr"] ytd-search #title-wrapper.ytd-video-renderer {
  395. padding-right: 24px;
  396. }
  397. body[dir="ltr"] ytd-search .ytd-video-renderer:is(#title-wrapper, #channel-name, #badges, #buttons),
  398. body[dir="ltr"] ytd-search ytd-video-meta-block.ytd-video-renderer {
  399. padding-left: 48px;
  400. }
  401. body[dir="rtl"] ytd-search ytd-menu-renderer.ytd-video-renderer {
  402. position: absolute;
  403. left: -12px;
  404. margin: 4px 0 0 0;
  405. }
  406. body[dir="rtl"] ytd-search #title-wrapper.ytd-video-renderer {
  407. padding-left: 24px;
  408. }
  409. body[dir="rtl"] ytd-search .ytd-video-renderer:is(#title-wrapper, #channel-name, #badges, #buttons),
  410. body[dir="rtl"] ytd-search ytd-video-meta-block.ytd-video-renderer {
  411. padding-right: 48px;
  412. }
  413. .channelSearch #description-text.ytd-video-renderer,
  414. .channelSearch #list.ytd-playlist-renderer {
  415. display: none;
  416. }
  417. .channelSearch #metadata.ytd-video-meta-block,
  418. .channelSearch #view-more.ytd-playlist-renderer {
  419. display: block;
  420. }
  421. .channelSearch ytd-browse[page-subtype=channels] ytd-two-column-browse-results-renderer {
  422. width: calc(100% - 32px) !important;
  423. max-width: calc(var(--numOfColumns) * (var(--ytd-rich-grid-item-max-width) + var(--ytd-rich-grid-item-margin))) !important;
  424. }
  425. .channelSearch #contents.ytd-item-section-renderer,
  426. .channelSearch ytd-item-section-renderer.ytd-section-list-renderer {
  427. display: contents;
  428. }
  429. .channelSearch #contents.ytd-section-list-renderer {
  430. display: flex;
  431. flex-wrap: wrap;
  432. }
  433. .channelSearch ytd-video-renderer,
  434. .channelSearch ytd-playlist-renderer,
  435. .channelSearch ytd-show-renderer {
  436. width: calc(100% / var(--numOfColumns) - var(--ytd-rich-grid-item-margin) - 0.01px);
  437. margin-left: calc(var(--ytd-rich-grid-item-margin) / 2);
  438. margin-right: calc(var(--ytd-rich-grid-item-margin) / 2);
  439. margin-bottom: 24px;
  440. }
  441. .channelSearch ytd-thumbnail.ytd-video-renderer,
  442. .channelSearch ytd-playlist-thumbnail.ytd-playlist-renderer,
  443. .channelSearch ytd-playlist-thumbnail.ytd-show-renderer {
  444. width: 100% !important;
  445. height: 100% !important;
  446. flex: 0 !important;
  447. }
  448. .channelSearch ytd-thumbnail.ytd-video-renderer:before,
  449. .channelSearch ytd-playlist-thumbnail.ytd-playlist-renderer:before,
  450. .channelSearch ytd-playlist-thumbnail.ytd-show-renderer:before {
  451. display: block;
  452. content: "";
  453. padding-top: 56.11%;
  454. }
  455. .channelSearch #dismissible.ytd-video-renderer,
  456. .channelSearch ytd-playlist-renderer,
  457. .channelSearch ytd-show-renderer {
  458. flex-direction: column;
  459. }
  460. .channelSearch #video-title {
  461. font-size: 1.6rem !important;
  462. line-height: 2.2rem !important;
  463. font-weight: 500 !important;
  464. }
  465. .channelSearch ytd-video-meta-block:not([rich-meta]) .ytd-video-meta-block:is(#byline-container, #metadata-line) {
  466. font-size: 1.4rem;
  467. line-height: 2rem;
  468. max-height: 3.6rem;
  469. }
  470. .channelSearch .title-and-badge.ytd-video-renderer,
  471. .channelSearch h3:is(.ytd-playlist-renderer, .ytd-show-renderer) {
  472. margin: 12px 0 4px 0;
  473. }
  474. .channelSearch #badges.ytd-video-renderer {
  475. margin: 4px 0 0 0;
  476. }
  477. .channelSearch #content.ytd-playlist-renderer {
  478. flex-basis: 100%;
  479. }
  480. .channelSearch .text-wrapper.ytd-video-renderer {
  481. max-width: 100%;
  482. }
  483. .channelSearch body[dir="ltr"] ytd-menu-renderer.ytd-video-renderer {
  484. position: absolute;
  485. right: -12px;
  486. margin-top: 4px;
  487. }
  488. .channelSearch body[dir="ltr"] #meta.ytd-video-renderer {
  489. padding-right: 24px;
  490. }
  491. .channelSearch body[dir="rtl"] ytd-menu-renderer.ytd-video-renderer {
  492. position: absolute;
  493. left: -12px;
  494. margin-top: 4px;
  495. }
  496. .channelSearch body[dir="rtl"] #meta.ytd-video-renderer {
  497. padding-left: 24px;
  498. }
  499.  
  500. ${GM_getValue('hideRightSidebar') ? `
  501. ytd-secondary-search-container-renderer {
  502. display: none !important;
  503. }
  504. `:`
  505. @media (min-width: 1091px) {
  506. #page-manager:has(ytd-search[role][has-secondary-content]) {
  507. --sidebarWidth: 425px;
  508. }
  509. }
  510. `}
  511. `;