YouTube Layout Fix

Forces 6 videos per row, adjusts text sizes, hides checkmarks, adjusts spacing.

目前為 2025-04-16 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name YouTube Layout Fix
  3. // @namespace http://tampermonkey.net/
  4. // @version 3.40
  5. // @description Forces 6 videos per row, adjusts text sizes, hides checkmarks, adjusts spacing.
  6. // @author Kalakaua
  7. // @match https://www.youtube.com/
  8. // @match https://www.youtube.com/feed/subscriptions*
  9. // @match https://www.youtube.com/feed/playlists*
  10. // @match https://www.youtube.com/channel/*
  11. // @match https://www.youtube.com/@*
  12. // @match https://www.youtube.com/watch*
  13. // @match https://www.youtube.com/results* // Target search results
  14. // @match https://www.youtube.com/playlist*
  15. // @match https://www.youtube.com/feed/history*
  16. // @grant GM_addStyle
  17. // @run-at document-start
  18. // @license MIT
  19. // ==/UserScript==
  20.  
  21. (function() {
  22. 'use strict';
  23.  
  24. // --- FINAL ADJUSTABLE VALUES (User Prefs) ---
  25.  
  26. // === Column Layout ===
  27. const desiredColumnCount = 6; // Number of columns for main content grids (Home, Subs, Playlists Feed, Channels)
  28.  
  29. // === Grid Items - Text Sizes (Home, Subs, Playlists Feed, Channel Pages*) ===
  30. const gridTitleScale = 1.12; // Title text size multiplier (em)
  31. const gridChannelScale = 0.85; // Channel name text size multiplier (em)
  32. const gridMetadataScale = 0.85; // Views/Date text size multiplier (em)
  33.  
  34. // === Grid Items - Internal Spacing ===
  35. const gridChannelMarginTop = "-1px"; // Space above channel name
  36. const gridMetaMarginTop = "-4px"; // Space above views/date line
  37.  
  38. // === Overall Grid Structure ===
  39. const gridEdgePadding = "24px"; // Padding on the left/right edges of the main grids
  40. const gridItemHorizontalMargin= "6px"; // Space between grid items horizontally
  41. const gridItemBottomMargin = "24px"; // Space below each grid item
  42.  
  43. // === Search Results Page (/results) ===
  44. const searchThumbnailWidth = "350px"; // Width of video thumbnails
  45. const searchResultsMarginTop = "16px"; // Space above the first search result
  46. const searchTitleScale = 1.4; // Scale factor for video titles (based on 1rem)
  47. const searchChannelScale = 1.20; // Scale factor for channel names (based on ~0.8em)
  48. const searchMetaSnippetScale = 1.25; // Scale factor for views/date/description snippet (based on ~0.8em)
  49. const searchDescFixedWidth = "800px"; // Fixed width for description snippet container (helps align 3-dot menu)
  50.  
  51. // === Watch Page - Below Video Player ===
  52. const watchOwnerChannelScale = 2.1; // Scale factor for channel name under video
  53. const watchTopRowMarginTop = "-4px"; // Space above views/date line under video title
  54. const watchSubCountMarginTop = "-3.5px";// Space above subscriber count
  55.  
  56. // === Sidebar (Watch Page Right Sidebar - Compact Video List) ===
  57. const sidebarTitleScale = 1.05; // Scale factor for video titles
  58. const sidebarChannelNameScale = 1.0; // Scale factor for channel names
  59. const sidebarViewsDateScale = 1.65; // Scale factor for views/date
  60. const sidebarTitleMarginBottom= "6px"; // Space below sidebar video titles
  61.  
  62. // === Sidebar Badge Styling (Watch Page Right Sidebar) ===
  63. const sidebarBadgeScale = 0.85; // Scale factor for badges (e.g., "New")
  64. const sidebarBadgeMarginTop = "2px"; // Space above badges
  65. const sidebarBadgeMarginBottom= "0px"; // Space below badges
  66.  
  67. // === Comment Section (Watch Page) ===
  68. const commentTextScale = 1.15; // Scale factor for main comment and reply text (applied to 1rem base)
  69. const commentMetaScale = 1.1; // Scale factor for author/timestamp (applied to ~0.8rem base)
  70.  
  71. // --- Technical Values ---
  72. const minimalPxReduction = "0.01px"; // Minimal reduction for calc() robustness in grid width calculation
  73.  
  74. // --- END OF ADJUSTABLE VALUES ---
  75.  
  76.  
  77. const css = `
  78. :root {
  79. /* Grid Variables */
  80. --gm-grid-title-size: ${gridTitleScale}em;
  81. --gm-grid-channel-size: ${gridChannelScale}em;
  82. --gm-grid-metadata-size: ${gridMetadataScale}em;
  83.  
  84. /* Watch Page Variables */
  85. --gm-watch-owner-channel-size: ${watchOwnerChannelScale}em;
  86. --gm-watch-title-size: 1.5rem; /* Fixed size for main video title */
  87.  
  88. /* Sidebar Variables */
  89. --gm-sidebar-title-size: ${sidebarTitleScale}em;
  90. --gm-sidebar-channel-size: ${sidebarChannelNameScale}em;
  91. --gm-sidebar-viewsdate-size: ${sidebarViewsDateScale}em;
  92. --gm-sidebar-badge-size: ${sidebarBadgeScale}em;
  93.  
  94. /* Search Results Variables */
  95. --gm-search-title-scale: ${searchTitleScale};
  96. --gm-search-channel-scale: ${searchChannelScale};
  97. --gm-search-metasnippet-scale: ${searchMetaSnippetScale};
  98. --gm-search-title-size: calc(1.0rem * var(--gm-search-title-scale));
  99. --gm-search-channel-size: calc(0.8em * var(--gm-search-channel-scale));
  100. --gm-search-metasnippet-size: calc(0.8em * var(--gm-search-metasnippet-scale));
  101.  
  102. /* Comment Section Variables (REM based for consistency) */
  103. --gm-comment-text-final-size: calc(1rem * ${commentTextScale});
  104. --gm-comment-meta-final-size: calc(0.8rem * ${commentMetaScale});
  105. }
  106.  
  107. /* ========================================================== */
  108. /* === VARIABLE ITEMS PER ROW - MAIN GRIDS === */
  109. /* ========================================================== */
  110.  
  111. /* --- Global Container Padding & Width --- */
  112. #contents.ytd-rich-grid-renderer { padding-left: ${gridEdgePadding} !important; padding-right: ${gridEdgePadding} !important; box-sizing: border-box !important; width: 100% !important; }
  113. ytd-browse[page-subtype="channels"] #contents.ytd-rich-grid-renderer, ytd-browse[page-subtype="channels"] ytd-item-section-renderer #contents > ytd-rich-grid-renderer { padding-left: ${gridEdgePadding} !important; padding-right: ${gridEdgePadding} !important; box-sizing: border-box !important; width: 100% !important; }
  114.  
  115. /* --- Rule Set 2: Playlist Feed Specific Styles (ytd-rich-item-renderer, via :not()) --- */
  116. #contents.ytd-rich-grid-renderer > ytd-rich-item-renderer:not( ytd-browse[page-subtype="home"] #contents.ytd-rich-grid-renderer > ytd-rich-item-renderer, ytd-browse[page-subtype="subscriptions"] #contents.ytd-rich-grid-renderer > ytd-rich-item-renderer, ytd-browse[page-subtype="channels"] #contents.ytd-rich-grid-renderer > ytd-rich-item-renderer ) { margin-left: ${gridItemHorizontalMargin} !important; margin-right: ${gridItemHorizontalMargin} !important; margin-bottom: ${gridItemBottomMargin} !important; max-width: calc(100% / ${desiredColumnCount} - ${gridItemHorizontalMargin} * 2 - ${minimalPxReduction}) !important; }
  117. #contents.ytd-rich-grid-renderer ytd-rich-item-renderer:not(...) .yt-lockup-metadata-view-model-wiz__title { font-size: var(--gm-grid-title-size) !important; line-height: 1.2em !important; max-height: 2.4em !important; overflow: hidden !important; text-overflow: ellipsis !important; display: -webkit-box !important; -webkit-line-clamp: 2 !important; -webkit-box-orient: vertical !important; margin-bottom: 1px !important; }
  118. #contents.ytd-rich-grid-renderer ytd-rich-item-renderer:not(...) .yt-content-metadata-view-model-wiz__metadata-text { font-size: var(--gm-grid-metadata-size) !important; line-height: 1.3em !important; display: inline !important; }
  119. #contents.ytd-rich-grid-renderer ytd-rich-item-renderer:not(...) .yt-lockup-metadata-view-model-wiz__metadata { margin-top: ${gridMetaMarginTop} !important; line-height: 1.3em; }
  120. #contents.ytd-rich-grid-renderer ytd-rich-item-renderer:not(...) a.yt-core-attributed-string__link--call-to-action-color { font-size: 0.9em !important; margin-top: 2px !important; }
  121.  
  122. /* --- Rule Set 1: Home, Subscriptions, AND Channel Homepage Grids (ytd-rich-item-renderer) --- */
  123. ytd-browse[page-subtype="home"] #contents.ytd-rich-grid-renderer > ytd-rich-item-renderer, ytd-browse[page-subtype="subscriptions"] #contents.ytd-rich-grid-renderer > ytd-rich-item-renderer, ytd-browse[page-subtype="channels"] #contents.ytd-rich-grid-renderer > ytd-rich-item-renderer { margin-left: ${gridItemHorizontalMargin} !important; margin-right: ${gridItemHorizontalMargin} !important; margin-bottom: ${gridItemBottomMargin} !important; max-width: calc(100% / ${desiredColumnCount} - ${gridItemHorizontalMargin} * 2 - ${minimalPxReduction}) !important; border: none !important; }
  124. ytd-browse[page-subtype="home"] ytd-rich-item-renderer #video-title.ytd-rich-grid-media, ytd-browse[page-subtype="subscriptions"] ytd-rich-item-renderer #video-title.ytd-rich-grid-media, ytd-browse[page-subtype="channels"] ytd-rich-item-renderer #video-title.ytd-rich-grid-media { font-size: var(--gm-grid-title-size) !important; line-height: 1.2em !important; max-height: 2.4em !important; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; margin-bottom: 1px !important; }
  125. ytd-browse[page-subtype="home"] ytd-rich-item-renderer #byline-container, ytd-browse[page-subtype="subscriptions"] ytd-rich-item-renderer #byline-container, ytd-browse[page-subtype="channels"] ytd-rich-item-renderer #byline-container { white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; display: flex !important; align-items: center !important; margin-top: ${gridChannelMarginTop} !important; line-height: 1.2em !important; gap: 0 !important; }
  126. ytd-browse[page-subtype="home"] ytd-rich-item-renderer ytd-channel-name, ytd-browse[page-subtype="subscriptions"] ytd-rich-item-renderer ytd-channel-name, ytd-browse[page-subtype="channels"] ytd-rich-item-renderer ytd-channel-name { font-size: var(--gm-grid-channel-size) !important; line-height: 1.25em !important; display: inline-block !important; overflow: hidden !important; text-overflow: ellipsis !important; flex-grow: 1 !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; vertical-align: baseline !important; }
  127. ytd-browse[page-subtype="home"] ytd-rich-item-renderer #metadata-line, ytd-browse[page-subtype="subscriptions"] ytd-rich-item-renderer #metadata-line, ytd-browse[page-subtype="channels"] ytd-rich-item-renderer #metadata-line { line-height: 1.3em !important; margin-top: ${gridMetaMarginTop} !important; display: block !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; }
  128. ytd-browse[page-subtype="home"] ytd-rich-item-renderer #metadata-line > span, ytd-browse[page-subtype="subscriptions"] ytd-rich-item-renderer #metadata-line > span, ytd-browse[page-subtype="channels"] ytd-rich-item-renderer #metadata-line > span { font-size: var(--gm-grid-metadata-size) !important; line-height: 1.2em !important; display: inline !important; vertical-align: baseline !important; }
  129. ytd-browse[page-subtype="home"] ytd-rich-item-renderer #metadata-line > span:first-of-type, ytd-browse[page-subtype="subscriptions"] ytd-rich-item-renderer #metadata-line > span:first-of-type, ytd-browse[page-subtype="channels"] ytd-rich-item-renderer #metadata-line > span:first-of-type { margin-right: 0.5em !important; }
  130. ytd-browse[page-subtype="home"] ytd-rich-item-renderer #metadata-line > span:first-of-type::after, ytd-browse[page-subtype="subscriptions"] ytd-rich-item-renderer #metadata-line > span:first-of-type::after, ytd-browse[page-subtype="channels"] ytd-rich-item-renderer #metadata-line > span:first-of-type::after { content: none !important; }
  131.  
  132. /* --- Rule Set 3: Channel Pages SPECIFIC TABS (e.g., Videos Tab using ytd-grid-video-renderer) --- */
  133. ytd-browse[page-subtype="channels"] #contents.ytd-rich-grid-renderer ytd-grid-video-renderer, ytd-browse[page-subtype="channels"] ytd-item-section-renderer #contents > ytd-rich-grid-renderer ytd-grid-video-renderer { margin-left: ${gridItemHorizontalMargin} !important; margin-right: ${gridItemHorizontalMargin} !important; margin-bottom: ${gridItemBottomMargin} !important; max-width: calc(100% / ${desiredColumnCount} - ${gridItemHorizontalMargin} * 2 - ${minimalPxReduction}) !important; }
  134. ytd-browse[page-subtype="channels"] ytd-grid-video-renderer #video-title { font-size: var(--gm-grid-title-size) !important; line-height: 1.2em !important; max-height: 2.4em !important; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; margin-bottom: 1px !important; }
  135. ytd-browse[page-subtype="channels"] ytd-grid-video-renderer #metadata-line { line-height: 1.3em !important; margin-top: ${gridMetaMarginTop} !important; display: block !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; }
  136. ytd-browse[page-subtype="channels"] ytd-grid-video-renderer #metadata-line > span { font-size: var(--gm-grid-metadata-size) !important; line-height: 1.2em !important; display: inline !important; vertical-align: baseline !important; }
  137. ytd-browse[page-subtype="channels"] ytd-grid-video-renderer #metadata-line > span:first-of-type { margin-right: 0.5em !important; }
  138. ytd-browse[page-subtype="channels"] ytd-grid-video-renderer #metadata-line > span:first-of-type::after { content: none !important; }
  139.  
  140. /* ========================================================== */
  141. /* === GLOBAL STYLES & FIXES === */
  142. /* ========================================================== */
  143. /* Hide Checkmarks */
  144. ytd-badge-supported-renderer:has(.badge.badge-style-type-verified) { display: none !important; }
  145. /* Fullscreen Scrollbar Fix */
  146. html.fullscreen, html.fullscreen body { overflow-x: hidden !important; }
  147.  
  148. /* ========================================================== */
  149. /* === SEARCH RESULTS PAGE (/results) === */
  150. /* ========================================================== */
  151. ytd-search ytd-item-section-renderer:first-of-type { margin-top: ${searchResultsMarginTop} !important; }
  152. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] { display: flex !important; margin-bottom: 16px !important; align-items: flex-start !important; }
  153. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] #dismissible ytd-thumbnail.ytd-video-renderer { width: ${searchThumbnailWidth} !important; min-width: ${searchThumbnailWidth} !important; max-width: ${searchThumbnailWidth} !important; flex-basis: ${searchThumbnailWidth} !important; flex-shrink: 0 !important; }
  154. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] #dismissible div.text-wrapper.ytd-video-renderer { margin-left: 12px !important; flex: 1 !important; min-width: 0; }
  155. /* Text Sizes */
  156. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] a#video-title.ytd-video-renderer yt-formatted-string { font-size: var(--gm-search-title-size) !important; line-height: 1.25em !important; max-height: 2.5em !important; -webkit-line-clamp: 2 !important; display: -webkit-box !important; -webkit-box-orient: vertical !important; overflow: hidden !important; text-overflow: ellipsis !important; margin-bottom: 3px !important; }
  157. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] #metadata-line.ytd-video-meta-block span.inline-metadata-item { font-size: var(--gm-search-metasnippet-size) !important; line-height: 1.3em !important; }
  158. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] #channel-name.ytd-video-renderer yt-formatted-string > a, ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] #channel-name.ytd-video-renderer yt-formatted-string { font-size: var(--gm-search-channel-size) !important; line-height:1.3em !important; }
  159. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] #channel-thumbnail.ytd-video-renderer { width: 20px !important; height: 20px !important; }
  160. /* Description Snippet */
  161. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] .metadata-snippet-container.ytd-video-renderer yt-formatted-string { font-size: var(--gm-search-metasnippet-size) !important; line-height: 1.3em !important; max-height: 2.6em !important; overflow: hidden !important; display: -webkit-box !important; -webkit-line-clamp: 2 !important; -webkit-box-orient: vertical !important; }
  162. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] .metadata-snippet-container.ytd-video-renderer { width: ${searchDescFixedWidth} !important; min-width: ${searchDescFixedWidth} !important; max-width: ${searchDescFixedWidth} !important; display: block !important; margin-top: 4px !important; }
  163. /* Hide Chapters Section */
  164. ytd-search ytd-item-section-renderer ytd-video-renderer[is-search] #expandable-metadata.ytd-video-renderer { display: none !important; }
  165.  
  166. /* ========================================================== */
  167. /* === WATCH PAGE (/watch) === */
  168. /* ========================================================== */
  169. /* --- Below Video Player --- */
  170. .title.ytd-video-primary-info-renderer h1.ytd-video-primary-info-renderer { font-size: var(--gm-watch-title-size) !important; line-height: 1.2em !important; }
  171. #top-row.ytd-watch-metadata { margin-top: ${watchTopRowMarginTop} !important; }
  172. ytd-video-owner-renderer ytd-channel-name { font-size: var(--gm-watch-owner-channel-size) !important; line-height: 1.05em !important; margin: 0 !important; padding: 0 !important; display: block !important; }
  173. #owner-sub-count.ytd-video-owner-renderer { margin-top: ${watchSubCountMarginTop} !important; margin-bottom: 0 !important; }
  174. #description-inner #description .content.ytd-video-secondary-info-renderer, .ytd-expander.ytd-video-secondary-info-renderer { font-size: 0.8em !important; line-height: 1.35em !important; }
  175. #info-text.ytd-video-primary-info-renderer { font-size: 0.8em !important; }
  176.  
  177. /* --- Sidebar --- */
  178. ytd-compact-video-renderer h3.ytd-compact-video-renderer { margin-bottom: ${sidebarTitleMarginBottom} !important; margin-top: 0 !important; }
  179. #video-title.ytd-compact-video-renderer { font-size: var(--gm-sidebar-title-size) !important; line-height: 1.3em !important; max-height: 2.6em !important; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  180. ytd-compact-video-renderer ytd-channel-name { font-size: var(--gm-sidebar-channel-size) !important; line-height: 1.35em !important; margin-bottom: 1px !important; }
  181. ytd-compact-video-renderer #metadata-line span.inline-metadata-item { font-size: var(--gm-sidebar-viewsdate-size) !important; line-height: 1.45em !important; }
  182. ytd-compact-video-renderer ytd-badge-supported-renderer.badges { margin-top: ${sidebarBadgeMarginTop} !important; margin-bottom: ${sidebarBadgeMarginBottom} !important; font-size: var(--gm-sidebar-badge-size) !important; line-height: 1.2 !important; }
  183.  
  184. /* --- Comment Section --- */
  185. /* Reset Reply Expander font size (Neutralizes default 1.4em) */
  186. ytd-comment-replies-renderer ytd-comment-view-model[is-reply] > #main > #expander {
  187. font-size: 1rem !important;
  188. }
  189. /* Rule for Comment/Reply Text Content (Uses REM for consistency) */
  190. ytd-comment-view-model #expander > #content > yt-attributed-string#content-text {
  191. font-size: var(--gm-comment-text-final-size) !important;
  192. line-height: 1.5em !important;
  193. }
  194. /* Rule for Main Comment Meta (Author/Timestamp - Uses REM) */
  195. ytd-comments ytd-comment-thread-renderer > ytd-comment-view-model:not([is-reply]) #header-author #author-text,
  196. ytd-comments ytd-comment-thread-renderer > ytd-comment-view-model:not([is-reply]) #header-author .published-time-text.ytd-comment-view-model a {
  197. font-size: var(--gm-comment-meta-final-size) !important;
  198. line-height: 1.4em !important;
  199. }
  200. /* Rule for Reply Meta (Author/Timestamp - Uses REM) */
  201. ytd-comments ytd-comment-replies-renderer ytd-comment-view-model[is-reply] #header-author #author-text,
  202. ytd-comments ytd-comment-replies-renderer ytd-comment-view-model[is-reply] #header-author .published-time-text.ytd-comment-view-model a {
  203. font-size: var(--gm-comment-meta-final-size) !important;
  204. line-height: 1.4em !important;
  205. }
  206. /* Rule for Comment Toolbar */
  207. #toolbar.ytd-comment-action-buttons-renderer {
  208. font-size: var(--gm-comment-meta-final-size) !important;
  209. }
  210. /* Rule for Author Text Color */
  211. #header-author #author-text.ytd-comment-renderer {
  212. color: #aaa !important;
  213. }
  214.  
  215. `;
  216.  
  217. // Inject the CSS using GM_addStyle
  218. if (typeof GM_addStyle === 'function') {
  219. // Read version from metadata block for consistency
  220. const scriptVersion = (typeof GM_info !== 'undefined' && GM_info.script) ? GM_info.script.version : 'N/A';
  221. GM_addStyle(css);
  222. console.log(`YouTube Layout Script: v${scriptVersion} Active (${desiredColumnCount} Cols) - Final v3`); // Updated log message
  223. } else {
  224. console.error("YouTube Layout Script: GM_addStyle is not defined.");
  225. const style = document.createElement('style');
  226. style.textContent = css;
  227. document.head.appendChild(style);
  228. }
  229.  
  230. })();