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-06-25 提交的版本,查看 最新版本

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