mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-16 04:41:47 +00:00
feat(album-color-theme): improve Album Color Theme style (#1571)
This commit is contained in:
@ -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<HTMLElement>('#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<VideoDataChanged>) => {
|
||||
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<HTMLElement>(
|
||||
'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<HTMLElement>('tp-yt-paper-listbox');
|
||||
change(ytRightClickList, this.getColor(COLOR_KEY));
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user