mirror of
https://github.com/meowarex/TidaLuna-Plugins.git
synced 2026-06-18 03:43:10 +10:00
Merge pull request #15 from meowarex/dev
Fixed Quality Matched Seek Bar
This commit is contained in:
@@ -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");
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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"],
|
||||||
|
|||||||
Reference in New Issue
Block a user