diff --git a/package.json b/package.json index 544d6397..66e96a59 100644 --- a/package.json +++ b/package.json @@ -147,6 +147,7 @@ "async-mutex": "0.4.0", "butterchurn": "3.0.0-beta.4", "butterchurn-presets": "3.0.0-beta.4", + "color": "4.2.3", "conf": "10.2.0", "custom-electron-prompt": "1.5.7", "dbus-next": "0.10.2", @@ -179,6 +180,7 @@ "devDependencies": { "@playwright/test": "1.41.0-alpha-dec-18-2023", "@total-typescript/ts-reset": "0.5.1", + "@types/color": "3.0.6", "@types/electron-localshortcut": "3.1.3", "@types/howler": "2.2.11", "@types/html-to-text": "9.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6c69517..b0358add 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,6 +54,9 @@ dependencies: butterchurn-presets: specifier: 3.0.0-beta.4 version: 3.0.0-beta.4 + color: + specifier: 4.2.3 + version: 4.2.3 conf: specifier: 10.2.0 version: 10.2.0 @@ -146,6 +149,9 @@ devDependencies: '@total-typescript/ts-reset': specifier: 0.5.1 version: 0.5.1 + '@types/color': + specifier: 3.0.6 + version: 3.0.6 '@types/electron-localshortcut': specifier: 3.1.3 version: 3.1.3 @@ -1284,6 +1290,22 @@ packages: '@types/har-format': 1.2.15 dev: false + /@types/color-convert@2.0.3: + resolution: {integrity: sha512-2Q6wzrNiuEvYxVQqhh7sXM2mhIhvZR/Paq4FdsQkOMgWsCIkKvSGj8Le1/XalulrmgOzPMqNa0ix+ePY4hTrfg==} + dependencies: + '@types/color-name': 1.1.3 + dev: true + + /@types/color-name@1.1.3: + resolution: {integrity: sha512-87W6MJCKZYDhLAx/J1ikW8niMvmGRyY+rpUxWpL1cO7F8Uu5CHuQoFv+R0/L5pgNdW4jTyda42kv60uwVIPjLw==} + dev: true + + /@types/color@3.0.6: + resolution: {integrity: sha512-NMiNcZFRUAiUUCCf7zkAelY8eV3aKqfbzyFQlXpPIEeoNDbsEHGpb854V3gzTsGKYj830I5zPuOwU/TP5/cW6A==} + dependencies: + '@types/color-convert': 2.0.3 + dev: true + /@types/debug@4.1.12: resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: @@ -2307,6 +2329,21 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} requiresBuild: true + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: false + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -4071,6 +4108,10 @@ packages: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: @@ -5608,6 +5649,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + /simple-update-notifier@2.0.0: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} diff --git a/src/plugins/album-color-theme/index.ts b/src/plugins/album-color-theme/index.ts index 5af646a1..cb153623 100644 --- a/src/plugins/album-color-theme/index.ts +++ b/src/plugins/album-color-theme/index.ts @@ -1,11 +1,13 @@ import { FastAverageColor } from 'fast-average-color'; +import Color from 'color'; import style from './style.css?inline'; import { createPlugin } from '@/utils'; import { t } from '@/i18n'; -import type { VideoDataChanged } from '@/types/video-data-changed'; +const COLOR_KEY = '--ytmusic-album-color'; +const DARK_COLOR_KEY = '--ytmusic-album-color-dark'; export default createPlugin({ name: () => t('plugins.album-color-theme.name'), @@ -16,69 +18,8 @@ export default createPlugin({ }, stylesheets: [style], renderer: { - hexToHSL: (H: string) => { - // Convert hex to RGB first - let r = 0; - let g = 0; - let b = 0; - if (H.length == 4) { - r = Number('0x' + H[1] + H[1]); - g = Number('0x' + H[2] + H[2]); - b = Number('0x' + H[3] + H[3]); - } else if (H.length == 7) { - r = Number('0x' + H[1] + H[2]); - g = Number('0x' + H[3] + H[4]); - b = Number('0x' + H[5] + H[6]); - } - // Then to HSL - r /= 255; - g /= 255; - b /= 255; - const cmin = Math.min(r, g, b); - const cmax = Math.max(r, g, b); - const delta = cmax - cmin; - let h: number; - let s: number; - let l: number; - - if (delta == 0) { - h = 0; - } else if (cmax == r) { - h = ((g - b) / delta) % 6; - } else if (cmax == g) { - h = ((b - r) / delta) + 2; - } else { - h = ((r - g) / delta) + 4; - } - - h = Math.round(h * 60); - - if (h < 0) { - h += 360; - } - - l = (cmax + cmin) / 2; - s = delta == 0 ? 0 : delta / (1 - Math.abs((2 * l) - 1)); - s = +(s * 100).toFixed(1); - l = +(l * 100).toFixed(1); - - //return "hsl(" + h + "," + s + "%," + l + "%)"; - return [h, s, l]; - }, - hue: 0, - saturation: 0, - lightness: 0, - - changeElementColor: ( - element: HTMLElement | null, - hue: number, - saturation: number, - lightness: number, - ) => { - if (element) { - element.style.backgroundColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`; - } - }, + color: null as Color | null, + darkColor: null as Color | null, playerPage: null as HTMLElement | null, navBarBackground: null as HTMLElement | null, @@ -103,113 +44,66 @@ export default createPlugin({ '#mini-guide-background', ); this.ytmusicAppLayout = document.querySelector('#layout'); - - const observer = new MutationObserver((mutationsList) => { - for (const mutation of mutationsList) { - if (mutation.type === 'attributes') { - const isPageOpen = - this.ytmusicAppLayout?.hasAttribute('player-page-open'); - if (isPageOpen) { - this.changeElementColor( - this.sidebarSmall, - this.hue, - this.saturation, - this.lightness - 30, - ); - } else { - if (this.sidebarSmall) { - this.sidebarSmall.style.backgroundColor = 'black'; - } - } - } - } - }); - - if (this.playerPage) { - observer.observe(this.playerPage, { attributes: true }); - } }, onPlayerApiReady(playerApi) { const fastAverageColor = new FastAverageColor(); - document.addEventListener( - 'videodatachange', - (event: CustomEvent) => { - if (event.detail.name === 'dataloaded') { - const playerResponse = playerApi.getPlayerResponse(); - const thumbnail = - playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0); - if (thumbnail) { - fastAverageColor - .getColorAsync(thumbnail.url) - .then((albumColor) => { - if (albumColor) { - const [hue, saturation, lightness] = ([ - this.hue, - this.saturation, - this.lightness, - ] = this.hexToHSL(albumColor.hex)); - this.changeElementColor( - this.playerPage, - hue, - saturation, - lightness - 30, - ); - this.changeElementColor( - this.navBarBackground, - hue, - saturation, - lightness - 15, - ); - this.changeElementColor( - this.ytmusicPlayerBar, - hue, - saturation, - lightness - 15, - ); - this.changeElementColor( - this.playerBarBackground, - hue, - saturation, - lightness - 15, - ); - this.changeElementColor( - this.sidebarBig, - hue, - saturation, - lightness - 15, - ); - if ( - this.ytmusicAppLayout?.hasAttribute('player-page-open') - ) { - this.changeElementColor( - this.sidebarSmall, - hue, - saturation, - lightness - 30, - ); - } - const ytRightClickList = - document.querySelector( - 'tp-yt-paper-listbox', - ); - this.changeElementColor( - ytRightClickList, - hue, - saturation, - lightness - 15, - ); - } else { - if (this.playerPage) { - this.playerPage.style.backgroundColor = '#000000'; - } - } - }) - .catch((e) => console.error(e)); - } + document.addEventListener('videodatachange', async (event) => { + if (event.detail.name !== 'dataloaded') return; + + const playerResponse = playerApi.getPlayerResponse(); + const thumbnail = playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0); + if (!thumbnail) return; + + const albumColor = await fastAverageColor.getColorAsync(thumbnail.url) + .catch((err) => { + console.error(err); + return null; + }); + + if (albumColor) { + const target = Color(albumColor.hex); + + this.darkColor = target.darken(0.3).rgb(); + this.color = target.darken(0.15).rgb(); + + while (this.color.luminosity() > 0.5) { + this.color = this.color?.darken(0.05); + this.darkColor = this.darkColor?.darken(0.05); } - }, - ); + + document.documentElement.style.setProperty(COLOR_KEY, `${~~this.color.red()}, ${~~this.color.green()}, ${~~this.color.blue()}`); + document.documentElement.style.setProperty(DARK_COLOR_KEY, `${~~this.darkColor.red()}, ${~~this.darkColor.green()}, ${~~this.darkColor.blue()}`); + } else { + document.documentElement.style.setProperty(COLOR_KEY, '0, 0, 0'); + document.documentElement.style.setProperty(DARK_COLOR_KEY, '0, 0, 0'); + } + + this.updateColor(); + }); + }, + getColor(key: string, alpha = 1) { + return `rgba(var(${key}), ${alpha})`; + }, + updateColor() { + const change = (element: HTMLElement | null, color: string) => { + if (element) { + element.style.backgroundColor = color; + } + }; + + change(this.playerPage, this.getColor(DARK_COLOR_KEY)); + change(this.navBarBackground, this.getColor(COLOR_KEY)); + change(this.ytmusicPlayerBar, this.getColor(COLOR_KEY)); + change(this.playerBarBackground, this.getColor(COLOR_KEY)); + change(this.sidebarBig, this.getColor(COLOR_KEY)); + + if (this.ytmusicAppLayout?.hasAttribute('player-page-open')) { + change(this.sidebarSmall, this.getColor(DARK_COLOR_KEY)); + } + + const ytRightClickList = document.querySelector('tp-yt-paper-listbox'); + change(ytRightClickList, this.getColor(COLOR_KEY)); }, }, }); diff --git a/src/plugins/album-color-theme/style.css b/src/plugins/album-color-theme/style.css index 4577d7bb..0c19951f 100644 --- a/src/plugins/album-color-theme/style.css +++ b/src/plugins/album-color-theme/style.css @@ -4,28 +4,24 @@ yt-page-navigation-progress { } #player-page { - transition: - transform 300ms, - background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; + transition: transform 300ms, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; } #nav-bar-background { - transition: - opacity 200ms, - background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; + transition: opacity 200ms, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; } #mini-guide-background { - transition: - opacity 200ms, - background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; + transition: opacity 200ms, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; border-right: 0px !important; } #guide-wrapper { - transition: - opacity 200ms, - background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; + transition: opacity 200ms, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; } #img, @@ -37,3 +33,35 @@ yt-page-navigation-progress { #items { border-radius: 10px !important; } + +/* fix blur navigation bar */ + +ytmusic-app-layout > [slot='player-page'] { + padding-top: 90px; + margin-top: calc(-90px + var(--menu-bar-height, 0px)) !important; +} + +/* fix icon color */ + +.duration.ytmusic-player-queue-item, .byline.ytmusic-player-queue-item { + color: rgba(255, 255, 255, 0.5) !important; + --yt-endpoint-color: rgba(255, 255, 255, 0.5) !important; + --yt-endpoint-hover-color: rgba(255, 255, 255, 0.5) !important; + --yt-endpoint-visited-color: rgba(255, 255, 255, 0.5) !important; +} + +.icon.ytmusic-menu-navigation-item-renderer { + color: rgba(255, 255, 255, 0.5) !important; +} +.menu.ytmusic-player-bar { + --iron-icon-fill-color: rgba(255, 255, 255, 0.5) !important; +} +ytmusic-player-bar { + color: rgba(255, 255, 255, 0.5) !important; +} +.time-info.ytmusic-player-bar { + color: rgba(255, 255, 255, 0.5) !important; +} +.volume-slider.ytmusic-player-bar, .expand-volume-slider.ytmusic-player-bar { + --paper-slider-container-color: rgba(255, 255, 255, 0.5) !important; +} diff --git a/src/plugins/in-app-menu/titlebar.css b/src/plugins/in-app-menu/titlebar.css index aa7b5475..24e548ab 100644 --- a/src/plugins/in-app-menu/titlebar.css +++ b/src/plugins/in-app-menu/titlebar.css @@ -233,7 +233,10 @@ ytmusic-app-layout > [slot='nav-bar'], var(--ytmusic-nav-bar-height) + var(--menu-bar-height, 36px) ) !important; } -ytmusic-app[is-bauhaus-sidenav-enabled] #guide-spacer.ytmusic-app, +ytmusic-app[is-bauhaus-sidenav-enabled] #guide-spacer.ytmusic-app { + margin-top: calc(var(--menu-bar-height, 36px)) !important; +} + ytmusic-app[is-bauhaus-sidenav-enabled] #mini-guide-spacer.ytmusic-app { margin-top: calc( var(--ytmusic-nav-bar-height) + var(--menu-bar-height, 36px)