feat(transparent-player): new plugin for Acrylic, Mica or Tabbed effects (#3529)

Co-authored-by: JellyBrick <shlee1503@naver.com>
This commit is contained in:
FrostyBiscuit
2025-09-06 02:00:39 +02:00
committed by GitHub
parent 789a30312b
commit 895210cbb6
9 changed files with 309 additions and 21 deletions

View File

@ -31,6 +31,7 @@ export interface DefaultConfig {
likeButtons: string;
proxy: string;
startingPage: string;
backgroundMaterial?: 'none' | 'mica' | 'acrylic' | 'tabbed';
overrideUserAgent: boolean;
usePodcastParticipantAsArtist: boolean;
themes: string[];

View File

@ -853,6 +853,26 @@
"description": "Fügt ein TouchBar-Widget für macOS-Benutzer hinzu",
"name": "TouchBar"
},
"transparent-player": {
"description": "Macht das Player-Fenster transparent",
"name": "Transparent Player",
"menu": {
"opacity": {
"label": "Hintergrund-Sichtbarkeit",
"submenu": {
"percent": "{{opacity}}%"
}
},
"type": {
"label": "Typ",
"submenu": {
"acrylic": "Acrylic",
"mica": "Mica",
"tabbed": "Tabbed"
}
}
}
},
"tuna-obs": {
"description": "Integration mit dem OBS-Plugin Tuna",
"name": "Tuna OBS"

View File

@ -874,6 +874,26 @@
"description": "Adds a TouchBar widget for macOS users",
"name": "TouchBar"
},
"transparent-player": {
"description": "Makes the app window transparent",
"name": "Transparent Player",
"menu": {
"opacity": {
"label": "Opacity",
"submenu": {
"percent": "{{opacity}}%"
}
},
"type": {
"label": "Type",
"submenu": {
"acrylic": "Acrylic",
"mica": "Mica",
"tabbed": "Tabbed"
}
}
}
},
"tuna-obs": {
"description": "Integration with OBS's plugin Tuna",
"name": "Tuna OBS"

View File

@ -59,7 +59,7 @@ import ErrorHtmlAsset from '@assets/error.html?asset';
import { defaultAuthProxyConfig } from '@/plugins/auth-proxy-adapter/config';
import { type PluginConfig } from '@/types/plugins';
import type { PluginConfig } from '@/types/plugins';
// Catch errors and log them
unhandled({
@ -338,8 +338,8 @@ async function createMainWindow() {
titleBarStyle: useInlineMenu
? 'hidden'
: is.macOS()
? 'hiddenInset'
: 'default',
? 'hiddenInset'
: 'default',
autoHideMenuBar: config.get('options.hideMenu'),
};
@ -349,7 +349,7 @@ async function createMainWindow() {
delete decorations.titleBarStyle;
}
const win = new BrowserWindow({
const electronWindowSettings: Electron.BrowserWindowConstructorOptions = {
icon,
width: windowSize.width,
height: windowSize.height,
@ -369,7 +369,10 @@ async function createMainWindow() {
}),
},
...decorations,
});
};
const win = new BrowserWindow(electronWindowSettings);
await initHook(win);
initTheme(win);
@ -529,8 +532,8 @@ app.once('browser-window-created', (_event, win) => {
const updatedUserAgent = is.macOS()
? userAgents.mac
: is.windows()
? userAgents.windows
: userAgents.linux;
? userAgents.windows
: userAgents.linux;
win.webContents.userAgent = updatedUserAgent;
app.userAgentFallback = updatedUserAgent;
@ -951,15 +954,18 @@ function removeContentSecurityPolicy(
betterSession.webRequest.setResolver(
'onHeadersReceived',
async (listeners) => {
return listeners.reduce(async (accumulator, listener) => {
const acc = await accumulator;
if (acc.cancel) {
return acc;
}
return listeners.reduce(
async (accumulator, listener) => {
const acc = await accumulator;
if (acc.cancel) {
return acc;
}
const result = await listener.apply();
return { ...accumulator, ...result };
}, Promise.resolve({ cancel: false }));
const result = await listener.apply();
return { ...accumulator, ...result };
},
Promise.resolve({ cancel: false }),
);
},
);
}

View File

@ -31,7 +31,7 @@ export default createPlugin<
alpha?: number,
ratioMultiply?: number,
): string;
updateColor(): void;
updateColor(alpha: number): void;
},
{
enabled: boolean;
@ -143,7 +143,16 @@ export default createPlugin<
document.documentElement.style.setProperty(DARK_COLOR_KEY, '0, 0, 0');
}
this.updateColor();
let alpha: number | null = null;
if (await window.mainConfig.plugins.isEnabled('transparent-player')) {
const value: unknown = window.mainConfig.get(
'plugins.transparent-player.opacity',
);
if (typeof value === 'number' && value >= 0 && value <= 1) {
alpha = value;
}
}
this.updateColor(alpha ?? 1);
});
},
onConfigChange(config) {
@ -163,7 +172,7 @@ export default createPlugin<
}
return `color-mix(in srgb, ${color} ${originalRatio}, ${keyColor} ${colorRatio})`;
},
updateColor() {
updateColor(alpha: number) {
const variableMap = {
'--ytmusic-color-black1': '#212121',
'--ytmusic-color-black2': '#181818',
@ -202,19 +211,20 @@ export default createPlugin<
Object.entries(variableMap).map(([variable, color]) => {
document.documentElement.style.setProperty(
variable,
this.getMixedColor(color, COLOR_KEY),
this.getMixedColor(color, COLOR_KEY, alpha),
'important',
);
});
document.body.style.setProperty(
'background',
this.getMixedColor('#030303', COLOR_KEY),
this.getMixedColor('rgba(3, 3, 3)', DARK_COLOR_KEY, alpha),
'important',
);
document.documentElement.style.setProperty(
'--ytmusic-background',
this.getMixedColor('#030303', DARK_COLOR_KEY),
// #030303
this.getMixedColor('rgba(3, 3, 3)', DARK_COLOR_KEY, alpha),
'important',
);
},

View File

@ -329,6 +329,7 @@ export const TitleBar = (props: TitleBarProps) => {
data-macos={props.isMacOS}
data-show={mouseY() < 32}
data-ytmd-main-panel={true}
id={'ytmd-title-bar-main-panel'}
>
<IconButton
onClick={() => setCollapsed(!collapsed())}

View File

@ -0,0 +1,112 @@
import { t } from '@/i18n';
import { createPlugin } from '@/utils';
import { Platform } from '@/types/plugins';
import { MaterialType, type TransparentPlayerConfig } from './types';
import style from './style.css?inline';
import type { BrowserWindow } from 'electron';
const defaultConfig: TransparentPlayerConfig = {
enabled: false,
opacity: 0.5,
type: MaterialType.ACRYLIC,
};
const opacityList = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1];
const typeList = Object.values(MaterialType);
export default createPlugin({
name: () => t('plugins.transparent-player.name'),
description: () => t('plugins.transparent-player.description'),
addedVersion: '3.10.x',
restartNeeded: true,
platform: Platform.Windows,
config: defaultConfig,
stylesheets: [style],
async menu({ getConfig, setConfig }) {
const config = await getConfig();
return [
{
label: t('plugins.transparent-player.menu.opacity.label'),
submenu: opacityList.map((opacity) => ({
label: t('plugins.transparent-player.menu.opacity.submenu.percent', {
opacity: opacity * 100,
}),
type: 'radio',
checked: config.opacity === opacity,
click() {
setConfig({ opacity });
},
})),
},
{
label: t('plugins.transparent-player.menu.type.label'),
submenu: typeList.map((type) => ({
label: t(`plugins.transparent-player.menu.type.submenu.${type}`),
type: 'radio',
checked: config.type === type,
click() {
setConfig({ type });
},
})),
},
];
},
backend: {
mainWindow: null as BrowserWindow | null,
async start({ window, getConfig }) {
this.mainWindow = window;
const config = await getConfig();
window.setBackgroundMaterial?.(config.type);
window.setBackgroundColor?.(`rgba(0, 0, 0, ${config.opacity})`);
},
onConfigChange(newConfig) {
this.mainWindow?.setBackgroundMaterial?.(newConfig.type);
},
stop({ window }) {
window.setBackgroundMaterial?.('none');
},
},
renderer: {
props: {
enabled: defaultConfig.enabled,
opacity: defaultConfig.opacity,
type: defaultConfig.type,
} as TransparentPlayerConfig,
async start({ getConfig }) {
const config = await getConfig();
this.props = config;
if (config.enabled) {
document.body.classList.add('transparent-background-color');
document.body.classList.add('transparent-player-backdrop-filter');
if (!(await window.mainConfig.plugins.isEnabled('album-color-theme'))) {
document.body.classList.add('transparent-player');
}
this.applyVariables();
}
},
onConfigChange(newConfig) {
this.props = newConfig;
this.applyVariables();
},
stop() {
document.body.classList.remove('transparent-background-color');
document.body.classList.remove('transparent-player-backdrop-filter');
document.body.classList.remove('transparent-player');
document.documentElement.style.removeProperty(
'--ytmd-transparent-player-opacity',
);
},
applyVariables(this: { props: TransparentPlayerConfig }) {
const { opacity } = this.props;
document.documentElement.style.setProperty(
'--ytmd-transparent-player-opacity',
opacity.toString(),
);
},
},
});

View File

@ -0,0 +1,106 @@
:root {
--ytmd-transparent-player-transparency-color: #111;
--ytmd-transparent-player-transparent-background: rgb(
from var(--ytmd-transparent-player-transparency-color) r g b /
var(--ytmd-transparent-player-opacity, 0.5)
);
--ytmd-transparent-player-transparent-background-dark: rgb(
from var(--ytmd-transparent-player-transparency-color) r g b / 0.8
);
--ytmd-transparent-player-backdrop-blur: blur(20px);
}
body.transparent-background-color {
background-color: var(--ytmd-transparent-player-transparent-background) !important;
}
body.transparent-player-backdrop-filter {
#layout {
#nav-bar-background,
#player-bar-background {
backdrop-filter: var(--ytmd-transparent-player-backdrop-blur) !important;
}
}
#search-page {
#tabs {
&.stuck {
backdrop-filter: var(--ytmd-transparent-player-backdrop-blur) !important;
}
}
}
ytmusic-menu-popup-renderer {
backdrop-filter: var(--ytmd-transparent-player-backdrop-blur) !important;
}
#ytmd-title-bar-main-panel {
backdrop-filter: var(--ytmd-transparent-player-backdrop-blur) !important;
}
}
body.transparent-player {
ytmusic-app {
ytmusic-app-layout[player-page-open] {
#nav-bar-background.ytmusic-app-layout,
#player-bar-background.ytmusic-app-layout {
opacity: 0 !important;
}
}
#layout {
#nav-bar-background,
#player-bar-background {
background: var(--ytmd-transparent-player-transparent-background-dark) !important;
}
#mini-guide-background {
background: none !important;
border: 0 !important;
}
#guide {
#guide-wrapper {
background: none !important;
border: 0 !important;
}
}
ytmusic-player-bar {
background: none !important;
}
#player-page {
background: none !important;
}
#search-page {
#tabs {
&.stuck {
background: var(--ytmd-transparent-player-transparent-background) !important;
}
}
}
#browse-page {
#background {
display: none !important;
}
.background-gradient {
background: none !important;
}
}
}
}
/* Window Top Panel */
nav[data-ytmd-main-panel] {
background-color: transparent !important;
}
/* Video Toggle Plugin */
.av-toggle.ytmusic-av-toggle {
background-color: var(--ytmd-transparent-player-transparent-background);
}
}

View File

@ -0,0 +1,12 @@
export enum MaterialType {
MICA = 'mica',
ACRYLIC = 'acrylic',
TABBED = 'tabbed',
NONE = 'none',
}
export type TransparentPlayerConfig = {
enabled: boolean;
opacity: number;
type: MaterialType;
};