// ==UserScript==
// @name YouTube - Force rounded corners + tweaks included
// @version 2025.02.10
// @description This script forces the rounded version of the layout (which includes some fewer tweaks applied which also improves bugs).
// @author Joey_JTS (original author: Magma_Craft)
// @license MIT
// @match *://www.youtube.com/*
// @namespace https://greasyfork.org/en/users/933798
// @icon https://www.youtube.com/favicon.ico
// @run-at document-start
// @grant none
// ==/UserScript==
// Attributes to remove from <html>
const ATTRS = [
"darker-dark-theme",
"darker-dark-theme-deprecate"
];
// Regular config keys.
const CONFIGS = {
BUTTON_REWORK: true
}
// Experiment flags.
const EXPFLAGS = {
/* Force rounded corners */
web_button_rework: true,
web_button_rework_with_live: true,
web_darker_dark_theme: true,
web_filled_subscribed_button: true,
web_guide_ui_refresh: true,
web_modern_ads: true,
web_modern_buttons: true,
web_modern_chips: true,
web_modern_dialogs: true,
web_modern_playlists: true,
web_modern_subscribe: true,
web_rounded_containers: true,
web_rounded_thumbnails: true,
web_searchbar_style: "rounded_corner_borders_light_btn",
web_segmented_like_dislike_button: true,
web_sheets_ui_refresh: true,
web_snackbar_ui_refresh: true,
/* Force rounded watch layout and few tweaks to be included (such as disabling the useless 'watch grid' UI */
kevlar_watch_metadata_refresh: true,
kevlar_watch_metadata_refresh_attached_subscribe: true,
kevlar_watch_metadata_refresh_clickable_description: true,
kevlar_watch_metadata_refresh_compact_view_count: true,
kevlar_watch_metadata_refresh_description_info_dedicated_line: true,
kevlar_watch_metadata_refresh_description_inline_expander: true,
kevlar_watch_metadata_refresh_description_primary_color: true,
kevlar_watch_metadata_refresh_for_live_killswitch: true,
kevlar_watch_metadata_refresh_full_width_description: true,
kevlar_watch_metadata_refresh_narrower_item_wrap: true,
kevlar_watch_metadata_refresh_relative_date: true,
kevlar_watch_metadata_refresh_top_aligned_actions: true,
kevlar_watch_modern_metapanel: true,
kevlar_watch_modern_panels: true,
kevlar_watch_panel_height_matches_player: true,
kevlar_watch_grid: false,
kevlar_watch_grid_hide_chips: false,
small_avatars_for_comments: false,
small_avatars_for_comments_ep: false,
web_watch_compact_comments: false,
web_watch_compact_comments_ep: false,
web_watch_theater_chat: false,
web_watch_theater_fixed_chat: false,
live_chat_over_engagement_panels: false,
live_chat_scaled_height: false,
live_chat_smaller_min_height: false,
wn_grid_max_item_width: 0,
wn_grid_min_item_width: 0,
kevlar_set_internal_player_size: false,
kevlar_watch_flexy_metadata_height: "136",
kevlar_watch_max_player_width: "1280",
web_watch_rounded_player_large: false,
kevlar_watch_cinematics: false,
desktop_delay_player_resizing: false,
/* Additional tweaks (which includes reverting new UI changes and disabling animations except for both web_modern_tabs and web_enable_youtab configs ) */
kevlar_refresh_on_theme_change: false,
smartimation_background: false,
web_animated_actions: false,
web_animated_like: false,
web_animated_like_lazy_load: false,
enable_channel_page_header_profile_section: false,
kevlar_modern_sd_v2: false,
web_modern_collections_v2: false,
web_modern_tabs: false,
web_modern_typography: true,
web_enable_youtab: true
}
// Player flags
// !!! USE STRINGS FOR VALUES !!!
// For example: "true" instead of true
const PLYRFLAGS = {
web_rounded_containers: "true",
web_rounded_thumbnails: "true"
}
class YTP {
static observer = new MutationObserver(this.onNewScript);
static _config = {};
static isObject(item) {
return (item && typeof item === "object" && !Array.isArray(item));
}
static mergeDeep(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (this.isObject(target) && this.isObject(source)) {
for (const key in source) {
if (this.isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
this.mergeDeep(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return this.mergeDeep(target, ...sources);
}
static onNewScript(mutations) {
for (var mut of mutations) {
for (var node of mut.addedNodes) {
YTP.bruteforce();
}
}
}
static start() {
this.observer.observe(document, {childList: true, subtree: true});
}
static stop() {
this.observer.disconnect();
}
static bruteforce() {
if (!window.yt) return;
if (!window.yt.config_) return;
this.mergeDeep(window.yt.config_, this._config);
}
static setCfg(name, value) {
this._config[name] = value;
}
static setCfgMulti(configs) {
this.mergeDeep(this._config, configs);
}
static setExp(name, value) {
if (!("EXPERIMENT_FLAGS" in this._config)) this._config.EXPERIMENT_FLAGS = {};
this._config.EXPERIMENT_FLAGS[name] = value;
}
static setExpMulti(exps) {
if (!("EXPERIMENT_FLAGS" in this._config)) this._config.EXPERIMENT_FLAGS = {};
this.mergeDeep(this._config.EXPERIMENT_FLAGS, exps);
}
static decodePlyrFlags(flags) {
var obj = {},
dflags = flags.split("&");
for (var i = 0; i < dflags.length; i++) {
var dflag = dflags[i].split("=");
obj[dflag[0]] = dflag[1];
}
return obj;
}
static encodePlyrFlags(flags) {
var keys = Object.keys(flags),
response = "";
for (var i = 0; i < keys.length; i++) {
if (i > 0) {
response += "&";
}
response += keys[i] + "=" + flags[keys[i]];
}
return response;
}
static setPlyrFlags(flags) {
if (!window.yt) return;
if (!window.yt.config_) return;
if (!window.yt.config_.WEB_PLAYER_CONTEXT_CONFIGS) return;
var conCfgs = window.yt.config_.WEB_PLAYER_CONTEXT_CONFIGS;
if (!("WEB_PLAYER_CONTEXT_CONFIGS" in this._config)) this._config.WEB_PLAYER_CONTEXT_CONFIGS = {};
for (var cfg in conCfgs) {
var dflags = this.decodePlyrFlags(conCfgs[cfg].serializedExperimentFlags);
this.mergeDeep(dflags, flags);
this._config.WEB_PLAYER_CONTEXT_CONFIGS[cfg] = {
serializedExperimentFlags: this.encodePlyrFlags(dflags)
}
}
}
}
window.addEventListener("yt-page-data-updated", function tmp() {
YTP.stop();
for (i = 0; i < ATTRS.length; i++) {
document.getElementsByTagName("html")[0].removeAttribute(ATTRS[i]);
}
window.removeEventListener("yt-page-date-updated", tmp);
});
YTP.start();
YTP.setCfgMulti(CONFIGS);
YTP.setExpMulti(EXPFLAGS);
YTP.setPlyrFlags(PLYRFLAGS);
function $(q) {
return document.querySelector(q);
}
(function() {
let css = `
/* Add rounded corners under the player */
div#ytp-id-17.ytp-popup.ytp-settings-menu,
div#ytp-id-18.ytp-popup.ytp-settings-menu {
border-radius: 12px !important
}
div.branding-context-container-inner.ytp-rounded-branding-context {
border-radius: 8px !important
}
.iv-card {
border-radius: 8px !important
}
.ytp-ad-overlay-container.ytp-overlay-ad .ytp-ad-overlay-image img, .ytp-ad-overlay-container.ytp-overlay-ad .ytp-ad-text-overlay, .ytp-ad-overlay-container.ytp-overlay-ad .ytp-ad-enhanced-overlay {
border-radius: 8px !important
}
.ytp-tooltip.ytp-text-detail.ytp-preview .ytp-tooltip-bg {
border-top-left-radius: 12px !important;
border-bottom-left-radius: 12px !important
}
.ytp-tooltip.ytp-text-detail.ytp-preview {
border-radius: 12px !important
}
.ytp-ce-video.ytp-ce-medium, .ytp-ce-playlist.ytp-ce-medium, .ytp-ce-medium .ytp-ce-expanding-overlay-background {
border-radius: 8px !important
}
.ytp-autonav-endscreen-upnext-thumbnail {
border-radius: 8px !important
}
.ytp-autonav-endscreen-upnext-button {
border-radius: 18px !important
}
.ytp-videowall-still-image {
border-radius: 8px !important
}
.ytp-sb-subscribe, .ytp-sb-unsubscribe {
border-radius: 18px !important
}
/* Watch page tweaks (including the 'Revert video list' CSS) */
ytd-watch-flexy[rounded-player-large]:not([fullscreen]):not([theater]) #ytd-player.ytd-watch-flexy {
border-radius: 0px !important
}
#actions.ytd-watch-metadata {
min-width: auto !important
}
ytd-watch-flexy[default-layout][reduced-top-margin] #primary.ytd-watch-flexy, ytd-watch-flexy[default-layout][reduced-top-margin] #secondary.ytd-watch-flexy {
padding-top: var(--ytd-margin-6x) !important
}
ytd-watch-metadata[title-headline-xs] h1.ytd-watch-metadata, ytd-watch-metadata[title-headline-m] h1.ytd-watch-metadata {
font-family: "YouTube Sans","Roboto",sans-serif !important;
font-weight: 600 !important;
font-size: 2rem !important;
line-height: 2.8rem !important
}
ytd-comments-header-renderer[compact-header] #title.ytd-comments-header-renderer {
margin-bottom: 24px !important
}
ytd-comments-header-renderer[modern-typography][compact-header] .count-text.ytd-comments-header-renderer {
font-size: 2rem !important;
line-height: 2.8rem !important;
font-weight: 700 !important;
max-height: 2.8rem !important;
display: flex !important;
flex-direction: row-reverse !important
}
[compact-header] .count-text.ytd-comments-header-renderer {
display: flex !important;
flex-direction: row-reverse !important
}
[compact-header] .count-text.ytd-comments-header-renderer span {
margin-right: 6px !important
}
ytd-watch-flexy #comment-teaser.ytd-watch-metadata {
display: none
}
ytd-watch-flexy ytd-rich-item-renderer[rendered-from-rich-grid] {
--ytd-rich-item-row-usable-width: 100% !important
}
ytd-watch-flexy ytd-rich-item-renderer[rendered-from-rich-grid][is-in-first-column] {
margin-left: 0
}
ytd-watch-flexy ytd-rich-item-renderer ytd-menu-renderer .ytd-menu-renderer[style-target=button] {
width: 24px !important;
height: 24px !important
}
ytd-watch-flexy #dismissible.ytd-rich-grid-media {
flex-direction: row
}
ytd-watch-flexy #attached-survey.ytd-rich-grid-media,
ytd-watch-flexy #avatar-link.ytd-rich-grid-media,
ytd-watch-flexy #avatar-container.ytd-rich-grid-media {
display: none
}
ytd-watch-flexy ytd-thumbnail.ytd-rich-grid-media,
ytd-watch-flexy ytd-playlist-thumbnail.ytd-rich-grid-media {
margin-right: 8px;
height: 94px;
width: 168px
}
ytd-watch-flexy ytd-thumbnail[size=large] a.ytd-thumbnail, ytd-watch-flexy ytd-thumbnail[size=large]:before,
ytd-watch-flexy ytd-thumbnail[size=large][large-margin] a.ytd-thumbnail, ytd-watch-flexy ytd-thumbnail[size=large][large-margin]:before {
border-radius: 8px
}
ytd-watch-flexy ytd-thumbnail[size=large][large-margin] ytd-thumbnail-overlay-time-status-renderer.ytd-thumbnail, ytd-watch-flexy ytd-thumbnail[size=large][large-margin] ytd-thumbnail-overlay-button-renderer.ytd-thumbnail, ytd-watch-flexy ytd-thumbnail[size=large][large-margin] ytd-thumbnail-overlay-toggle-button-renderer.ytd-thumbnail,
ytd-watch-flexy ytd-thumbnail[size=large] ytd-thumbnail-overlay-time-status-renderer.ytd-thumbnail, ytd-watch-flexy ytd-thumbnail[size=large] ytd-thumbnail-overlay-button-renderer.ytd-thumbnail, ytd-watch-flexy ytd-thumbnail[size=large] ytd-thumbnail-overlay-toggle-button-renderer.ytd-thumbnail {
margin: 4px
}
ytd-watch-flexy ytd-rich-item-renderer,
ytd-watch-flexy ytd-rich-grid-row #contents.ytd-rich-grid-row {
margin: 0
}
ytd-watch-flexy ytd-rich-item-renderer[reduced-bottom-margin] {
margin-top: 8px;
margin-bottom: 0
}
ytd-watch-flexy ytd-rich-grid-renderer[reduced-top-margin] #contents.ytd-rich-grid-renderer {
padding-top: 0px
}
ytd-watch-flexy ytd-rich-grid-media {
margin-bottom: 8px
}
ytd-watch-flexy #details.ytd-rich-grid-media {
width: 100%;
min-width: 0
}
ytd-watch-flexy ytd-video-meta-block[rich-meta] #metadata-line.ytd-video-meta-block,
ytd-watch-flexy #channel-name.ytd-video-meta-block {
font-family: "Roboto", "Arial", sans-serif;
font-size: 1.2rem;
line-height: 1.8rem;
font-weight: 400
}
ytd-watch-flexy #video-title.ytd-rich-grid-media {
margin: 0 0 4px 0;
display: block;
font-family: "Roboto", "Arial", sans-serif;
font-size: 1.4rem;
line-height: 2rem;
font-weight: 500;
overflow: hidden;
display: block;
max-height: 4rem;
-webkit-line-clamp: 2;
display: box;
display: -webkit-box;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
white-space: normal
}
ytd-watch-flexy h3.ytd-rich-grid-media {
margin: 0
}
ytd-watch-flexy .title-badge.ytd-rich-grid-media, ytd-watch-flexy .video-badge.ytd-rich-grid-media {
margin-top: 0
}
ytd-watch-flexy ytd-rich-section-renderer.style-scope.ytd-rich-grid-renderer {
display: none
}
ytd-watch-flexy ytd-rich-grid-renderer[hide-chips-bar] ytd-feed-filter-chip-bar-renderer.ytd-rich-grid-renderer, ytd-watch-flexy ytd-rich-grid-renderer[hide-chips-bar-on-watch] ytd-feed-filter-chip-bar-renderer.ytd-rich-grid-renderer, ytd-watch-flexy ytd-rich-grid-renderer[hide-chips-bar-on-home] #header.ytd-rich-grid-renderer ytd-feed-filter-chip-bar-renderer.ytd-rich-grid-renderer {
display: flex;
height: 51px;
margin-bottom: 8px
}
ytd-watch-flexy #chips-wrapper.ytd-feed-filter-chip-bar-renderer {
position: relative;
top: 0
}
ytd-watch-flexy ytd-feed-filter-chip-bar-renderer[fluid-width] #chips-content.ytd-feed-filter-chip-bar-renderer {
padding: 0
}
ytd-watch-flexy yt-chip-cloud-chip-renderer.ytd-feed-filter-chip-bar-renderer, ytd-watch-flexy yt-chip-cloud-chip-renderer.ytd-feed-filter-chip-bar-renderer:first-of-type {
margin: 8px;
margin-left: 0
}
ytd-watch-flexy ytd-button-renderer.ytd-feed-filter-chip-bar-renderer {
margin: 0;
padding: 0 8px
}
/* More tweaks to be applied */
#buttons.ytd-c4-tabbed-header-renderer {
flex-direction: row-reverse !important
}
ytd-channel-tagline-renderer {
display: block !important;
padding: 0 !important
}
#content.ytd-channel-tagline-renderer::before {
content: "More about this channel";
font-weight: 500 !important
}
#content.ytd-channel-tagline-renderer {
max-width: 162px !important
}
ytd-browse[page-subtype="channels"] .page-header-view-model-wiz__page-header-description {
margin-top: 0px !important;
max-width: 236px !important
}
ytd-browse[page-subtype="channels"] yt-description-preview-view-model .truncated-text-wiz__truncated-text-content:before {
content: "More about this channel > ";
font-weight: 500 !important
}
ytd-browse[page-subtype="channels"] button.truncated-text-wiz__absolute-button {
display: none !important
}
#avatar.ytd-c4-tabbed-header-renderer, .yt-spec-avatar-shape__button--button-giant {
width: 80px !important;
height: 80px !important;
margin: 0 24px 0 0 !important;
flex: none !important;
overflow: hidden !important
}
.yt-spec-avatar-shape__button--button-giant, .yt-spec-avatar-shape--avatar-size-giant, .yt-spec-avatar-shape__button--button-extra-extra-large, .yt-spec-avatar-shape--avatar-size-extra-extra-large {
width: 80px !important;
height: 80px !important;
margin-right: 0px !important;
}
#avatar-editor.ytd-c4-tabbed-header-renderer {
--ytd-channel-avatar-editor-size: 80px !important
}
#channel-name.ytd-c4-tabbed-header-renderer {
margin-bottom: 0 !important
}
#channel-header-container.ytd-c4-tabbed-header-renderer {
padding-top: 0 !important;
align-items: center !important
}
#inner-header-container.ytd-c4-tabbed-header-renderer {
margin-top: 0 !important;
align-items: center !important
}
.yt-content-metadata-view-model-wiz--inline .yt-content-metadata-view-model-wiz__metadata-row {
margin-top: 0 !important
}
yt-formatted-string#channel-pronouns.style-scope.ytd-c4-tabbed-header-renderer, #videos-count {
display: none !important
}
.meta-item.ytd-c4-tabbed-header-renderer {
display: block !important
}
div#channel-header-links.style-scope.ytd-c4-tabbed-header-renderer,
.page-header-view-model-wiz__page-header-attribution {
display: none !important
}
ytd-c4-tabbed-header-renderer[use-page-header-style] #channel-name.ytd-c4-tabbed-header-renderer,
[page-subtype="channels"] .page-header-view-model-wiz__page-header-title--page-header-title-large {
font-size: 2.4em !important;
font-weight: 400 !important;
line-height: var(--yt-channel-title-line-height, 3rem) !important;
margin: 0 !important
}
span.delimiter.style-scope.ytd-c4-tabbed-header-renderer, .yt-content-metadata-view-model-wiz__delimiter {
display: none !important
}
div#meta.style-scope.ytd-c4-tabbed-header-renderer {
width: auto !important
}
ytd-c4-tabbed-header-renderer[use-page-header-style] #inner-header-container.ytd-c4-tabbed-header-renderer {
flex-direction: row !important
}
div.page-header-banner.style-scope.ytd-c4-tabbed-header-renderer {
margin-left: 0px !important;
margin-right: 8px !important;
border-radius: 0px !important
}
[has-inset-banner] #page-header-banner.ytd-tabbed-page-header {
padding-left: 0 !important;
padding-right: 0 !important
}
ytd-c4-tabbed-header-renderer[use-page-header-style] .page-header-banner.ytd-c4-tabbed-header-renderer,
.yt-image-banner-view-model-wiz--inset {
border-radius: 0px !important
}
.yt-content-metadata-view-model-wiz__metadata-text {
margin-right: 8px !important
}
.yt-content-metadata-view-model-wiz__metadata-text, .truncated-text-wiz, .truncated-text-wiz__absolute-button {
font-size: 1.4rem !important
}
.yt-tab-shape-wiz {
padding: 0 32px !important;
margin-right: 0 !important
}
.yt-tab-shape-wiz__tab {
font-size: 14px !important;
font-weight: 500 !important;
letter-spacing: var(--ytd-tab-system-letter-spacing) !important;
text-transform: uppercase !important
}
.yt-tab-group-shape-wiz__slider {
display: none !important
}
ytd-browse[page-subtype="channels"] ytd-tabbed-page-header .yt-content-metadata-view-model-wiz__metadata-row--metadata-row-inline {
display: flex
}
ytd-browse[page-subtype="channels"] ytd-tabbed-page-header .yt-content-metadata-view-model-wiz__metadata-text:last-of-type {
display: none
}
ytd-browse[page-subtype="channels"] ytd-tabbed-page-header .yt-content-metadata-view-model-wiz__metadata-text:first-of-type {
display: flex
}
ytd-browse[page-subtype="channels"] .yt-flexible-actions-view-model-wiz--inline {
flex-direction: row-reverse
}
ytd-browse[page-subtype="channels"] .page-header-view-model-wiz__page-header-flexible-actions {
margin-top: -56px
}
ytd-browse[page-subtype="channels"] .yt-flexible-actions-view-model-wiz__action-row {
margin-top: 60px
}
ytd-browse[page-subtype="channels"] .yt-flexible-actions-view-model-wiz__action {
padding-right: 8px
}
ytd-browse[page-subtype="channels"] span.yt-core-attributed-string--link-inherit-color {
font-weight: 400 !important
}
ytd-browse[page-subtype="channels"] .page-header-view-model-wiz__page-header-headline-info {
margin-bottom: 8px
}
#title.ytd-playlist-sidebar-primary-info-renderer,
ytd-inline-form-renderer[component-style=INLINE_FORM_STYLE_TITLE] #text-displayed.ytd-inline-form-renderer {
font-family: YouTube Sans !important;
font-weight: 700 !important
}
ytd-comments-header-renderer[use-space-between] #title.ytd-comments-header-renderer {
justify-content: start !important
}
#panel-button.ytd-comments-header-renderer {
margin-left: 32px;
margin-right: 8px
}
#panel-button .yt-spec-button-shape-next__icon {
margin-right: 0
}
#panel-button .yt-spec-button-shape-next--size-m {
padding-left: 12px;
padding-right: 6px
}
#panel-button .yt-spec-button-shape-next__button-text-content {
display: none !important
}
#panel-button .yt-spec-button-shape-next__icon path {
d: path("M10 3H17V7H10V3ZM20 0H0V14H20V0ZM1 1H19V13H1V1Z");
transform: scale(1.20)
}
div#end.style-scope.ytd-masthead .yt-spec-button-shape-next--size-m[aria-label="Create"] {
height: 40px !important;
border-radius: 50px !important;
color: var(--yt-spec-icon-active-other) !important;
background-color: transparent !important
}
div#end.style-scope.ytd-masthead .yt-spec-button-shape-next--size-m.yt-spec-button-shape-next--icon-leading[aria-label="Create"] .yt-spec-button-shape-next__button-text-content {
display: none !important
}
div#end.style-scope.ytd-masthead .yt-spec-button-shape-next--size-m.yt-spec-button-shape-next--icon-leading[aria-label="Create"] .yt-spec-button-shape-next__icon {
margin-left: -8px !important;
margin-right: -8px !important
}
div#end.style-scope.ytd-masthead .yt-spec-button-shape-next--size-m.yt-spec-button-shape-next--icon-leading[aria-label="Create"] path {
d: path("M14 13h-3v3H9v-3H6v-2h3V8h2v3h3v2zm3-7H3v12h14v-6.39l4 1.83V8.56l-4 1.83V6m1-1v3.83L22 7v8l-4-1.83V19H2V5h16z")
}
div#end.style-scope.ytd-masthead .yt-spec-icon-badge-shape--style-overlay.yt-spec-icon-badge-shape--type-cart-refresh .yt-spec-icon-badge-shape__badge {
color: #fff !important
}
ytd-feed-filter-chip-bar-renderer[frosted-glass] ytd-button-renderer.ytd-feed-filter-chip-bar-renderer {
background-color: transparent !important
}
ytd-feed-filter-chip-bar-renderer[frosted-glass] #left-arrow-button.ytd-feed-filter-chip-bar-renderer,
ytd-feed-filter-chip-bar-renderer[frosted-glass] #right-arrow-button.ytd-feed-filter-chip-bar-renderer {
background-color: var(--yt-spec-base-background) !important
}
ytd-feed-filter-chip-bar-renderer[frosted-glass] #left-arrow.ytd-feed-filter-chip-bar-renderer:after {
background: linear-gradient(to right, var(--yt-spec-base-background) 20%, rgba(255, 255, 255, 0) 80%) !important
}
ytd-feed-filter-chip-bar-renderer[frosted-glass] #right-arrow.ytd-feed-filter-chip-bar-renderer:before {
background: linear-gradient(to left, var(--yt-spec-base-background) 20%, rgba(255, 255, 255, 0) 80%) !important
}
ytd-masthead[frosted-glass=with-chipbar] #background.ytd-masthead,
ytd-masthead[frosted-glass=without-chipbar] #background.ytd-masthead,
ytd-app[frosted-glass-mode=with-chipbar] #frosted-glass.ytd-app,
ytd-app[frosted-glass-mode=without-chipbar] #frosted-glass.ytd-app {
background: var(--yt-spec-base-background) !important;
backdrop-filter: none !important
}
.ytp-cairo-refresh-signature-moments .ytp-play-progress, ytd-thumbnail-overlay-resume-playback-renderer[enable-refresh-signature-moments-web] #progress.ytd-thumbnail-overlay-resume-playback-renderer, .YtThumbnailOverlayProgressBarHostWatchedProgressBarSegmentModern, .YtChapteredProgressBarChapteredPlayerBarChapterRefresh, .YtChapteredProgressBarChapteredPlayerBarFillRefresh, .YtProgressBarLineProgressBarPlayedRefresh, yt-page-navigation-progress[enable-refresh-signature-moments-web] #progress.yt-page-navigation-progress, ytd-progress-bar-line[enable-refresh-signature-moments-web] .progress-bar-played.ytd-progress-bar-line, #logo-icon > .yt-spec-icon-shape.yt-icon.style-scope.yt-icon-shape > div > svg > g:first-of-type > path:first-of-type {
background: #ff0000 !important
}
[d*="M18 4v15.06l-5.42-3.87-.58-.42-.58.42L6 19.06V4h12m1-1H5v18l7-5 7 5V3z"] {
d: path("M22 13h-4v4h-2v-4h-4v-2h4V7h2v4h4v2zm-8-6H2v1h12V7zM2 12h8v-1H2v1zm0 4h8v-1H2v1z")
}`;
if (typeof GM_addStyle !== "undefined") {
GM_addStyle(css);
} else {
let styleNode = document.createElement("style");
styleNode.appendChild(document.createTextNode(css));
(document.querySelector("head") || document.documentElement).appendChild(styleNode);
}
})();
// Integrate 'YouTube Video Resize Fix' script (special thanks to CY Fung)
/* jshint esversion:8 */
((__CONTEXT01__) => {
'use strict';
const win = this instanceof Window ? this : window;
// Create a unique key for the script and check if it is already running
const hkey_script = 'ahceihvpbosz';
if (win[hkey_script]) throw new Error('Duplicated Userscript Calling'); // avoid duplicated scripting
win[hkey_script] = true;
const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);
const indr = o => insp(o).$ || o.$ || 0;
/** @type {globalThis.PromiseConstructor} */
const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
const cleanContext = async (win) => {
const waitFn = requestAnimationFrame; // shall have been binded to window
try {
let mx = 16; // MAX TRIAL
const frameId = 'vanillajs-iframe-v1'
let frame = document.getElementById(frameId);
let removeIframeFn = null;
if (!frame) {
frame = document.createElement('iframe');
frame.id = frameId;
const blobURL = typeof webkitCancelAnimationFrame === 'function' && typeof kagi === 'undefined' ? (frame.src = URL.createObjectURL(new Blob([], { type: 'text/html' }))) : null; // avoid Brave Crash
frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
n.appendChild(frame);
while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
const root = document.documentElement;
root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
if (blobURL) Promise.resolve().then(() => URL.revokeObjectURL(blobURL));
removeIframeFn = (setTimeout) => {
const removeIframeOnDocumentReady = (e) => {
e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
e = n;
n = win = removeIframeFn = 0;
setTimeout ? setTimeout(() => e.remove(), 200) : e.remove();
}
if (!setTimeout || document.readyState !== 'loading') {
removeIframeOnDocumentReady();
} else {
win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
}
}
}
while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
const fc = frame.contentWindow;
if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
try {
const { requestAnimationFrame, setTimeout, clearTimeout } = fc;
const res = { requestAnimationFrame, setTimeout, clearTimeout };
for (let k in res) res[k] = res[k].bind(win); // necessary
if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
return res;
} catch (e) {
if (removeIframeFn) removeIframeFn();
return null;
}
} catch (e) {
console.warn(e);
return null;
}
};
const isWatchPageURL = (url) => {
url = url || location;
return location.pathname === '/watch' || location.pathname.startsWith('/live/')
};
cleanContext(win).then(__CONTEXT02__ => {
if (!__CONTEXT02__) return null;
const { ResizeObserver } = __CONTEXT01__;
const { requestAnimationFrame, setTimeout, clearTimeout } = __CONTEXT02__;
const elements = {};
let rid1 = 0;
let rid2 = 0;
/** @type {MutationObserver | null} */
let attrObserver = null;
/** @type {ResizeObserver | null} */
let resizeObserver = null;
let isHTMLAttrApplied = false;
const core = {
begin() {
document.addEventListener('yt-player-updated', core.hanlder, true);
document.addEventListener('ytd-navigate-finish', core.hanlder, true);
},
hanlder: () => {
rid1++;
if (rid1 > 1e9) rid1 = 9;
const tid = rid1;
requestAnimationFrame(() => {
if (tid !== rid1) return;
core.runner();
})
},
async runner() {
if (!location.href.startsWith('https://www.youtube.com/')) return;
if (!isWatchPageURL()) return;
elements.ytdFlexy = document.querySelector('ytd-watch-flexy');
elements.video = document.querySelector('ytd-watch-flexy #movie_player video, ytd-watch-flexy #movie_player audio.video-stream.html5-main-video');
if (elements.ytdFlexy && elements.video) { } else return;
elements.moviePlayer = elements.video.closest('#movie_player');
if (!elements.moviePlayer) return;
// resize Video
let { ytdFlexy } = elements;
if (!ytdFlexy.ElYTL) {
ytdFlexy.ElYTL = 1;
const ytdFlexyCnt = insp(ytdFlexy);
if (typeof ytdFlexyCnt.calculateNormalPlayerSize_ === 'function') {
ytdFlexyCnt.calculateNormalPlayerSize_ = core.resizeFunc(ytdFlexyCnt.calculateNormalPlayerSize_, 1);
} else {
console.warn('ytdFlexyCnt.calculateNormalPlayerSize_ is not a function.')
}
if (typeof ytdFlexyCnt.calculateCurrentPlayerSize_ === 'function') {
ytdFlexyCnt.calculateCurrentPlayerSize_ = core.resizeFunc(ytdFlexyCnt.calculateCurrentPlayerSize_, 0);
} else {
console.warn('ytdFlexyCnt.calculateCurrentPlayerSize_ is not a function.')
}
}
ytdFlexy = null;
// when video is fetched
elements.video.removeEventListener('canplay', core.triggerResizeDelayed, false);
elements.video.addEventListener('canplay', core.triggerResizeDelayed, false);
// when video is resized
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
if (typeof ResizeObserver === 'function') {
resizeObserver = new ResizeObserver(core.triggerResizeDelayed);
resizeObserver.observe(elements.moviePlayer);
}
// MutationObserver:[collapsed] @ ytd-live-chat-frame#chat
if (attrObserver) {
attrObserver.takeRecords();
attrObserver.disconnect();
attrObserver = null;
}
let chat = document.querySelector('ytd-watch-flexy ytd-live-chat-frame#chat');
if (chat) {
// resize due to DOM update
attrObserver = new MutationObserver(core.triggerResizeDelayed);
attrObserver.observe(chat, { attributes: true, attributeFilter: ["collapsed"] });
chat = null;
}
// resize on idle
Promise.resolve().then(core.triggerResizeDelayed);
},
resizeFunc(originalFunc, kb) {
return function () {
rid2++;
if (!isHTMLAttrApplied) {
isHTMLAttrApplied = true;
Promise.resolve(0).then(() => {
document.documentElement.classList.add('youtube-video-resize-fix');
}).catch(console.warn);
}
if (document.fullscreenElement === null) {
// calculateCurrentPlayerSize_ shall be always return NaN to make correct positioning of toolbars
if (!kb) return { width: NaN, height: NaN };
let ret = core.calculateSize();
if (ret.height > 0 && ret.width > 0) {
return ret;
}
}
return originalFunc.apply(this, arguments);
}
},
calculateSize_() {
const { moviePlayer, video } = elements;
const rect1 = { width: video.videoWidth, height: video.videoHeight }; // native values independent of css rules
if (rect1.width > 0 && rect1.height > 0) {
const rect2 = moviePlayer.getBoundingClientRect();
const aspectRatio = rect1.width / rect1.height;
let h2 = rect2.width / aspectRatio;
let w2 = rect2.height * aspectRatio;
return { rect2, h2, w2 };
}
return null;
},
calculateSize() {
let rs = core.calculateSize_();
if (!rs) return { width: NaN, height: NaN };
const { rect2, h2, w2 } = rs;
if (h2 > rect2.height) {
return { width: w2, height: rect2.height };
} else {
return { width: rect2.width, height: h2 };
}
},
triggerResizeDelayed: () => {
rid2++;
if (rid2 > 1e9) rid2 = 9;
const tid = rid2;
requestAnimationFrame(() => {
if (tid !== rid2) return;
const { ytdFlexy } = elements;
let r = false;
const ytdFlexyCnt = insp(ytdFlexy);
const windowSize_ = ytdFlexyCnt.windowSize_;
if (windowSize_ && typeof ytdFlexyCnt.onWindowResized_ === 'function') {
try {
ytdFlexyCnt.onWindowResized_(windowSize_);
r = true;
} catch (e) { }
}
if (!r) window.dispatchEvent(new Event('resize'));
})
}
};
core.begin();
// YouTube Watch Page Reflect (WPR)
// This script enhances the functionality of YouTube pages by reflecting changes in the page state.
(async function youTubeWPR() {
let checkPageVisibilityChanged = false;
// A WeakSet to keep track of elements being monitored for mutations.
const monitorWeakSet = new WeakSet();
/** @type {globalThis.PromiseConstructor} */
const Promise = (async () => { })().constructor;
// Function to reflect the current state of the YouTube page.
async function _reflect() {
await Promise.resolve();
const youtubeWpr = document.documentElement.getAttribute("youtube-wpr");
let s = '';
// Check if the current page is the video watch page.
if (isWatchPageURL()) {
let watch = document.querySelector("ytd-watch-flexy");
let chat = document.querySelector("ytd-live-chat-frame#chat");
if (watch) {
// Determine the state of the chat and video player on the watch page and generate a state string.
s += !chat ? 'h0' : (chat.hasAttribute('collapsed') || !document.querySelector('iframe#chatframe')) ? 'h1' : 'h2';
s += watch.hasAttribute('is-two-columns_') ? 's' : 'S';
s += watch.hasAttribute('fullscreen') ? 'F' : 'f';
s += watch.hasAttribute('theater') ? 'T' : 't';
}
}
// Update the reflected state if it has changed.
if (s !== youtubeWpr) {
document.documentElement.setAttribute("youtube-wpr", s);
}
}
// Function to reflect changes in specific attributes of monitored elements.
async function reflect(nodeName, attrNames, forced) {
await Promise.resolve();
if (!forced) {
let skip = true;
for (const attrName of attrNames) {
if (nodeName === 'ytd-live-chat-frame') {
if (attrName === 'collapsed') skip = false;
} else if (nodeName === 'ytd-watch-flexy') {
if (attrName === 'is-two-columns_') skip = false;
else if (attrName === 'fullscreen') skip = false;
else if (attrName === 'theater') skip = false;
}
}
if (skip) return;
}
// Log the mutated element and its attributes.
// console.log(nodeName, attrNames);
// Call _reflect() to update the reflected state.
_reflect();
}
// Callback function for the MutationObserver that tracks mutations in monitored elements.
function callback(mutationsList) {
const attrNames = new Set();
let nodeName = null;
for (const mutation of mutationsList) {
if (nodeName === null && mutation.target) nodeName = mutation.target.nodeName.toLowerCase();
attrNames.add(mutation.attributeName);
}
reflect(nodeName, attrNames, false);
}
function getParent(element) {
return element.__shady_native_parentNode || element.__shady_parentNode || element.parentNode;
}
let lastPageTypeChanged = 0;
function chatContainerMutationHandler() {
if (Date.now() - lastPageTypeChanged < 800) _reflect();
}
// Function to start monitoring an element for mutations.
function monitor(element) {
if (!element) return;
if (monitorWeakSet.has(element)) {
return;
}
monitorWeakSet.add(element);
const observer = new MutationObserver(callback);
observer.observe(element, { attributes: true });
if (element.id === 'chat') {
const parentNode = getParent(element);
if (parentNode instanceof Element && parentNode.id === 'chat-container' && !monitorWeakSet.has(parentNode)) {
monitorWeakSet.add(parentNode);
const observer = new MutationObserver(chatContainerMutationHandler);
observer.observe(parentNode, { childList: true, subtree: false });
}
}
return 1;
}
let timeout = 0;
// Function to monitor relevant elements and update the reflected state.
let g = async (forced) => {
await Promise.resolve();
let b = 0;
b = b | monitor(document.querySelector("ytd-watch-flexy"));
b = b | monitor(document.querySelector("ytd-live-chat-frame#chat"));
if (b || forced) {
_reflect();
}
}
// let renderId = 0;
// Event handler function that triggers when the page finishes navigation or page data updates.
let eventHandlerFunc = async (evt) => {
checkPageVisibilityChanged = true;
timeout = Date.now() + 800;
g(1);
if (evt.type === 'yt-navigate-finish') {
// delay required when page type is changed for #chat (home -> watch).
setTimeout(() => {
g(1);
}, 80);
} else if (evt.type === 'yt-page-type-changed') {
lastPageTypeChanged = Date.now();
// setTimeout(() => {
// if (renderId > 1e9) renderId = 9;
// const t = ++renderId;
// requestAnimationFrame(() => {
// if (t !== renderId) return;
// g(1);
// });
// }, 180);
if (typeof requestIdleCallback === 'function') {
requestIdleCallback(() => {
g(1);
});
}
}
}
let loadState = 0;
// Function to initialize the script and start monitoring the page.
async function actor() {
if (loadState === 0) {
if (!document.documentElement.hasAttribute("youtube-wpr")) {
loadState = 1;
document.documentElement.setAttribute("youtube-wpr", "");
document.addEventListener("yt-navigate-finish", eventHandlerFunc, false);
document.addEventListener("yt-page-data-updated", eventHandlerFunc, false);
document.addEventListener("yt-page-type-changed", eventHandlerFunc, false);
} else {
loadState = -1;
document.removeEventListener("yt-page-data-fetched", actor, false);
return;
}
}
if (loadState === 1) {
timeout = Date.now() + 800;
// Function to continuously monitor elements and update the reflected state.
let pf = () => {
g(0);
if (Date.now() < timeout) requestAnimationFrame(pf);
};
pf();
}
}
// Event listener that triggers when page data is fetched.
document.addEventListener("yt-page-data-fetched", actor, false);
// Update after visibility changed (looks like there are bugs due to inactive tab)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState !== 'visible') return;
if (checkPageVisibilityChanged) {
checkPageVisibilityChanged = false;
setTimeout(() => {
g(1);
}, 100);
requestAnimationFrame(() => {
g(1);
});
}
}, false);
})();
});
})({ ResizeObserver });