diff --git a/.common/utils/helpers.js b/.common/utils/helpers.js index d0f1789..20a30d8 100644 --- a/.common/utils/helpers.js +++ b/.common/utils/helpers.js @@ -413,42 +413,25 @@ async function LoadHTMLTemplate(name) { } } -// Calculate an accent color given an image -// async function GetAccentPalette(imageUrl, count = 20) { -// if (typeof ColorThief === 'undefined') { -// await new Promise((resolve, reject) => { -// const script = document.createElement('script'); -// script.src = "https://cdnjs.cloudflare.com/ajax/libs/color-thief/2.3.2/color-thief.min.js"; -// script.onload = resolve; -// script.onerror = reject; -// document.head.appendChild(script); -// }); -// } +function ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(time) { + if (isNaN(time) || time <= 0) return "0:00"; -// return new Promise((resolve) => { -// const img = new Image(); -// img.crossOrigin = "Anonymous"; -// img.src = imageUrl; - -// img.onload = () => { -// const colorThief = new ColorThief(); -// // Clamp count between 2 and 20 to ensure algorithm stability -// const safeCount = Math.max(2, Math.min(20, count)); -// const palette = colorThief.getPalette(img, safeCount); + const totalSeconds = Math.floor(time / 1000); + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = totalSeconds % 60; -// // Convert RGB array to Hex -// const hexPalette = palette.map(([r, g, b]) => { -// return "#" + ((1 << 24) + (r << 16) + (g << 8) + b) -// .toString(16) -// .slice(1); -// }); - -// resolve(hexPalette); -// }; + // Format seconds with a leading zero + const paddedSeconds = ('0' + seconds).slice(-2); -// img.onerror = () => resolve(["#ffffff"]); -// }); -// } + if (hours > 0) { + // Format minutes with a leading zero if hours are present + const paddedMinutes = ('0' + minutes).slice(-2); + return `${hours}:${paddedMinutes}:${paddedSeconds}`; + } + + return `${minutes}:${paddedSeconds}`; +} async function GetAccentPalette(imageUrl) { // 1. Dynamic Loader: Load Vibrant.js @@ -487,7 +470,7 @@ async function GetAccentPalette(imageUrl) { } // Generic popup -function showPopup(iconSrc, title, subtitle, attribute, background, button) { +function SplashscreenPopup(iconSrc, title, subtitle, attribute, background, button) { // 1. Check if a popup is already on screen const existingOverlay = document.getElementById('global-common-overlay'); diff --git a/now-playing/script.js b/now-playing/script.js index e45f5b4..5b9777c 100644 --- a/now-playing/script.js +++ b/now-playing/script.js @@ -5,6 +5,12 @@ const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); +//////////////// +// CONSTANTS // +//////////////// + +const REQUIRED_VERSION = '0.0.1'; +const SMTC_BRIDGE_DOWNLOAD_URL = 'https://github.com/nuttylmao/smtc-bridge/releases'; /////////////////// // PAGE ELEMENTS // @@ -37,13 +43,11 @@ const hideAnimation = urlParams.get('hideAnimation') || 'slide-out-bottom'; // GLOBAL VARS // ///////////////// -const REQUIRED_VERSION = '0.0.1'; -const SMTC_BRIDGE_DOWNLOAD_URL = 'https://github.com/nuttylmao/smtc-bridge/releases'; let smtcBridgePopup = null; let versionCheckPopup = null; let skipVersionCheck = false; let CurrentPlaybackStatus; -let CurrentSong; +let CurrentSongKey; let hideTimeout = null; //////////////// @@ -178,7 +182,7 @@ window.addEventListener('DOMContentLoaded', () => { //////////// function ShowWaitingForSMTCBridgePopup() { - const newPopup = showPopup( + const newPopup = SplashscreenPopup( '/.common/resources/smtc-bridge-icon.png', 'Waiting for SMTC Bridge', 'Please launch SMTC Bridge', @@ -199,7 +203,7 @@ function ShowWaitingForSMTCBridgePopup() { function ShowSMTCBridgeUpdateRequiredPopup(installedVersion) { - const newPopup = showPopup( + const newPopup = SplashscreenPopup( '/.common/resources/smtc-bridge-icon.png', 'Update Required', 'Your version of SMTC Bridge is out of date.', @@ -219,7 +223,7 @@ function ShowSMTCBridgeUpdateRequiredPopup(installedVersion) { } function ShowSMTCBridgeUpdateAvailablePopup(installedVersion) { - const newPopup = showPopup( + const newPopup = SplashscreenPopup( '/.common/resources/smtc-bridge-icon.png', 'Update Available', 'A new version of SMTC Bridge is available.', @@ -286,22 +290,90 @@ function CheckSMTCBridgeVersion(installedVersion) { } } -function ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(time) { - if (isNaN(time) || time <= 0) return "0:00"; +// Each theme must implement the following +async function UpdatePlayerState(data) { + // Check if the user has provided a target application in the settings + const isFiltering = targetApplication && targetApplication.trim() !== ""; - const totalSeconds = Math.floor(time / 1000); - const hours = Math.floor(totalSeconds / 3600); - const minutes = Math.floor((totalSeconds % 3600) / 60); - const seconds = totalSeconds % 60; + // Search for the session that matches the target application + const sessionToFind = isFiltering ? targetApplication : data.current_session_id; + let targetSession = data.sessions.find(s => { + // If filtering, check if the source_app_id matches the user's string + if (isFiltering) { + return s.source_app_id.toLowerCase() === targetApplication.toLowerCase(); + } + // Otherwise, match the system's current active session ID + return s.source_app_id === sessionToFind; + }); - // Format seconds with a leading zero - const paddedSeconds = ('0' + seconds).slice(-2); + // If a target session was found, update the state of the widget + if (targetSession) { + // Extract the relevant properties from the session + const playbackInfo = targetSession.playback_info; + const mediaProps = targetSession.media_properties; + const timelineProps = targetSession.timeline_properties; - if (hours > 0) { - // Format minutes with a leading zero if hours are present - const paddedMinutes = ('0' + minutes).slice(-2); - return `${hours}:${paddedMinutes}:${paddedSeconds}`; + // Calcualte an accent color + const accentColorPalette = await GetAccentPalette(mediaProps.Thumbnail); + + // 1. Check if playback status has changed and update visibility accordingly + if (playbackInfo.PlaybackStatus !== CurrentPlaybackStatus) { + if (playbackInfo.PlaybackStatus === PlaybackStatus.PLAYING) + SetVisibility(true); + else + SetVisibility(false); + CurrentPlaybackStatus = playbackInfo.PlaybackStatus; + } + + + // 2. Check if the track name/artist have changed - this is our indicator that the next track has loaded + // Only proceed if the player state is actively playing audio + if (CurrentPlaybackStatus == PlaybackStatus.PLAYING) { + const newTrackKey = `${mediaProps.Title}|${mediaProps.Artist}`; + if (newTrackKey !== CurrentSongKey) { + ChangeTrack(mediaProps, accentColorPalette); // Now trigger your cross-fade logic here! + CurrentSongKey = newTrackKey; // Update the tracker with the string key + } + } + + // 3. Update the progress info + if (timelineProps) { + // Parse the Windows timestamp into a JavaScript time object + const lastUpdateAnchor = Date.parse(timelineProps.LastUpdatedTime.replace(' ', 'T')); + + // Calculate the drift (i.e. how many milliseconds have passed since Windows last updated) + const driftMs = Date.now() - lastUpdateAnchor; + + // Add that drift to the reported Position + // Only add drift if the status is PLAYING + const isPlaying = (targetSession.playback_info.PlaybackStatus === PlaybackStatus.PLAYING); + const currentPositionMs = isPlaying && timelineProps.EndTime > 0 ? timelineProps.Position + driftMs : timelineProps.Position; + + SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette); + } + } + else { + SetVisibility(false); + } +} + +function SetVisibility(visible) { + // Always clear any pending hide timers whenever we change visibility + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; } - return `${minutes}:${paddedSeconds}`; + if (visible) { + mainWrapper.style.animation = `${showAnimation} 0.5s ease-out forwards`; + + // Only set a new timer if autoHide is enabled + if (autoHide) { + hideTimeout = setTimeout(() => { + SetVisibility(false); + }, displayDuration * 1000); + } + } else { + mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`; + } } \ No newline at end of file diff --git a/now-playing/themes/card/script.js b/now-playing/themes/card/script.js index 3263bb6..762360c 100644 --- a/now-playing/themes/card/script.js +++ b/now-playing/themes/card/script.js @@ -42,116 +42,7 @@ if (!showProgressBar) // CORE FUNCTIONS // //////////////////// -// Each theme must implement the following -async function UpdatePlayerState(data) { - // Check if the user has provided a target application in the settings - const isFiltering = targetApplication && targetApplication.trim() !== ""; - - // Search for the session that matches the target application - const sessionToFind = isFiltering ? targetApplication : data.current_session_id; - let targetSession = data.sessions.find(s => { - // If filtering, check if the source_app_id matches the user's string - if (isFiltering) { - return s.source_app_id.toLowerCase() === targetApplication.toLowerCase(); - } - // Otherwise, match the system's current active session ID - return s.source_app_id === sessionToFind; - }); - - // If a target session was found, update the state of the widget - if (targetSession) { - // Extract the relevant properties from the session - const playbackInfo = targetSession.playback_info; - const mediaProps = targetSession.media_properties; - const timelineProps = targetSession.timeline_properties; - - // Calcualte an accent color - const accentColorPalette = await GetAccentPalette(mediaProps.Thumbnail); - - // 1. Check if playback status has changed and update visibility accordingly - if (playbackInfo.PlaybackStatus !== CurrentPlaybackStatus) { - if (playbackInfo.PlaybackStatus === PlaybackStatus.PLAYING) - SetVisibility(true); - else - SetVisibility(false); - CurrentPlaybackStatus = playbackInfo.PlaybackStatus; - } - - // 2. Check if the track name/artist have changed - this is our indicator that the next track has loaded - // Only proceed if the player state is actively playing audio - if (CurrentPlaybackStatus == PlaybackStatus.PLAYING) - { - const newTrackKey = `${mediaProps.Title}|${mediaProps.Artist}`; - if (newTrackKey !== CurrentSong) { - ChangeTrack(mediaProps, accentColorPalette.LightVibrant); // Now trigger your cross-fade logic here! - CurrentSong = newTrackKey; // Update the tracker with the string key - } - } - - // 3. Update the progress info - if (timelineProps) - { - // Parse the Windows timestamp into a JavaScript time object - const lastUpdateAnchor = Date.parse(timelineProps.LastUpdatedTime.replace(' ', 'T')); - - // Calculate the drift (i.e. how many milliseconds have passed since Windows last updated) - const driftMs = Date.now() - lastUpdateAnchor; - - // Add that drift to the reported Position - // Only add drift if the status is PLAYING - const isPlaying = (targetSession.playback_info.PlaybackStatus === PlaybackStatus.PLAYING); - const currentPositionMs = isPlaying && timelineProps.EndTime > 0 ? timelineProps.Position + driftMs : timelineProps.Position; - - // Update the label using your naming convention - currentTimeLabel.innerText = - ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(currentPositionMs); - - durationLabel.innerText = - ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(timelineProps.EndTime); - - // Set progressbar - // Ensure we don't divide by zero or exceed 100% - const durationMs = timelineProps.EndTime; - let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; - progressPercent = Math.min(100, Math.max(0, progressPercent)); - progressBarFill.style.width = `${progressPercent}%`; - progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant); - } - } - else - { - SetVisibility(false); - } -} - - - -////////////////////// -// HELPER FUNCTIONS // -////////////////////// - -function SetVisibility(visible) { - // Always clear any pending hide timers whenever we change visibility - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - - if (visible) { - mainWrapper.style.animation = `${showAnimation} 0.5s ease-out forwards`; - - // Only set a new timer if autoHide is enabled - if (autoHide) { - hideTimeout = setTimeout(() => { - SetVisibility(false); - }, displayDuration * 1000); - } - } else { - mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`; - } -} - -async function ChangeTrack(mediaProps, tintColor) { +async function ChangeTrack(mediaProps, accentColorPalette) { // Fade in the overlay (shows the new text) trackLabel.style.opacity = "0"; artistLabel.style.opacity = "0"; @@ -172,7 +63,7 @@ async function ChangeTrack(mediaProps, tintColor) { // Apply a tint: Use 30% opacity of the accent color + a dark overlay for contrast // 'rgba(0,0,0,0.6)' ensures the text stays readable - // backgroundLayer.style.backgroundColor = tintColor + "80"; // 80 is 50% opacity in hex + // backgroundLayer.style.backgroundColor = accentColorPalette.DarkMuted + "80"; // 80 is 50% opacity in hex trackLabel.style.opacity = ""; artistLabel.style.opacity = ""; @@ -186,13 +77,30 @@ async function ChangeTrack(mediaProps, tintColor) { // Apply a tint: Use 30% opacity of the accent color + a dark overlay for contrast // 'rgba(0,0,0,0.6)' ensures the text stays readable - // backgroundTransitionLayer.style.backgroundColor = tintColor + "80"; // 80 is 50% opacity in hex + // backgroundTransitionLayer.style.backgroundColor = accentColorPalette.DarkMuted + "80"; // 80 is 50% opacity in hex SetVisibility(true); // Show the overlay for a few seconds if autoHide is enabled }, 250); }, 250); } +function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) { + // Update the label using your naming convention + currentTimeLabel.innerText = + ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(currentPositionMs); + + durationLabel.innerText = + ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(timelineProps.EndTime); + + // Set progressbar + // Ensure we don't divide by zero or exceed 100% + const durationMs = timelineProps.EndTime; + let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; + progressPercent = Math.min(100, Math.max(0, progressPercent)); + progressBarFill.style.width = `${progressPercent}%`; + progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant); +} + function SetAlbumArtSize() { // const height = songInfoContainer.offsetHeight; albumArtContainer.style.height = `100%`; diff --git a/now-playing/themes/card/style.css b/now-playing/themes/card/style.css index 748d119..b7f33ad 100644 --- a/now-playing/themes/card/style.css +++ b/now-playing/themes/card/style.css @@ -7,7 +7,6 @@ width: 500px; display: flex; flex-direction: row; - /* This forces both containers to be the same height */ align-items: stretch; gap: 0.5em; opacity: 0; diff --git a/now-playing/themes/classic/script.js b/now-playing/themes/classic/script.js index 3d86bf4..453646e 100644 --- a/now-playing/themes/classic/script.js +++ b/now-playing/themes/classic/script.js @@ -42,116 +42,7 @@ if (!showProgressBar) // CORE FUNCTIONS // //////////////////// -// Each theme must implement the following -async function UpdatePlayerState(data) { - // Check if the user has provided a target application in the settings - const isFiltering = targetApplication && targetApplication.trim() !== ""; - - // Search for the session that matches the target application - const sessionToFind = isFiltering ? targetApplication : data.current_session_id; - let targetSession = data.sessions.find(s => { - // If filtering, check if the source_app_id matches the user's string - if (isFiltering) { - return s.source_app_id.toLowerCase() === targetApplication.toLowerCase(); - } - // Otherwise, match the system's current active session ID - return s.source_app_id === sessionToFind; - }); - - // If a target session was found, update the state of the widget - if (targetSession) { - // Extract the relevant properties from the session - const playbackInfo = targetSession.playback_info; - const mediaProps = targetSession.media_properties; - const timelineProps = targetSession.timeline_properties; - - // Calcualte an accent color - const accentColorPalette = await GetAccentPalette(mediaProps.Thumbnail); - - // 1. Check if playback status has changed and update visibility accordingly - if (playbackInfo.PlaybackStatus !== CurrentPlaybackStatus) { - if (playbackInfo.PlaybackStatus === PlaybackStatus.PLAYING) - SetVisibility(true); - else - SetVisibility(false); - CurrentPlaybackStatus = playbackInfo.PlaybackStatus; - } - - // 2. Check if the track name/artist have changed - this is our indicator that the next track has loaded - // Only proceed if the player state is actively playing audio - if (CurrentPlaybackStatus == PlaybackStatus.PLAYING) - { - const newTrackKey = `${mediaProps.Title}|${mediaProps.Artist}`; - if (newTrackKey !== CurrentSong) { - ChangeTrack(mediaProps, accentColorPalette.LightVibrant); // Now trigger your cross-fade logic here! - CurrentSong = newTrackKey; // Update the tracker with the string key - } - } - - // 3. Update the progress info - if (timelineProps) - { - // Parse the Windows timestamp into a JavaScript time object - const lastUpdateAnchor = Date.parse(timelineProps.LastUpdatedTime.replace(' ', 'T')); - - // Calculate the drift (i.e. how many milliseconds have passed since Windows last updated) - const driftMs = Date.now() - lastUpdateAnchor; - - // Add that drift to the reported Position - // Only add drift if the status is PLAYING - const isPlaying = (targetSession.playback_info.PlaybackStatus === PlaybackStatus.PLAYING); - const currentPositionMs = isPlaying && timelineProps.EndTime > 0 ? timelineProps.Position + driftMs : timelineProps.Position; - - // Update the label using your naming convention - currentTimeLabel.innerText = - ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(currentPositionMs); - - durationLabel.innerText = - ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(timelineProps.EndTime); - - // Set progressbar - // Ensure we don't divide by zero or exceed 100% - const durationMs = timelineProps.EndTime; - let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; - progressPercent = Math.min(100, Math.max(0, progressPercent)); - progressBarFill.style.width = `${progressPercent}%`; - progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant); - } - } - else - { - SetVisibility(false); - } -} - - - -////////////////////// -// HELPER FUNCTIONS // -////////////////////// - -function SetVisibility(visible) { - // Always clear any pending hide timers whenever we change visibility - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - - if (visible) { - mainWrapper.style.animation = `${showAnimation} 0.5s ease-out forwards`; - - // Only set a new timer if autoHide is enabled - if (autoHide) { - hideTimeout = setTimeout(() => { - SetVisibility(false); - }, displayDuration * 1000); - } - } else { - mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`; - } -} - -async function ChangeTrack(mediaProps, tintColor) { +async function ChangeTrack(mediaProps, accentColorPalette) { // Fade in the overlay (shows the new text) trackLabel.style.opacity = "0"; artistLabel.style.opacity = "0"; @@ -172,7 +63,7 @@ async function ChangeTrack(mediaProps, tintColor) { // Apply a tint: Use 30% opacity of the accent color + a dark overlay for contrast // 'rgba(0,0,0,0.6)' ensures the text stays readable - // backgroundLayer.style.backgroundColor = tintColor + "80"; // 80 is 50% opacity in hex + backgroundLayer.style.backgroundColor = accentColorPalette.DarkMuted + "80"; // 80 is 50% opacity in hex trackLabel.style.opacity = ""; artistLabel.style.opacity = ""; @@ -186,13 +77,30 @@ async function ChangeTrack(mediaProps, tintColor) { // Apply a tint: Use 30% opacity of the accent color + a dark overlay for contrast // 'rgba(0,0,0,0.6)' ensures the text stays readable - // backgroundTransitionLayer.style.backgroundColor = tintColor + "80"; // 80 is 50% opacity in hex + backgroundTransitionLayer.style.backgroundColor = aaccentColorPalette.DarkMuted + "80"; // 80 is 50% opacity in hex SetVisibility(true); // Show the overlay for a few seconds if autoHide is enabled }, 250); }, 250); } +function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) { + // Update the label using your naming convention + currentTimeLabel.innerText = + ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(currentPositionMs); + + durationLabel.innerText = + ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(timelineProps.EndTime); + + // Set progressbar + // Ensure we don't divide by zero or exceed 100% + const durationMs = timelineProps.EndTime; + let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; + progressPercent = Math.min(100, Math.max(0, progressPercent)); + progressBarFill.style.width = `${progressPercent}%`; + progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant); +} + function SetAlbumArtSize() { const height = songInfoContainer.offsetHeight; albumArtContainer.style.height = `${height}px`; diff --git a/now-playing/themes/classic/style.css b/now-playing/themes/classic/style.css index bdc5c85..9dc3d90 100644 --- a/now-playing/themes/classic/style.css +++ b/now-playing/themes/classic/style.css @@ -6,7 +6,6 @@ width: 500px; display: flex; flex-direction: row; - /* This forces both containers to be the same height */ align-items: stretch; gap: 0.5em; opacity: 0; diff --git a/now-playing/themes/compact/script.js b/now-playing/themes/compact/script.js index 1922dbd..ff5dc85 100644 --- a/now-playing/themes/compact/script.js +++ b/now-playing/themes/compact/script.js @@ -40,115 +40,6 @@ else // CORE FUNCTIONS // //////////////////// -// Each theme must implement the following -async function UpdatePlayerState(data) { - // Check if the user has provided a target application in the settings - const isFiltering = targetApplication && targetApplication.trim() !== ""; - - // Search for the session that matches the target application - const sessionToFind = isFiltering ? targetApplication : data.current_session_id; - let targetSession = data.sessions.find(s => { - // If filtering, check if the source_app_id matches the user's string - if (isFiltering) { - return s.source_app_id.toLowerCase() === targetApplication.toLowerCase(); - } - // Otherwise, match the system's current active session ID - return s.source_app_id === sessionToFind; - }); - - // If a target session was found, update the state of the widget - if (targetSession) { - // Extract the relevant properties from the session - const playbackInfo = targetSession.playback_info; - const mediaProps = targetSession.media_properties; - const timelineProps = targetSession.timeline_properties; - - // Calcualte an accent color - const accentColorPalette = await GetAccentPalette(mediaProps.Thumbnail); - - // 1. Check if playback status has changed and update visibility accordingly - if (playbackInfo.PlaybackStatus !== CurrentPlaybackStatus) { - if (playbackInfo.PlaybackStatus === PlaybackStatus.PLAYING) - SetVisibility(true); - else - SetVisibility(false); - CurrentPlaybackStatus = playbackInfo.PlaybackStatus; - } - - // 2. Check if the track name/artist have changed - this is our indicator that the next track has loaded - // Only proceed if the player state is actively playing audio - if (CurrentPlaybackStatus == PlaybackStatus.PLAYING) - { - const newTrackKey = `${mediaProps.Title}|${mediaProps.Artist}`; - if (newTrackKey !== CurrentSong) { - ChangeTrack(mediaProps, accentColorPalette); // Now trigger your cross-fade logic here! - CurrentSong = newTrackKey; // Update the tracker with the string key - } - } - - // 3. Update the progress info - if (timelineProps) - { - // Parse the Windows timestamp into a JavaScript time object - const lastUpdateAnchor = Date.parse(timelineProps.LastUpdatedTime.replace(' ', 'T')); - - // Calculate the drift (i.e. how many milliseconds have passed since Windows last updated) - const driftMs = Date.now() - lastUpdateAnchor; - - // Add that drift to the reported Position - // Only add drift if the status is PLAYING - const isPlaying = (targetSession.playback_info.PlaybackStatus === PlaybackStatus.PLAYING); - const currentPositionMs = isPlaying && timelineProps.EndTime > 0 ? timelineProps.Position + driftMs : timelineProps.Position; - - // Set progressbar - // Ensure we don't divide by zero or exceed 100% - const durationMs = timelineProps.EndTime; - let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; - progressPercent = Math.min(100, Math.max(0, progressPercent)); - - // Calculate how much needs to be hidden (the right-side offset) - const clipRight = 100 - progressPercent; - - // Update the clip-path - progressBar.style.clipPath = `inset(0 ${clipRight}% 0 0)`; - - // const clipRight = progressPercent; - // progressBar.style.maskImage = `linear-gradient(to right, black calc(${clipRight}% - 4em), transparent ${clipRight}%)` - } - } - else - { - SetVisibility(false); - } -} - - - -////////////////////// -// HELPER FUNCTIONS // -////////////////////// - -function SetVisibility(visible) { - // Always clear any pending hide timers whenever we change visibility - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - - if (visible) { - mainWrapper.style.animation = `${showAnimation} 0.5s ease-out forwards`; - - // Only set a new timer if autoHide is enabled - if (autoHide) { - hideTimeout = setTimeout(() => { - SetVisibility(false); - }, displayDuration * 1000); - } - } else { - mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`; - } -} - async function ChangeTrack(mediaProps, accentColorPalette) { // Fade in the overlay (shows the new text) songLabel.style.opacity = "0"; @@ -210,4 +101,21 @@ async function ChangeTrack(mediaProps, accentColorPalette) { SetVisibility(true); // Show the overlay for a few seconds if autoHide is enabled }, 250); }, 250); +} + +function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) { + // Set progressbar + // Ensure we don't divide by zero or exceed 100% + const durationMs = timelineProps.EndTime; + let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; + progressPercent = Math.min(100, Math.max(0, progressPercent)); + + // Calculate how much needs to be hidden (the right-side offset) + const clipRight = 100 - progressPercent; + + // Update the clip-path + progressBar.style.clipPath = `inset(0 ${clipRight}% 0 0)`; + + // const clipRight = progressPercent; + // progressBar.style.maskImage = `linear-gradient(to right, black calc(${clipRight}% - 4em), transparent ${clipRight}%)` } \ No newline at end of file diff --git a/now-playing/themes/simple/script.js b/now-playing/themes/simple/script.js index a318e97..3c01a4d 100644 --- a/now-playing/themes/simple/script.js +++ b/now-playing/themes/simple/script.js @@ -42,111 +42,6 @@ else // CORE FUNCTIONS // //////////////////// -// Each theme must implement the following -function UpdatePlayerState(data) { - // Check if the user has provided a target application in the settings - const isFiltering = targetApplication && targetApplication.trim() !== ""; - - // Search for the session that matches the target application - const sessionToFind = isFiltering ? targetApplication : data.current_session_id; - let targetSession = data.sessions.find(s => { - // If filtering, check if the source_app_id matches the user's string - if (isFiltering) { - return s.source_app_id.toLowerCase() === targetApplication.toLowerCase(); - } - // Otherwise, match the system's current active session ID - return s.source_app_id === sessionToFind; - }); - - // If a target session was found, update the state of the widget - if (targetSession) { - // Extract the relevant properties from the session - const playbackInfo = targetSession.playback_info; - const mediaProps = targetSession.media_properties; - const timelineProps = targetSession.timeline_properties; - - // 1. Check if playback status has changed and update visibility accordingly - if (playbackInfo.PlaybackStatus !== CurrentPlaybackStatus) { - if (playbackInfo.PlaybackStatus === PlaybackStatus.PLAYING) - SetVisibility(true); - else - SetVisibility(false); - CurrentPlaybackStatus = playbackInfo.PlaybackStatus; - } - - // 2. Check if the track name/artist have changed - this is our indicator that the next track has loaded - // Only proceed if the player state is actively playing audio - if (CurrentPlaybackStatus == PlaybackStatus.PLAYING) - { - const newTrackKey = `${mediaProps.Title}|${mediaProps.Artist}`; - if (newTrackKey !== CurrentSong) { - ChangeTrack(mediaProps); // Now trigger your cross-fade logic here! - CurrentSong = newTrackKey; // Update the tracker with the string key - } - } - - // // 3. Update the progress info - // if (timelineProps) - // { - // // Parse the Windows timestamp into a JavaScript time object - // const lastUpdateAnchor = Date.parse(timelineProps.LastUpdatedTime.replace(' ', 'T')); - - // // Calculate the drift (i.e. how many milliseconds have passed since Windows last updated) - // const driftMs = Date.now() - lastUpdateAnchor; - - // // Add that drift to the reported Position - // // Only add drift if the status is PLAYING - // const isPlaying = (targetSession.playback_info.PlaybackStatus === PlaybackStatus.PLAYING); - // const currentPositionMs = isPlaying && timelineProps.EndTime > 0 ? timelineProps.Position + driftMs : timelineProps.Position; - - // // Update the label using your naming convention - // currentTimeLabel.innerText = - // ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(currentPositionMs); - - // durationLabel.innerText = - // ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(timelineProps.EndTime); - - // // Set progressbar - // // Ensure we don't divide by zero or exceed 100% - // const durationMs = timelineProps.EndTime; - // let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; - // progressPercent = Math.min(100, Math.max(0, progressPercent)); - // progressBarFill.style.width = `${progressPercent}%`; - // } - } - else - { - SetVisibility(false); - } -} - - - -////////////////////// -// HELPER FUNCTIONS // -////////////////////// - -function SetVisibility(visible) { - // Always clear any pending hide timers whenever we change visibility - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - - if (visible) { - mainWrapper.style.animation = `${showAnimation} 0.5s ease-out forwards`; - - // Only set a new timer if autoHide is enabled - if (autoHide) { - hideTimeout = setTimeout(() => { - SetVisibility(false); - }, displayDuration * 1000); - } - } else { - mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`; - } -} - async function ChangeTrack(mediaProps) { // Fade in the overlay (shows the new text) trackLabel.style.opacity = "0"; @@ -189,6 +84,9 @@ async function ChangeTrack(mediaProps) { }, 250); } +function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) { +} + function SetAlbumArtSize() { const height = songInfoContainer.offsetHeight; albumArtContainer.style.height = `${1.5 * height}px`; diff --git a/now-playing/themes/simple/style.css b/now-playing/themes/simple/style.css index 8d046ad..5430809 100644 --- a/now-playing/themes/simple/style.css +++ b/now-playing/themes/simple/style.css @@ -6,7 +6,6 @@ width: 500px; display: flex; flex-direction: row; - /* This forces both containers to be the same height */ align-items: stretch; gap: 1em; opacity: 0; diff --git a/now-playing/themes/standard/script.js b/now-playing/themes/standard/script.js index 6c498a0..ca388e9 100644 --- a/now-playing/themes/standard/script.js +++ b/now-playing/themes/standard/script.js @@ -54,121 +54,6 @@ switch (themeVariant) { // CORE FUNCTIONS // //////////////////// -// Each theme must implement the following -async function UpdatePlayerState(data) { - // Check if the user has provided a target application in the settings - const isFiltering = targetApplication && targetApplication.trim() !== ""; - - // Search for the session that matches the target application - const sessionToFind = isFiltering ? targetApplication : data.current_session_id; - let targetSession = data.sessions.find(s => { - // If filtering, check if the source_app_id matches the user's string - if (isFiltering) { - return s.source_app_id.toLowerCase() === targetApplication.toLowerCase(); - } - // Otherwise, match the system's current active session ID - return s.source_app_id === sessionToFind; - }); - - // If a target session was found, update the state of the widget - if (targetSession) { - // Extract the relevant properties from the session - const playbackInfo = targetSession.playback_info; - const mediaProps = targetSession.media_properties; - const timelineProps = targetSession.timeline_properties; - - // Calcualte an accent color - const accentColorPalette = await GetAccentPalette(mediaProps.Thumbnail); - - // 1. Check if playback status has changed and update visibility accordingly - if (playbackInfo.PlaybackStatus !== CurrentPlaybackStatus) { - if (playbackInfo.PlaybackStatus === PlaybackStatus.PLAYING) - SetVisibility(true); - else - SetVisibility(false); - CurrentPlaybackStatus = playbackInfo.PlaybackStatus; - } - - // 2. Check if the track name/artist have changed - this is our indicator that the next track has loaded - // Only proceed if the player state is actively playing audio - if (CurrentPlaybackStatus == PlaybackStatus.PLAYING) { - const newTrackKey = `${mediaProps.Title}|${mediaProps.Artist}`; - if (newTrackKey !== CurrentSong) { - ChangeTrack(mediaProps, accentColorPalette); // Now trigger your cross-fade logic here! - CurrentSong = newTrackKey; // Update the tracker with the string key - } - } - - // 3. Update the progress info - if (timelineProps) { - // Parse the Windows timestamp into a JavaScript time object - const lastUpdateAnchor = Date.parse(timelineProps.LastUpdatedTime.replace(' ', 'T')); - - // Calculate the drift (i.e. how many milliseconds have passed since Windows last updated) - const driftMs = Date.now() - lastUpdateAnchor; - - // Add that drift to the reported Position - // Only add drift if the status is PLAYING - const isPlaying = (targetSession.playback_info.PlaybackStatus === PlaybackStatus.PLAYING); - const currentPositionMs = isPlaying && timelineProps.EndTime > 0 ? timelineProps.Position + driftMs : timelineProps.Position; - - // Update the label using your naming convention - currentTimeLabel.innerText = - ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(currentPositionMs); - - durationLabel.innerText = - ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(timelineProps.EndTime); - - // Set progressbar - // Ensure we don't divide by zero or exceed 100% - const durationMs = timelineProps.EndTime; - let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; - progressPercent = Math.min(100, Math.max(0, progressPercent)); - progressBarFill.style.width = `${progressPercent}%`; - progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant); - switch (themeVariant) { - case "matte": - progressBarFill.style.setProperty('--accent-color', accentColorPalette.DarkVibrant); - break; - case "matte-dark": - default: - progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant); - break; - } - } - } - else { - SetVisibility(false); - } -} - - - -////////////////////// -// HELPER FUNCTIONS // -////////////////////// - -function SetVisibility(visible) { - // Always clear any pending hide timers whenever we change visibility - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - - if (visible) { - mainWrapper.style.animation = `${showAnimation} 0.5s ease-out forwards`; - - // Only set a new timer if autoHide is enabled - if (autoHide) { - hideTimeout = setTimeout(() => { - SetVisibility(false); - }, displayDuration * 1000); - } - } else { - mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`; - } -} - async function ChangeTrack(mediaProps, accentColorPalette) { // Fade in the overlay (shows the new text) trackLabel.style.opacity = "0"; @@ -228,12 +113,28 @@ async function ChangeTrack(mediaProps, accentColorPalette) { }, 250); } -// function SetAlbumArtSize() { -// const height = songInfoContainer.offsetHeight; -// albumArtContainer.style.height = `${height}px`; -// albumArtContainer.style.width = `${height}px`; -// } +function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) { + // Update the label using your naming convention + currentTimeLabel.innerText = + ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(currentPositionMs); -// requestAnimationFrame(() => { -// SetAlbumArtSize(); -// }); \ No newline at end of file + durationLabel.innerText = + ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(timelineProps.EndTime); + + // Set progressbar + // Ensure we don't divide by zero or exceed 100% + const durationMs = timelineProps.EndTime; + let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; + progressPercent = Math.min(100, Math.max(0, progressPercent)); + progressBarFill.style.width = `${progressPercent}%`; + progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant); + switch (themeVariant) { + case "matte": + progressBarFill.style.setProperty('--accent-color', accentColorPalette.DarkVibrant); + break; + case "matte-dark": + default: + progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant); + break; + } +} \ No newline at end of file diff --git a/now-playing/themes/standard/style.css b/now-playing/themes/standard/style.css index 79c7b95..7b00cdd 100644 --- a/now-playing/themes/standard/style.css +++ b/now-playing/themes/standard/style.css @@ -7,7 +7,6 @@ width: 500px; display: flex; flex-direction: row; - /* This forces both containers to be the same height */ align-items: stretch; gap: 0.5em; opacity: 0; diff --git a/now-playing/themes/vinyl/script.js b/now-playing/themes/vinyl/script.js index 7a438f9..fd25045 100644 --- a/now-playing/themes/vinyl/script.js +++ b/now-playing/themes/vinyl/script.js @@ -38,112 +38,6 @@ if (!showProgressBar) // CORE FUNCTIONS // //////////////////// -// Each theme must implement the following -async function UpdatePlayerState(data) { - // Check if the user has provided a target application in the settings - const isFiltering = targetApplication && targetApplication.trim() !== ""; - - // Search for the session that matches the target application - const sessionToFind = isFiltering ? targetApplication : data.current_session_id; - let targetSession = data.sessions.find(s => { - // If filtering, check if the source_app_id matches the user's string - if (isFiltering) { - return s.source_app_id.toLowerCase() === targetApplication.toLowerCase(); - } - // Otherwise, match the system's current active session ID - return s.source_app_id === sessionToFind; - }); - - // If a target session was found, update the state of the widget - if (targetSession) { - // Extract the relevant properties from the session - const playbackInfo = targetSession.playback_info; - const mediaProps = targetSession.media_properties; - const timelineProps = targetSession.timeline_properties; - - // Calcualte an accent color - const accentColorPalette = await GetAccentPalette(mediaProps.Thumbnail); - - // 1. Check if playback status has changed and update visibility accordingly - if (playbackInfo.PlaybackStatus !== CurrentPlaybackStatus) { - if (playbackInfo.PlaybackStatus === PlaybackStatus.PLAYING) - SetVisibility(true); - else - SetVisibility(false); - CurrentPlaybackStatus = playbackInfo.PlaybackStatus; - } - - // 2. Check if the track name/artist have changed - this is our indicator that the next track has loaded - // Only proceed if the player state is actively playing audio - if (CurrentPlaybackStatus == PlaybackStatus.PLAYING) - { - const newTrackKey = `${mediaProps.Title}|${mediaProps.Artist}`; - if (newTrackKey !== CurrentSong) { - ChangeTrack(mediaProps); // Now trigger your cross-fade logic here! - CurrentSong = newTrackKey; // Update the tracker with the string key - } - } - - // 3. Update the progress info - if (timelineProps) - { - // Parse the Windows timestamp into a JavaScript time object - const lastUpdateAnchor = Date.parse(timelineProps.LastUpdatedTime.replace(' ', 'T')); - - // Calculate the drift (i.e. how many milliseconds have passed since Windows last updated) - const driftMs = Date.now() - lastUpdateAnchor; - - // Add that drift to the reported Position - // Only add drift if the status is PLAYING - const isPlaying = (targetSession.playback_info.PlaybackStatus === PlaybackStatus.PLAYING); - const currentPositionMs = isPlaying && timelineProps.EndTime > 0 ? timelineProps.Position + driftMs : timelineProps.Position; - - // Set progressbar - // Ensure we don't divide by zero or exceed 100% - const durationMs = timelineProps.EndTime; - progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; - progressPercent = Math.min(100, Math.max(0, progressPercent)); - document.documentElement.style.setProperty('--accent-color', `${accentColorPalette.LightVibrant}`); - - // Calculate the offset. - // 0% progress = 157.1 offset. 100% progress = 0 offset. - const offset = circumference - (progressPercent / 100) * circumference; - progressCircle.style.strokeDashoffset = offset; - } - } - else - { - SetVisibility(false); - } -} - - - -////////////////////// -// HELPER FUNCTIONS // -////////////////////// - -function SetVisibility(visible) { - // Always clear any pending hide timers whenever we change visibility - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - - if (visible) { - mainWrapper.style.animation = `${showAnimation} 0.5s ease-out forwards`; - - // Only set a new timer if autoHide is enabled - if (autoHide) { - hideTimeout = setTimeout(() => { - SetVisibility(false); - }, displayDuration * 1000); - } - } else { - mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`; - } -} - async function ChangeTrack(mediaProps) { // // Fade in the overlay (shows the new text) albumArtLayer.style.opacity = "0"; @@ -164,4 +58,18 @@ async function ChangeTrack(mediaProps) { SetVisibility(true); // Show the overlay for a few seconds if autoHide is enabled }, 250); }, 250); +} + +function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) { + // Set progressbar + // Ensure we don't divide by zero or exceed 100% + const durationMs = timelineProps.EndTime; + progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0; + progressPercent = Math.min(100, Math.max(0, progressPercent)); + document.documentElement.style.setProperty('--accent-color', `${accentColorPalette.LightVibrant}`); + + // Calculate the offset. + // 0% progress = 157.1 offset. 100% progress = 0 offset. + const offset = circumference - (progressPercent / 100) * circumference; + progressCircle.style.strokeDashoffset = offset; } \ No newline at end of file diff --git a/now-playing/themes/vinyl/style.css b/now-playing/themes/vinyl/style.css index e9bd003..cce72c6 100644 --- a/now-playing/themes/vinyl/style.css +++ b/now-playing/themes/vinyl/style.css @@ -4,7 +4,6 @@ width: 500px; display: flex; flex-direction: row; - /* This forces both containers to be the same height */ gap: 1em; opacity: 0; align-items: center;