From 85ebd460ed29fbe66713b2fce65971bb2c6dd03a Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Thu, 30 Oct 2025 11:45:04 +1100 Subject: [PATCH] feat: Add progress bar to spotify widget --- blueprints/spotify.py | 30 ++------------- templates/index.html | 90 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 26 deletions(-) diff --git a/blueprints/spotify.py b/blueprints/spotify.py index 7e07e03..677e0ca 100644 --- a/blueprints/spotify.py +++ b/blueprints/spotify.py @@ -93,30 +93,7 @@ def callback(): @spotify_bp.route("/currently-playing") def currently_playing(): """Public endpoint showing your current track.""" - token = refresh_access_token() - if not token: - return json_response(request, {"error": "Failed to refresh access token"}, 500) - - headers = {"Authorization": f"Bearer {token}"} - response = requests.get(SPOTIFY_CURRENTLY_PLAYING_URL, headers=headers) - - if response.status_code == 204: - return json_response(request, {"message": "Nothing is currently playing."}, 200) - elif response.status_code != 200: - return json_response(request, {"error": "Spotify API error", "status": response.status_code}, response.status_code) - - data = response.json() - if not data.get("item"): - return json_response(request, {"message": "Nothing is currently playing."}, 200) - - - track = { - "song_name": data["item"]["name"], - "artist": ", ".join([artist["name"] for artist in data["item"]["artists"]]), - "album_name": data["item"]["album"]["name"], - "album_art": data["item"]["album"]["images"][0]["url"], - "is_playing": data["is_playing"] - } + track = get_spotify_track() return json_response(request, {"spotify":track}, 200) def get_spotify_track(): @@ -137,12 +114,13 @@ def get_spotify_track(): if not data.get("item"): return {"error": "Nothing is currently playing."} - track = { "song_name": data["item"]["name"], "artist": ", ".join([artist["name"] for artist in data["item"]["artists"]]), "album_name": data["item"]["album"]["name"], "album_art": data["item"]["album"]["images"][0]["url"], - "is_playing": data["is_playing"] + "is_playing": data["is_playing"], + "progress_ms": data.get("progress_ms",0), + "duration_ms": data["item"].get("duration_ms",1) } return track \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 46ea342..ab06300 100644 --- a/templates/index.html +++ b/templates/index.html @@ -362,6 +362,21 @@ Check them out here! + +
+
+
@@ -405,6 +420,15 @@ widget.addEventListener('click', (e) => { e.stopPropagation(); }); +// Variable to track progress bar animation +let progressInterval = null; +let progressSpeed = 0; +let lastUpdateTime = Date.now(); +let currentProgress = 0; +let targetProgress = 0; +let trackDuration = 0; +let currentTrackId = null; + // --- Spotify fetch --- async function updateSpotifyWidget() { try { @@ -423,6 +447,11 @@ async function updateSpotifyWidget() { document.getElementById('spotify-song').textContent = 'Not Playing'; document.getElementById('spotify-artist').textContent = ''; document.getElementById('spotify-album').textContent = ''; + document.getElementById('spotify-progress').style.width = '0%'; + clearInterval(progressInterval); + progressInterval = null; + currentProgress = 0; + currentTrackId = null; return; } @@ -432,6 +461,23 @@ async function updateSpotifyWidget() { if (!document.getElementById('spotify-song').textContent) { firstLoad = true; } + + // Check if track has changed (new song started) + const trackId = track.song_name + track.artist; // Simple track identifier + const isNewTrack = currentTrackId !== null && currentTrackId !== trackId; + + if (isNewTrack) { + // Reset progress bar instantly for new track + currentProgress = 0; + document.getElementById('spotify-progress').style.transition = 'none'; + document.getElementById('spotify-progress').style.width = '0%'; + // Force reflow to apply the instant reset + document.getElementById('spotify-progress').offsetHeight; + // Re-enable transition + document.getElementById('spotify-progress').style.transition = 'width 0.1s linear'; + } + + currentTrackId = trackId; document.getElementById('spotify-album-art').src = track.album_art; @@ -439,6 +485,26 @@ async function updateSpotifyWidget() { document.getElementById('spotify-artist').textContent = track.artist; document.getElementById('spotify-album').textContent = track.album_name; + // Update progress bar + if (track.is_playing) { + currentProgress = (track.progress_ms / track.duration_ms) * 100; + trackDuration = track.duration_ms; + lastUpdateTime = Date.now(); + + document.getElementById('spotify-progress').style.width = currentProgress + '%'; + + // Clear existing interval + if (progressInterval) { + clearInterval(progressInterval); + } + // Start interval to animate progress bar + progressInterval = setInterval(animateProgressBar, 100); + } else { + document.getElementById('spotify-progress').style.width = currentProgress + '%'; + clearInterval(progressInterval); + progressInterval = null; + } + // If first load and desktop, slide in the widget if (firstLoad) { updateVisibility(); @@ -449,6 +515,30 @@ async function updateSpotifyWidget() { } } +// Animate progress bar +function animateProgressBar() { + if (trackDuration === 0) return; + + const now = Date.now(); + const elapsed = now - lastUpdateTime; + lastUpdateTime = now; + + // Calculate progress increment based on elapsed time + const progressIncrement = (elapsed / trackDuration) * 100; + currentProgress += progressIncrement; + + if (currentProgress >= 100) { + currentProgress = 100; + document.getElementById('spotify-progress').style.width = '100%'; + clearInterval(progressInterval); + progressInterval = null; + // Refresh API when progress reaches 100% + setTimeout(updateSpotifyWidget, 500); + } else { + document.getElementById('spotify-progress').style.width = currentProgress + '%'; + } +} + // Wait for Spotify API to have responded before initial display updateSpotifyWidget();