YouTube - Non-Rounded Design

This script disables YouTube's new rounded corners (reverts back to the layout from 2021.)

当前为 2022-12-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube - Non-Rounded Design
  3. // @version 3.1.0
  4. // @description This script disables YouTube's new rounded corners (reverts back to the layout from 2021.)
  5. // @author Magma_Craft
  6. // @license MIT
  7. // @match *://*.youtube.com/*
  8. // @namespace https://gf.qytechs.cn/en/users/933798
  9. // @icon https://www.youtube.com/favicon.ico
  10. // @run-at document-start
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. // Attributes to remove from <html>
  15. const ATTRS = [
  16. ];
  17.  
  18. // Regular config keys.
  19. const CONFIGS = {
  20. BUTTON_REWORK: false
  21. }
  22.  
  23. // Experiment flags.
  24. const EXPFLAGS = {
  25. kevlar_unavailable_video_error_ui_client: false,
  26. kevlar_refresh_on_theme_change: false,
  27. kevlar_watch_cinematics: false,
  28. kevlar_watch_metadata_refresh: false,
  29. kevlar_watch_modern_metapanel: false,
  30. web_amsterdam_playlists: false,
  31. web_animated_like: false,
  32. web_button_rework: false,
  33. web_button_rework_with_live: false,
  34. web_darker_dark_theme: false,
  35. web_guide_ui_refresh: false,
  36. web_modern_ads: false,
  37. web_modern_buttons: false,
  38. web_modern_chips: false,
  39. web_modern_dialogs: false,
  40. web_modern_playlists: false,
  41. web_modern_subscribe: false,
  42. web_rounded_containers: false,
  43. web_rounded_thumbnails: false,
  44. web_searchbar_style: "default",
  45. web_segmented_like_dislike_button: false,
  46. web_sheets_ui_refresh: false,
  47. web_snackbar_ui_refresh: false
  48. }
  49.  
  50. // Player flags
  51. // !!! USE STRINGS FOR VALUES !!!
  52. // For example: "true" instead of true
  53. const PLYRFLAGS = {
  54. web_rounded_containers: "false",
  55. web_rounded_thumbnails: "false"
  56. }
  57.  
  58. class YTP {
  59. static observer = new MutationObserver(this.onNewScript);
  60.  
  61. static _config = {};
  62.  
  63. static isObject(item) {
  64. return (item && typeof item === "object" && !Array.isArray(item));
  65. }
  66.  
  67. static mergeDeep(target, ...sources) {
  68. if (!sources.length) return target;
  69. const source = sources.shift();
  70.  
  71. if (this.isObject(target) && this.isObject(source)) {
  72. for (const key in source) {
  73. if (this.isObject(source[key])) {
  74. if (!target[key]) Object.assign(target, { [key]: {} });
  75. this.mergeDeep(target[key], source[key]);
  76. } else {
  77. Object.assign(target, { [key]: source[key] });
  78. }
  79. }
  80. }
  81.  
  82. return this.mergeDeep(target, ...sources);
  83. }
  84.  
  85.  
  86. static onNewScript(mutations) {
  87. for (var mut of mutations) {
  88. for (var node of mut.addedNodes) {
  89. YTP.bruteforce();
  90. }
  91. }
  92. }
  93.  
  94. static start() {
  95. this.observer.observe(document, {childList: true, subtree: true});
  96. }
  97.  
  98. static stop() {
  99. this.observer.disconnect();
  100. }
  101.  
  102. static bruteforce() {
  103. if (!window.yt) return;
  104. if (!window.yt.config_) return;
  105.  
  106. this.mergeDeep(window.yt.config_, this._config);
  107. }
  108.  
  109. static setCfg(name, value) {
  110. this._config[name] = value;
  111. }
  112.  
  113. static setCfgMulti(configs) {
  114. this.mergeDeep(this._config, configs);
  115. }
  116.  
  117. static setExp(name, value) {
  118. if (!("EXPERIMENT_FLAGS" in this._config)) this._config.EXPERIMENT_FLAGS = {};
  119.  
  120. this._config.EXPERIMENT_FLAGS[name] = value;
  121. }
  122.  
  123. static setExpMulti(exps) {
  124. if (!("EXPERIMENT_FLAGS" in this._config)) this._config.EXPERIMENT_FLAGS = {};
  125.  
  126. this.mergeDeep(this._config.EXPERIMENT_FLAGS, exps);
  127. }
  128.  
  129. static decodePlyrFlags(flags) {
  130. var obj = {},
  131. dflags = flags.split("&");
  132.  
  133. for (var i = 0; i < dflags.length; i++) {
  134. var dflag = dflags[i].split("=");
  135. obj[dflag[0]] = dflag[1];
  136. }
  137.  
  138. return obj;
  139. }
  140.  
  141. static encodePlyrFlags(flags) {
  142. var keys = Object.keys(flags),
  143. response = "";
  144.  
  145. for (var i = 0; i < keys.length; i++) {
  146. if (i > 0) {
  147. response += "&";
  148. }
  149. response += keys[i] + "=" + flags[keys[i]];
  150. }
  151.  
  152. return response;
  153. }
  154.  
  155. static setPlyrFlags(flags) {
  156. if (!window.yt) return;
  157. if (!window.yt.config_) return;
  158. if (!window.yt.config_.WEB_PLAYER_CONTEXT_CONFIGS) return;
  159. var conCfgs = window.yt.config_.WEB_PLAYER_CONTEXT_CONFIGS;
  160. if (!("WEB_PLAYER_CONTEXT_CONFIGS" in this._config)) this._config.WEB_PLAYER_CONTEXT_CONFIGS = {};
  161.  
  162. for (var cfg in conCfgs) {
  163. var dflags = this.decodePlyrFlags(conCfgs[cfg].serializedExperimentFlags);
  164. this.mergeDeep(dflags, flags);
  165. this._config.WEB_PLAYER_CONTEXT_CONFIGS[cfg] = {
  166. serializedExperimentFlags: this.encodePlyrFlags(dflags)
  167. }
  168. }
  169. }
  170. }
  171.  
  172. window.addEventListener("yt-page-data-updated", function tmp() {
  173. YTP.stop();
  174. for (i = 0; i < ATTRS.length; i++) {
  175. document.getElementsByTagName("html")[0].removeAttribute(ATTRS[i]);
  176. }
  177. window.removeEventListener("yt-page-date-updated", tmp);
  178. });
  179.  
  180. YTP.start();
  181.  
  182. YTP.setCfgMulti(CONFIGS);
  183. YTP.setExpMulti(EXPFLAGS);
  184. YTP.setPlyrFlags(PLYRFLAGS);
  185.  
  186. function $(q) {
  187. return document.querySelector(q);
  188. }
  189.  
  190. // Fix for Return YouTube Dislike including action buttons
  191. const abtnconfig = {
  192. unsegmentLikeButton: false,
  193. noFlexibleItems: true
  194. };
  195.  
  196. function updateBtns() {
  197. var watchFlexy = document.querySelector("ytd-watch-flexy");
  198. var results = watchFlexy.data.contents.twoColumnWatchNextResults.results.results.contents;
  199.  
  200. for (var i = 0; i < results.length; i++) {
  201. if (results[i].videoPrimaryInfoRenderer) {
  202. var actions = results[i].videoPrimaryInfoRenderer.videoActions.menuRenderer;
  203.  
  204. if (abtnconfig.unsegmentLikeButton) {
  205. if (actions.topLevelButtons[0].segmentedLikeDislikeButtonRenderer) {
  206. var segmented = actions.topLevelButtons[0].segmentedLikeDislikeButtonRenderer;
  207. actions.topLevelButtons.splice(0, 1);
  208. actions.topLevelButtons.unshift(segmented.dislikeButton);
  209. actions.topLevelButtons.unshift(segmented.likeButton);
  210. }
  211. }
  212.  
  213. if (abtnconfig.noFlexibleItems) {
  214. for (var i = 0; i < actions.flexibleItems.length; i++) {
  215. actions.topLevelButtons.push(actions.flexibleItems[i].menuFlexibleItemRenderer.topLevelButton);
  216. }
  217.  
  218. delete actions.flexibleItems
  219. }
  220. }
  221. }
  222.  
  223. var temp = watchFlexy.data;
  224. watchFlexy.data = {};
  225. watchFlexy.data = temp;
  226. }
  227.  
  228. document.addEventListener("yt-page-data-updated", (e) => {
  229. if (e.detail.pageType == "watch") {
  230. updateBtns();
  231. }
  232. });
  233.  
  234. addEventListener('yt-page-data-updated', function() {
  235. if(!location.pathname.startsWith('/watch')) return;
  236.  
  237. var lds = $('ytd-video-primary-info-renderer div#top-level-buttons-computed');
  238. var like = $('ytd-video-primary-info-renderer div#segmented-like-button > ytd-toggle-button-renderer');
  239. var share = $('ytd-video-primary-info-renderer div#top-level-buttons-computed > ytd-segmented-like-dislike-button-renderer + ytd-button-renderer');
  240.  
  241. lds.insertBefore(like, share);
  242.  
  243. like.setAttribute('class', like.getAttribute('class').replace('ytd-segmented-like-dislike-button-renderer', 'ytd-menu-renderer force-icon-button'));
  244. like.removeAttribute('is-paper-button-with-icon');
  245. like.removeAttribute('is-paper-button');
  246. like.setAttribute('style-action-button', '');
  247. like.setAttribute('is-icon-button', '');
  248. like.querySelector('a').insertBefore(like.querySelector('yt-formatted-string'), like.querySelector('tp-yt-paper-tooltip'));
  249. try { like.querySelector('paper-ripple').remove(); } catch(e) {}
  250. var paper = like.querySelector('tp-yt-paper-button');
  251. paper.removeAttribute('style-target');
  252. paper.removeAttribute('animated');
  253. paper.removeAttribute('elevation');
  254. like.querySelector('a').insertBefore(paper.querySelector('yt-icon'), like.querySelector('yt-formatted-string'));
  255. paper.outerHTML = paper.outerHTML.replace('<tp-yt-paper-button ', '<yt-icon-button ').replace('</tp-yt-paper-button>', '</yt-icon-button>');
  256. paper = like.querySelector('yt-icon-button');
  257. paper.querySelector('button#button').appendChild(like.querySelector('yt-icon'));
  258.  
  259. var dislike = $('ytd-video-primary-info-renderer div#segmented-dislike-button > ytd-toggle-button-renderer');
  260. lds.insertBefore(dislike, share);
  261. $('ytd-video-primary-info-renderer ytd-segmented-like-dislike-button-renderer').remove();
  262. dislike.setAttribute('class', dislike.getAttribute('class').replace('ytd-segmented-like-dislike-button-renderer', 'ytd-menu-renderer force-icon-button'));
  263. dislike.removeAttribute('has-no-text');
  264. dislike.setAttribute('style-action-button', '');
  265. var dlabel = document.createElement('yt-formatted-stringx');
  266. dlabel.setAttribute('id', 'text');
  267. if(dislike.getAttribute('class').includes('style-default-active'))
  268. dlabel.setAttribute('class', dlabel.getAttribute('class').replace('style-default', 'style-default-active'));
  269. dislike.querySelector('a').insertBefore(dlabel, dislike.querySelector('tp-yt-paper-tooltip'));
  270.  
  271. $('ytd-video-primary-info-renderer').removeAttribute('flex-menu-enabled');
  272. });
  273.  
  274. // Revert old Comments UI
  275. var observingComments = false;
  276. var hl;
  277.  
  278. const cfconfig = {
  279. unicodeEmojis: true
  280. };
  281.  
  282. const cfi18n = {
  283. en: {
  284. viewSingular: "View reply",
  285. viewMulti: "View %s replies",
  286. viewSingularOwner: "View reply from %s",
  287. viewMultiOwner: "View %s replies from %s and others",
  288. hideSingular: "Hide reply",
  289. hideMulti: "Hide replies",
  290. replyCountIsolator: /( REPLIES)|( REPLY)/
  291. }
  292. }
  293.  
  294. /**
  295. * Get a string from the localization strings.
  296. *
  297. * @param {string} string Name of string to get
  298. * @param {string} hl Language to use.
  299. * @param {...array} args Strings.
  300. * @returns {string}
  301. */
  302. function getString(string, hl = "en", ...args) {
  303. if (!string) return;
  304. var str;
  305. if (cfi18n[hl]) {
  306. if (cfi18n[hl][string]) {
  307. str = cfi18n[hl][string];
  308. } else if (cfi18n.en[string]) {
  309. str = cfi18n.en[string];
  310. } else {
  311. return;
  312. }
  313. } else {
  314. if (cfi18n.en[string]) str = cfi18n.en[string];
  315. }
  316.  
  317. for (var i = 0; i < args.length; i++) {
  318. str = str.replace(/%s/, args[i]);
  319. }
  320.  
  321. return str;
  322. }
  323.  
  324. /**
  325. * Wait for a selector to exist
  326. *
  327. * @param {string} selector CSS Selector
  328. * @param {HTMLElement} base Element to search inside
  329. * @returns {Node}
  330. */
  331. async function waitForElm(selector, base = document) {
  332. if (!selector) return null;
  333. if (!base.querySelector) return null;
  334. while (base.querySelector(selector) == null) {
  335. await new Promise(r => requestAnimationFrame(r));
  336. };
  337. return base.querySelector(selector);
  338. };
  339.  
  340. /**
  341. * Is a value in an array?
  342. *
  343. * @param {*} needle Value to search
  344. * @param {Array} haystack Array to search
  345. * @returns {boolean}
  346. */
  347. function inArray(needle, haystack) {
  348. for (var i = 0; i < haystack.length; i++) {
  349. if (needle == haystack[i]) return true;
  350. }
  351. return false;
  352. }
  353.  
  354. /**
  355. * Get text of an InnerTube string.
  356. *
  357. * @param {object} object String container.
  358. */
  359. function getSimpleString(object) {
  360. if (object.simpleText) return object.simpleText;
  361.  
  362. var str = "";
  363. for (var i = 0; i < object.runs.length; i++) {
  364. str += object.runs[i].text;
  365. }
  366. return str;
  367. }
  368.  
  369. /**
  370. * Format a commentRenderer.
  371. *
  372. * @param {object} comment commentRenderer from InnerTube.
  373. */
  374. function formatComment(comment) {
  375. if (cfconfig.unicodeEmojis) {
  376. var runs;
  377. try {
  378. runs = comment.contentText.runs
  379. for (var i = 0; i < runs.length; i++) {
  380. delete runs[i].emoji;
  381. delete runs[i].loggingDirectives;
  382. }
  383. } catch(err) {}
  384. }
  385.  
  386. return comment;
  387. }
  388.  
  389. /**
  390. * Format a commentThreadRenderer.
  391. *
  392. * @param {object} thread commentThreadRenderer from InnerTube.
  393. */
  394. async function formatCommentThread(thread) {
  395. if (thread.comment.commentRenderer) {
  396. thread.comment.commentRenderer = formatComment(thread.comment.commentRenderer);
  397. }
  398.  
  399. var replies;
  400. try {
  401. replies = thread.replies.commentRepliesRenderer;
  402. if (replies.viewRepliesIcon) {
  403. replies.viewReplies.buttonRenderer.icon = replies.viewRepliesIcon.buttonRenderer.icon;
  404. delete replies.viewRepliesIcon;
  405. }
  406.  
  407. if (replies.hideRepliesIcon) {
  408. replies.hideReplies.buttonRenderer.icon = replies.hideRepliesIcon.buttonRenderer.icon;
  409. delete replies.hideRepliesIcon;
  410. }
  411.  
  412. var creatorName;
  413. try {
  414. creatorName = replies.viewRepliesCreatorThumbnail.accessibility.accessibilityData.label;
  415. delete replies.viewRepliesCreatorThumbnail;
  416. } catch(err) {}
  417.  
  418. var replyCount = getSimpleString(replies.viewReplies.buttonRenderer.text);
  419. replyCount = +replyCount.replace(getString("replyCountIsolator", hl), "");
  420.  
  421. var viewMultiString = creatorName ? "viewMultiOwner" : "viewMulti";
  422. var viewSingleString = creatorName ? "viewSingularOwner" : "viewSingular";
  423.  
  424. replies.viewReplies.buttonRenderer.text = {
  425. runs: [
  426. {
  427. text: (replyCount > 1) ? getString(viewMultiString, hl, replyCount, creatorName) : getString(viewSingleString, hl, creatorName)
  428. }
  429. ]
  430. }
  431.  
  432. replies.hideReplies.buttonRenderer.text = {
  433. runs: [
  434. {
  435. text: (replyCount > 1) ? getString("hideMulti", hl) : getString("hideSingular", hl)
  436. }
  437. ]
  438. };
  439. } catch(err) {}
  440.  
  441. return thread;
  442. }
  443.  
  444. /**
  445. * Force Polymer to refresh data of an element.
  446. *
  447. * @param {Node} element Element to refresh data of.
  448. */
  449. function refreshData(element) {
  450. var clone = element.cloneNode();
  451. clone.data = element.data;
  452. // Let the script know we left our mark
  453. // in a way that doesn't rely on classes
  454. // because Polymer likes to cast comments
  455. // into the fucking void for later reuse
  456. clone.data.fixedByCF = true;
  457. for (var i in element.properties) {
  458. clone[i] = element[i];
  459. }
  460. element.insertAdjacentElement("afterend", clone);
  461. element.remove();
  462. }
  463.  
  464. var commentObserver = new MutationObserver((list) => {
  465. list.forEach(async (mutation) => {
  466. if (mutation.addedNodes) {
  467. for (var i = 0; i < mutation.addedNodes.length; i++) {
  468. var elm = mutation.addedNodes[i];
  469. if (elm.classList && elm.data && !elm.data.fixedByCF) {
  470. if (elm.tagName == "YTD-COMMENT-THREAD-RENDERER") {
  471. elm.data = await formatCommentThread(elm.data);
  472. refreshData(elm);
  473. } else if (elm.tagName == "YTD-COMMENT-RENDERER") {
  474. if (!elm.classList.contains("ytd-comment-thread-renderer")) {
  475. elm.data = formatComment(elm.data);
  476. refreshData(elm);
  477. }
  478. }
  479. }
  480. }
  481. }
  482. });
  483. });
  484.  
  485. document.addEventListener("yt-page-data-updated", async (e) => {
  486. hl = yt.config_.HL;
  487. commentObserver.observe(document.querySelector("ytd-app"), { childList: true, subtree: true });
  488. });
  489.  
  490. // CSS adjustments and UI fixes
  491. (function() {
  492. ApplyCSS();
  493. function ApplyCSS() {
  494. var styles = document.createElement("style");
  495. styles.innerHTML=`
  496. #cinematics.ytd-watch-flexy {
  497. display: none;
  498. }
  499.  
  500. div#clarify-box.attached-message.style-scope.ytd-watch-flexy {
  501. margin-top: 0px;
  502. }
  503.  
  504. ytd-clarification-renderer.style-scope.ytd-item-section-renderer {
  505. border: 1px solid;
  506. border-color: #0000001a;
  507. border-radius: 0px;
  508. }
  509.  
  510. ytd-clarification-renderer.style-scope.ytd-watch-flexy {
  511. border: 1px solid;
  512. border-color: #0000001a;
  513. border-radius: 0px;
  514. }
  515.  
  516. yt-formatted-string.description.style-scope.ytd-clarification-renderer {
  517. font-size: 1.4rem;
  518. }
  519.  
  520. div.content-title.style-scope.ytd-clarification-renderer {
  521. padding-bottom: 4px;
  522. }
  523.  
  524. div.ytp-sb-subscribe.ytp-sb-rounded, .ytp-sb-unsubscribe.ytp-sb-rounded {
  525. border-radius: 2px;
  526. }
  527.  
  528. .yt-spec-button-shape-next--size-m {
  529. background-color: transparent;
  530. padding-right: 6px;
  531. }
  532.  
  533. .yt-spec-button-shape-next--mono.yt-spec-button-shape-next--tonal {
  534. background-color: transparent;
  535. }
  536.  
  537. div.cbox.yt-spec-button-shape-next--button-text-content {
  538. display: none;
  539. }
  540.  
  541. div.yt-spec-button-shape-next__secondary-icon {
  542. display: none;
  543. }
  544.  
  545. div#ytp-id-18.ytp-popup,ytp-settings-menu.ytp-rounded-menu {
  546. border-radius: 2px;
  547. }
  548.  
  549. div.branding-context-container-inner.ytp-rounded-branding-context {
  550. border-radius: 2px;
  551. }
  552.  
  553. div.iv-card.iv-card-video.ytp-rounded-info {
  554. border-radius: 0px;
  555. }
  556.  
  557. div.iv-card.iv-card-playlist.ytp-rounded-info {
  558. border-radius: 0px;
  559. }
  560.  
  561. div.iv-card.iv-card-channel.ytp-rounded-info {
  562. border-radius: 0px;
  563. }
  564.  
  565. div.iv-card.ytp-rounded-info {
  566. border-radius: 0px;
  567. }
  568.  
  569. .ytp-tooltip.ytp-rounded-tooltip.ytp-text-detail.ytp-preview, .ytp-tooltip.ytp-rounded-tooltip.ytp-text-detail.ytp-preview .ytp-tooltip-bg {
  570. border-radius: 0px;
  571. }
  572.  
  573. .ytp-ce-video.ytp-ce-medium-round, .ytp-ce-playlist.ytp-ce-medium-round, .ytp-ce-medium-round .ytp-ce-expanding-overlay-background {
  574. border-radius: 0px;
  575. }
  576.  
  577. div.ytp-autonav-endscreen-upnext-thumbnail.rounded-thumbnail {
  578. border-radius: 0px;
  579. }
  580.  
  581. button.ytp-autonav-endscreen-upnext-button.ytp-autonav-endscreen-upnext-cancel-button.ytp-autonav-endscreen-upnext-button-rounded {
  582. border-radius: 2px;
  583. }
  584.  
  585. a.ytp-autonav-endscreen-upnext-button.ytp-autonav-endscreen-upnext-play-button.ytp-autonav-endscreen-upnext-button-rounded {
  586. border-radius: 2px;
  587. }
  588.  
  589. .ytp-videowall-still-image {
  590. border-radius: 0px;
  591. }`
  592. document.head.appendChild(styles);
  593. }
  594. })();

QingJ © 2025

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