Skip to content

Commit 78d990c

Browse files
authored
feat(album-color-theme): improve Album Color Theme style (#1571)
1 parent 4d3e2c0 commit 78d990c

File tree

5 files changed

+151
-177
lines changed

5 files changed

+151
-177
lines changed

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
"async-mutex": "0.4.0",
148148
"butterchurn": "3.0.0-beta.4",
149149
"butterchurn-presets": "3.0.0-beta.4",
150+
"color": "4.2.3",
150151
"conf": "10.2.0",
151152
"custom-electron-prompt": "1.5.7",
152153
"dbus-next": "0.10.2",
@@ -179,6 +180,7 @@
179180
"devDependencies": {
180181
"@playwright/test": "1.41.0-alpha-dec-18-2023",
181182
"@total-typescript/ts-reset": "0.5.1",
183+
"@types/color": "3.0.6",
182184
"@types/electron-localshortcut": "3.1.3",
183185
"@types/howler": "2.2.11",
184186
"@types/html-to-text": "9.0.4",

pnpm-lock.yaml

+47
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+58-164
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { FastAverageColor } from 'fast-average-color';
2+
import Color from 'color';
23

34
import style from './style.css?inline';
45

56
import { createPlugin } from '@/utils';
67
import { t } from '@/i18n';
78

8-
import type { VideoDataChanged } from '@/types/video-data-changed';
9+
const COLOR_KEY = '--ytmusic-album-color';
10+
const DARK_COLOR_KEY = '--ytmusic-album-color-dark';
911

1012
export default createPlugin({
1113
name: () => t('plugins.album-color-theme.name'),
@@ -16,69 +18,8 @@ export default createPlugin({
1618
},
1719
stylesheets: [style],
1820
renderer: {
19-
hexToHSL: (H: string) => {
20-
// Convert hex to RGB first
21-
let r = 0;
22-
let g = 0;
23-
let b = 0;
24-
if (H.length == 4) {
25-
r = Number('0x' + H[1] + H[1]);
26-
g = Number('0x' + H[2] + H[2]);
27-
b = Number('0x' + H[3] + H[3]);
28-
} else if (H.length == 7) {
29-
r = Number('0x' + H[1] + H[2]);
30-
g = Number('0x' + H[3] + H[4]);
31-
b = Number('0x' + H[5] + H[6]);
32-
}
33-
// Then to HSL
34-
r /= 255;
35-
g /= 255;
36-
b /= 255;
37-
const cmin = Math.min(r, g, b);
38-
const cmax = Math.max(r, g, b);
39-
const delta = cmax - cmin;
40-
let h: number;
41-
let s: number;
42-
let l: number;
43-
44-
if (delta == 0) {
45-
h = 0;
46-
} else if (cmax == r) {
47-
h = ((g - b) / delta) % 6;
48-
} else if (cmax == g) {
49-
h = ((b - r) / delta) + 2;
50-
} else {
51-
h = ((r - g) / delta) + 4;
52-
}
53-
54-
h = Math.round(h * 60);
55-
56-
if (h < 0) {
57-
h += 360;
58-
}
59-
60-
l = (cmax + cmin) / 2;
61-
s = delta == 0 ? 0 : delta / (1 - Math.abs((2 * l) - 1));
62-
s = +(s * 100).toFixed(1);
63-
l = +(l * 100).toFixed(1);
64-
65-
//return "hsl(" + h + "," + s + "%," + l + "%)";
66-
return [h, s, l];
67-
},
68-
hue: 0,
69-
saturation: 0,
70-
lightness: 0,
71-
72-
changeElementColor: (
73-
element: HTMLElement | null,
74-
hue: number,
75-
saturation: number,
76-
lightness: number,
77-
) => {
78-
if (element) {
79-
element.style.backgroundColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
80-
}
81-
},
21+
color: null as Color | null,
22+
darkColor: null as Color | null,
8223

8324
playerPage: null as HTMLElement | null,
8425
navBarBackground: null as HTMLElement | null,
@@ -103,113 +44,66 @@ export default createPlugin({
10344
'#mini-guide-background',
10445
);
10546
this.ytmusicAppLayout = document.querySelector<HTMLElement>('#layout');
47+
},
48+
onPlayerApiReady(playerApi) {
49+
const fastAverageColor = new FastAverageColor();
50+
51+
document.addEventListener('videodatachange', async (event) => {
52+
if (event.detail.name !== 'dataloaded') return;
53+
54+
const playerResponse = playerApi.getPlayerResponse();
55+
const thumbnail = playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0);
56+
if (!thumbnail) return;
10657

107-
const observer = new MutationObserver((mutationsList) => {
108-
for (const mutation of mutationsList) {
109-
if (mutation.type === 'attributes') {
110-
const isPageOpen =
111-
this.ytmusicAppLayout?.hasAttribute('player-page-open');
112-
if (isPageOpen) {
113-
this.changeElementColor(
114-
this.sidebarSmall,
115-
this.hue,
116-
this.saturation,
117-
this.lightness - 30,
118-
);
119-
} else {
120-
if (this.sidebarSmall) {
121-
this.sidebarSmall.style.backgroundColor = 'black';
122-
}
123-
}
58+
const albumColor = await fastAverageColor.getColorAsync(thumbnail.url)
59+
.catch((err) => {
60+
console.error(err);
61+
return null;
62+
});
63+
64+
if (albumColor) {
65+
const target = Color(albumColor.hex);
66+
67+
this.darkColor = target.darken(0.3).rgb();
68+
this.color = target.darken(0.15).rgb();
69+
70+
while (this.color.luminosity() > 0.5) {
71+
this.color = this.color?.darken(0.05);
72+
this.darkColor = this.darkColor?.darken(0.05);
12473
}
74+
75+
document.documentElement.style.setProperty(COLOR_KEY, `${~~this.color.red()}, ${~~this.color.green()}, ${~~this.color.blue()}`);
76+
document.documentElement.style.setProperty(DARK_COLOR_KEY, `${~~this.darkColor.red()}, ${~~this.darkColor.green()}, ${~~this.darkColor.blue()}`);
77+
} else {
78+
document.documentElement.style.setProperty(COLOR_KEY, '0, 0, 0');
79+
document.documentElement.style.setProperty(DARK_COLOR_KEY, '0, 0, 0');
12580
}
81+
82+
this.updateColor();
12683
});
84+
},
85+
getColor(key: string, alpha = 1) {
86+
return `rgba(var(${key}), ${alpha})`;
87+
},
88+
updateColor() {
89+
const change = (element: HTMLElement | null, color: string) => {
90+
if (element) {
91+
element.style.backgroundColor = color;
92+
}
93+
};
94+
95+
change(this.playerPage, this.getColor(DARK_COLOR_KEY));
96+
change(this.navBarBackground, this.getColor(COLOR_KEY));
97+
change(this.ytmusicPlayerBar, this.getColor(COLOR_KEY));
98+
change(this.playerBarBackground, this.getColor(COLOR_KEY));
99+
change(this.sidebarBig, this.getColor(COLOR_KEY));
127100

128-
if (this.playerPage) {
129-
observer.observe(this.playerPage, { attributes: true });
101+
if (this.ytmusicAppLayout?.hasAttribute('player-page-open')) {
102+
change(this.sidebarSmall, this.getColor(DARK_COLOR_KEY));
130103
}
131-
},
132-
onPlayerApiReady(playerApi) {
133-
const fastAverageColor = new FastAverageColor();
134104

135-
document.addEventListener(
136-
'videodatachange',
137-
(event: CustomEvent<VideoDataChanged>) => {
138-
if (event.detail.name === 'dataloaded') {
139-
const playerResponse = playerApi.getPlayerResponse();
140-
const thumbnail =
141-
playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0);
142-
if (thumbnail) {
143-
fastAverageColor
144-
.getColorAsync(thumbnail.url)
145-
.then((albumColor) => {
146-
if (albumColor) {
147-
const [hue, saturation, lightness] = ([
148-
this.hue,
149-
this.saturation,
150-
this.lightness,
151-
] = this.hexToHSL(albumColor.hex));
152-
this.changeElementColor(
153-
this.playerPage,
154-
hue,
155-
saturation,
156-
lightness - 30,
157-
);
158-
this.changeElementColor(
159-
this.navBarBackground,
160-
hue,
161-
saturation,
162-
lightness - 15,
163-
);
164-
this.changeElementColor(
165-
this.ytmusicPlayerBar,
166-
hue,
167-
saturation,
168-
lightness - 15,
169-
);
170-
this.changeElementColor(
171-
this.playerBarBackground,
172-
hue,
173-
saturation,
174-
lightness - 15,
175-
);
176-
this.changeElementColor(
177-
this.sidebarBig,
178-
hue,
179-
saturation,
180-
lightness - 15,
181-
);
182-
if (
183-
this.ytmusicAppLayout?.hasAttribute('player-page-open')
184-
) {
185-
this.changeElementColor(
186-
this.sidebarSmall,
187-
hue,
188-
saturation,
189-
lightness - 30,
190-
);
191-
}
192-
const ytRightClickList =
193-
document.querySelector<HTMLElement>(
194-
'tp-yt-paper-listbox',
195-
);
196-
this.changeElementColor(
197-
ytRightClickList,
198-
hue,
199-
saturation,
200-
lightness - 15,
201-
);
202-
} else {
203-
if (this.playerPage) {
204-
this.playerPage.style.backgroundColor = '#000000';
205-
}
206-
}
207-
})
208-
.catch((e) => console.error(e));
209-
}
210-
}
211-
},
212-
);
105+
const ytRightClickList = document.querySelector<HTMLElement>('tp-yt-paper-listbox');
106+
change(ytRightClickList, this.getColor(COLOR_KEY));
213107
},
214108
},
215109
});

0 commit comments

Comments
 (0)