Merge pull request #15 from meowarex/dev

Fixed Quality Matched Seek Bar
This commit is contained in:
Meow Meow
2025-06-03 19:34:31 +10:00
committed by GitHub
3 changed files with 150 additions and 44 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ export const Settings = () => {
<LunaSettings> <LunaSettings>
<LunaSwitchSetting <LunaSwitchSetting
title="Quality Color Matched Seek Bar" title="Quality Color Matched Seek Bar"
desc="Apply Tidals Default color styling for the seek bar for color matching with song quality" desc="Color the Seek/Progress Bar based on audio quality"
checked={qualityColorMatchedSeekBar} checked={qualityColorMatchedSeekBar}
onChange={(_, checked) => { onChange={(_, checked) => {
console.log("Quality Color Matched Seek Bar:", checked ? "enabled" : "disabled"); console.log("Quality Color Matched Seek Bar:", checked ? "enabled" : "disabled");
+85 -3
View File
@@ -1,5 +1,5 @@
import { LunaUnload, Tracer } from "@luna/core"; import { LunaUnload, Tracer } from "@luna/core";
import { StyleTag } from "@luna/lib"; import { StyleTag, observePromise, PlayState, Quality, type MediaItem } from "@luna/lib";
import { settings, Settings } from "./Settings"; import { settings, Settings } from "./Settings";
// Import CSS files directly using Luna's file:// syntax // Import CSS files directly using Luna's file:// syntax
@@ -17,26 +17,107 @@ export const unloads = new Set<LunaUnload>();
// StyleTag instance for theme management // StyleTag instance for theme management
const themeStyleTag = new StyleTag("OLED-Theme", unloads); const themeStyleTag = new StyleTag("OLED-Theme", unloads);
// Quality color mapping
const QUALITY_COLORS = {
MAX: "#FED330", // Max/HiFi
HIGH: "#31FFEE", // High
LOW: "#FFFFFE" // Low
};
// Function to get quality color based on audio quality
const getQualityColor = (audioQuality: string): string => {
const quality = audioQuality?.toUpperCase();
if (quality?.includes("HI_RES_LOSSLESS")) {
return QUALITY_COLORS.MAX;
} else if (quality?.includes("LOSSLESS")) {
return QUALITY_COLORS.HIGH;
} else {
return QUALITY_COLORS.LOW;
}
};
// Function to Reset Seek Bar Color (if setting gets disabled while playing)
const resetSeekBarColor = async (): Promise<void> => {
try {
const progressBarWrapper = await observePromise<HTMLElement>(unloads, `[class^="_progressBarWrapper"]`);
if (!progressBarWrapper) return;
progressBarWrapper.style.removeProperty('color');
progressBarWrapper.querySelectorAll('[class*="progress"], [class*="bar"]').forEach(el => {
if (el instanceof HTMLElement) el.style.removeProperty('color');
});
} catch (error) {
trace.msg.err(`Failed to reset seek bar color: ${error}`);
}
};
// Function to apply quality-based seek bar coloring (if enabled)
const applyQualityColors = async (): Promise<void> => {
if (!settings.qualityColorMatchedSeekBar) return;
try {
const progressBarWrapper = await observePromise<HTMLElement>(unloads, `[class^="_progressBarWrapper"]`);
if (!progressBarWrapper) return;
const audioQuality = PlayState.playbackContext?.actualAudioQuality;
if (!audioQuality) return;
const qualityColor = getQualityColor(audioQuality);
progressBarWrapper.style.setProperty('color', qualityColor, 'important');
progressBarWrapper.querySelectorAll('[class*="progress"], [class*="bar"]').forEach(el => {
if (el instanceof HTMLElement) el.style.setProperty('color', qualityColor, 'important');
});
//trace.msg.log(`Applied quality color ${qualityColor}`);
} catch (error) {
trace.msg.err(`Failed to apply quality colors: ${error}`);
}
};
// Function to monitor track changes using track ID
const setupQualityMonitoring = (): void => {
let lastTrackId: string | null = null;
const interval = setInterval(() => {
if (!settings.qualityColorMatchedSeekBar) return;
const currentTrackId = PlayState.playbackContext?.actualProductId;
if (currentTrackId && currentTrackId !== lastTrackId) {
//trace.msg.log(`[OLED Theme] Track ID changed: ${lastTrackId} -> ${currentTrackId}`);
lastTrackId = currentTrackId;
applyQualityColors();
}
}, 250);
unloads.add(() => clearInterval(interval));
// Initial color application (if a track is already loaded)
const currentTrackId = PlayState.playbackContext?.actualProductId;
if (settings.qualityColorMatchedSeekBar && currentTrackId) {
lastTrackId = currentTrackId;
applyQualityColors();
}
};
// Function to apply theme styles based on current settings // Function to apply theme styles based on current settings
const applyThemeStyles = function(): void { const applyThemeStyles = function(): void {
// Choose the appropriate CSS file based on settings // Choose the appropriate CSS file based on settings
let selectedStyle: string; let selectedStyle: string;
if (settings.lightMode) { if (settings.lightMode) {
// Light mode always uses the full light theme (OLED friendly doesn't apply to light theme) // Light mode - (OLED friendly doesn't apply to light theme)
selectedStyle = lightTheme; selectedStyle = lightTheme;
} else { } else {
// Dark mode - choose between full dark theme or OLED friendly version // Dark mode
selectedStyle = settings.oledFriendlyButtons ? oledFriendlyTheme : darkTheme; selectedStyle = settings.oledFriendlyButtons ? oledFriendlyTheme : darkTheme;
} }
// Remove SeekBar coloring if Quality Color Matched Seek Bar is enabled // Remove SeekBar coloring if Quality Color Matched Seek Bar is enabled
// This allows our manual coloring to take precedence
if (settings.qualityColorMatchedSeekBar) { if (settings.qualityColorMatchedSeekBar) {
selectedStyle = selectedStyle.replace(/\[class\^="_progressBarWrapper"\]\s*\{[^}]*\}/g, ''); selectedStyle = selectedStyle.replace(/\[class\^="_progressBarWrapper"\]\s*\{[^}]*\}/g, '');
setupQualityMonitoring();
} else {
// If disabling, reset the seek bar color
resetSeekBarColor();
} }
// Apply the selected theme using StyleTag // Apply the selected theme using StyleTag
themeStyleTag.css = selectedStyle; themeStyleTag.css = selectedStyle;
}; };
// Make this function available globally so Settings can call it // Make this function available globally so Settings can call it
@@ -44,3 +125,4 @@ const applyThemeStyles = function(): void {
// Apply the OLED theme initially // Apply the OLED theme initially
applyThemeStyles(); applyThemeStyles();
+63 -39
View File
@@ -129,13 +129,7 @@
color: #333333; color: #333333;
} }
/* Override button span color specifically for play/shuffle buttons */
button[data-test="play-all"] [class^="button"]>span,
button[data-test="shuffle-all"] [class^="button"]>span,
button[data-test="play-all"] span,
button[data-test="shuffle-all"] span {
color: white !important;
}
[class^="_explicitBadge"] { [class^="_explicitBadge"] {
color: var(--wave-color-solid-accent-fill); color: var(--wave-color-solid-accent-fill);
@@ -292,42 +286,72 @@ button[data-test="close-now-playing"]:hover {
margin-top: 7.5px; margin-top: 7.5px;
} }
/* Play and Shuffle buttons - ultra aggressive targeting */ /* Button styling using proper light theme approach */
button[data-test="play-all"], :root {
button[data-test="shuffle-all"], --button-light: #d9d9d9 !important;
div[data-test="play-all"], --button-medium: #cbcbcb !important;
div[data-test="shuffle-all"] {
background-color: #666666 !important;
color: white !important;
border-radius: 12px !important;
} }
/* Target all possible text and icon elements */ /*buttons*/
button[data-test="play-all"] *, ._activeTab_f47dafa {
button[data-test="shuffle-all"] *, background: #0000001c;
div[data-test="play-all"] *,
div[data-test="shuffle-all"] *,
[data-test="play-all"] span,
[data-test="shuffle-all"] span,
[data-test="play-all"] svg,
[data-test="shuffle-all"] svg,
[data-test="play-all"] path,
[data-test="shuffle-all"] path,
[data-test="play-all"] div,
[data-test="shuffle-all"] div,
[data-test="play-all"] button,
[data-test="shuffle-all"] button {
color: white !important;
fill: white !important;
background-color: transparent !important;
} }
/* Override any CSS variable usage */ /*canvas nav buttons*/
[data-test="play-all"], .viewAllButton--Nb87U,
[data-test="shuffle-all"] { .css-7l8ggf {
--wave-color-solid-accent-fill: white !important; background: #e0e0e0;
--wave-color-solid-contrast-fill: white !important; }
--wave-text-body-medium: white !important;
.viewAllButton--Nb87U:hover,
.css-7l8ggf:hover {
background: #cbcbcb;
}
/*tracks page*/
.variantPrimary--pjymy,
._button_3357ce6 {
background-color: var(--button-light);
}
._button_f1c7fcb {
background: var(--wave-color-solid-base-brighter);
}
._button_84b8ffe {
background-color: var(--wave-color-solid-base-brighter);
}
._button_84b8ffe:hover {
background-color: var(--wave-color-solid-base-brightest);
}
.button--_0I_t {
background-color: var(--button-light);
}
.button--_0I_t:hover {
background-color: var(--wave-color-opacity-contrast-fill-regular);
}
._button_94c5125 {
background: var(--wave-color-solid-base-brighter);
}
.primary--NLSX4 {
background-color: #d5d5d5;
}
.primary--NLSX4:hover {
background-color: var(--wave-color-opacity-contrast-fill-regular) !important;
}
.primary--NLSX4:disabled {
background-color: #e7e7e8;
}
.primary--NLSX4:disabled:hover {
background-color: #e7e7e8;
} }
[class^="__NEPTUNE_PAGE"], [class^="__NEPTUNE_PAGE"],