diff --git a/src/player.ts b/src/player.ts index ed70e6e..0502ed1 100644 --- a/src/player.ts +++ b/src/player.ts @@ -27,6 +27,13 @@ const BITRATE_MAP: Record = { low: "128000", }; +const VOLUME_ICONS: Record = { + high: ``, + medium: ``, + low: ``, + muted: ``, +}; + // Stop Jellfin audio export function stop() { hijackActive.set(false); @@ -117,7 +124,7 @@ export function registerEvents() { const results = new Fuse(list, { keys: ["name", "artists"], - threshold: 0.7, + threshold: 0.75, }).search(`${trackName} ${artists}`); console.debug(`[Jellyfin]: Query is "${trackName} ${artists}"`); @@ -188,6 +195,7 @@ export function registerEvents() { oldTime = event.data; }); + const volumeIcon: SVGElement | null = document.querySelector(".volume-bar__icon-button > span > svg"); const volumeSlider: HTMLDivElement | null = document.querySelector(".volume-bar__slider-container > div > div"); const volumeSliderInput: HTMLInputElement | null = document.querySelector(".volume-bar__slider-container > div > label > input"); @@ -203,6 +211,7 @@ export function registerEvents() { audio.volume = Math.pow(currentVolume, 3) * 0.425; if (volumeSlider) volumeSlider.style.setProperty("--progress-bar-transform", `${currentVolume * 100}%`); if (volumeSliderInput) volumeSliderInput.value = currentVolume.toString(); + if (volumeIcon) volumeIcon.innerHTML = VOLUME_ICONS[getExpectedVolumeIcon()]; return; } return Reflect.apply(target, thisArg, args); @@ -210,31 +219,63 @@ export function registerEvents() { }); // Spotify tries to set the volume on the slider to 0 when hijacked, this tries to revert it - if (!volumeSlider) return; - const observer = new MutationObserver(() => { - const transform = volumeSlider.style.getPropertyValue("--progress-bar-transform"); + if (volumeSlider) { + const observer = new MutationObserver(() => { + if (!hijackActive.get()) return; + const transform = volumeSlider.style.getPropertyValue("--progress-bar-transform"); - const currentPercent = currentVolume * 100; - const transformPercent = parseFloat(transform); // strips the "%" + const currentPercent = currentVolume * 100; + const transformPercent = parseFloat(transform); // strips the "%" - // 0.1% tolerance (floating point) - if (Math.abs(currentPercent - transformPercent) > 0.1) { - observer.disconnect(); // prevent re-triggering while we update - volumeSlider.style.setProperty("--progress-bar-transform", `${currentPercent}%`); - observer.observe(volumeSlider, { attributes: true, attributeFilter: ["style"] }); - } - }); - observer.observe(volumeSlider, { attributes: true, attributeFilter: ["style"] }); + // 0.1% tolerance (floating point) + if (Math.abs(currentPercent - transformPercent) > 0.1) { + observer.disconnect(); // prevent re-triggering while we update + volumeSlider.style.setProperty("--progress-bar-transform", `${currentPercent}%`); + observer.observe(volumeSlider, { attributes: true, attributeFilter: ["style"] }); + } + }); + + observer.observe(volumeSlider, { attributes: true, attributeFilter: ["style"] }); + } // Similar to the other observer, but for the input (you'll notice it when scrolling the volume slider) - if (!volumeSliderInput) return; - const inputObserver = new MutationObserver(() => { - // 0.1% tolerance (floating point) - if (Math.abs(currentVolume - volumeSliderInput.valueAsNumber) > 0.1) { - inputObserver.disconnect(); // prevent re-triggering while we update - volumeSliderInput.value = currentVolume.toString(); - inputObserver.observe(volumeSlider, { attributes: true, attributeFilter: ["value"] }); - } - }); - inputObserver.observe(volumeSlider, { attributes: true, attributeFilter: ["value"] }); + if (volumeSliderInput) { + const inputObserver = new MutationObserver(() => { + if (!hijackActive.get()) return; + + // 0.1% tolerance (floating point) + if (Math.abs(currentVolume - volumeSliderInput.valueAsNumber) > 0.1) { + inputObserver.disconnect(); // prevent re-triggering while we update + volumeSliderInput.value = currentVolume.toString(); + inputObserver.observe(volumeSliderInput, { attributes: true, attributeFilter: ["value"] }); + } + }); + + inputObserver.observe(volumeSliderInput, { attributes: true, attributeFilter: ["value"] }); + } + + // Similar, but for volume icon (tries to show up as muted) + if (volumeIcon) { + let currentIcon = ""; + const observer = new MutationObserver(() => { + if (!hijackActive.get()) return; + + const expectedIcon = getExpectedVolumeIcon(); + if (currentIcon === expectedIcon) return; + + observer.disconnect(); // prevent re-triggering while we update + currentIcon = expectedIcon; + volumeIcon.innerHTML = VOLUME_ICONS[getExpectedVolumeIcon()]; + observer.observe(volumeIcon, { childList: true, subtree: true }); + }); + + observer.observe(volumeIcon, { childList: true, subtree: true }); + } +} + +function getExpectedVolumeIcon(): string { + if (currentVolume >= 0.66) return "high"; + if (currentVolume >= 0.33) return "medium"; + if (currentVolume !== 0) return "low"; + return "muted"; }