Big refactor

This commit is contained in:
nutty
2026-07-01 04:14:21 +10:00
parent 3fe4ee5051
commit a39e05147c
13 changed files with 206 additions and 725 deletions
+16 -33
View File
@@ -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;
const totalSeconds = Math.floor(time / 1000);
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
// 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);
// Format seconds with a leading zero
const paddedSeconds = ('0' + seconds).slice(-2);
// // Convert RGB array to Hex
// const hexPalette = palette.map(([r, g, b]) => {
// return "#" + ((1 << 24) + (r << 16) + (g << 8) + b)
// .toString(16)
// .slice(1);
// });
if (hours > 0) {
// Format minutes with a leading zero if hours are present
const paddedMinutes = ('0' + minutes).slice(-2);
return `${hours}:${paddedMinutes}:${paddedSeconds}`;
}
// resolve(hexPalette);
// };
// img.onerror = () => resolve(["#ffffff"]);
// });
// }
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');
+91 -19
View File
@@ -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`;
}
}
+20 -112
View File
@@ -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%`;
-1
View File
@@ -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;
+20 -112
View File
@@ -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`;
-1
View File
@@ -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;
+17 -109
View File
@@ -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";
@@ -211,3 +102,20 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
}, 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}%)`
}
+3 -105
View File
@@ -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`;
-1
View File
@@ -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;
+24 -123
View File
@@ -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();
// });
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;
}
}
-1
View File
@@ -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;
+14 -106
View File
@@ -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";
@@ -165,3 +59,17 @@ async function ChangeTrack(mediaProps) {
}, 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;
}
-1
View File
@@ -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;