fuzzyworld

hawk / commit

Added control to show the current playing song and a way to play/pause.

cc1af884a1c420b905e907b6a58cb4c9b57e1dd0 · Pavel Safronov · 2023-10-13 13:47:42 -0700 · parents 71213149b50e

Added control to show the current playing song and a way to play/pause.
Slightly reorganized the landing page, breaking out the different sections with the use of h3 tags.
Buttons are centered and there can be multiple in a single row.
 public/api.js     | 36 ++++++++++++++++++++++++++++++++++--
 public/index.html | 43 ++++++++++++++++++++++++++++++++++++++-----
 public/main.js    | 27 ++++++++++++++++++++++++++-
 public/signin.js  |  2 +-
 public/style.css  | 23 +++++++++++++++++++++++
 5 files changed, 122 insertions(+), 9 deletions(-)

diff --git a/public/api.js b/public/api.js
index 3015aa5..8aad24a 100644
--- a/public/api.js
+++ b/public/api.js
@@ -8,7 +8,7 @@ async function getAlbums() {
         return albums;
     }
 
-    let accessToken = await getAccessToken();
+    const accessToken = await getAccessToken();
     const response = await fetch('https://api.spotify.com/v1/me/albums', {
         headers: {
             Authorization: 'Bearer ' + accessToken
@@ -28,7 +28,7 @@ async function getTracks(albumId) {
         return tracks;
     }
 
-    let accessToken = await getAccessToken();
+    const accessToken = await getAccessToken();
     const response = await fetch(`https://api.spotify.com/v1/albums/${albumId}/tracks`, {
         headers: {
             Authorization: 'Bearer ' + accessToken
@@ -53,3 +53,35 @@ async function playTrack(trackId) {
         })
     });
 }
+
+async function getPlayingTrack() {
+    const accessToken = await getAccessToken();
+    const response = await fetch("https://api.spotify.com/v1/me/player", {
+        headers: {
+            Authorization: 'Bearer ' + accessToken
+        }
+    });
+
+    const data = await response.json();
+    return data;
+}
+
+async function play() {
+    const accessToken = await getAccessToken();
+    await fetch("https://api.spotify.com/v1/me/player/play", {
+        method: "PUT",
+        headers: {
+            Authorization: 'Bearer ' + accessToken
+        }
+    });
+}
+
+async function pause() {
+    const accessToken = await getAccessToken();
+    await fetch("https://api.spotify.com/v1/me/player/pause", {
+        method: "PUT",
+        headers: {
+            Authorization: 'Bearer ' + accessToken
+        }
+    });
+}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 34875ae..a383007 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6,6 +6,8 @@
     <title>Hawk</title>
     <link rel="icon" type="image/png" href="favicon.png">
     <link rel="stylesheet" href="style.css">
+    <link rel="stylesheet"
+        href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
 </head>
 
 <body>
@@ -15,9 +17,19 @@
                 <span>${name}</span> - <span>${artist}</span>
                 <span>(${duration})</span>
             </div>
-            <div>
-                <button id="play-${trackId}" onclick="playTrack('${trackId}')">Play</button>
-                <button id="open-${trackId}" onclick="window.open('${trackHref}')">Open</button>
+            <div class="buttons">
+                <span class="button" onclick="playTrack('${trackId}')">
+                    <span class="material-symbols-outlined">
+                        play_arrow
+                    </span>
+                    <span>Play</span>
+                </span>
+                <span class="button" onclick="window.open('${trackHref}')">
+                    <span class="material-symbols-outlined">
+                        link
+                    </span>
+                    <span>Open</span>
+                </span>
             </div>
         </div>
     </template>
@@ -44,7 +56,25 @@
             <button id="signin">Sign In to Spotify</button>
         </div>
         <div id="controls">
-            <div id="greeting"></div>
+            <div>
+                <div>
+                    <h3>Currently playing:
+                        <span id="current-song"></span>
+                    </h3>
+                </div>
+                <div>
+                    <span class="button" onclick="pauseOrPlay()">
+                        <span class="material-symbols-outlined">
+                            play_pause
+                        </span>
+                        <span>Play/Pause</span>
+                    </span>
+                </div>
+            </div>
+
+            <div>
+                <h3>Search for a song by duration:</h3>
+            </div>
             <span id="minutes">
                 <input id="minutes-input" type="number" min="0" max="59" value="5" />
                 <label for="minutes-input">min</label>
@@ -55,7 +85,10 @@
             </span>
             <button id="search">Search</button>
 
-            <div id="results"></div>
+            <div>
+                <h3>Found songs:</h3>
+                <div id="results"></div>
+            </div>
 
             <button id="signout">Sign Out</button>
         </div>
diff --git a/public/main.js b/public/main.js
index 8861d38..43d896d 100644
--- a/public/main.js
+++ b/public/main.js
@@ -27,6 +27,8 @@ if (showSignInButton) {
 } else {
     signInButton.setAttribute('style', 'display: none');
     controlsDiv.setAttribute('style', 'display: block');
+    setCurrentPlayingSong();
+    setInterval(setCurrentPlayingSong, 5 * 1000);
 }
 
 const signOutButton = document.getElementById('signout');
@@ -41,7 +43,7 @@ searchButton.addEventListener('click', async () => {
         const tracks = await search(minutes, seconds);
         const resultsDiv = document.getElementById('results');
         if (tracks.length == 0) {
-            resultsDiv.innerHTML = 'No tracks found for that duration.';
+            resultsDiv.innerHTML = 'No songs found for that duration.';
         } else {
             resultsDiv.innerHTML = '';
             tracks.forEach(track => {
@@ -61,6 +63,17 @@ searchButton.addEventListener('click', async () => {
     }
 });
 
+async function setCurrentPlayingSong() {
+    const currentSongSpan = document.getElementById('current-song');
+    const currentTrack = await getPlayingTrack();
+    console.debug(currentTrack);
+    if (currentTrack && currentTrack.is_playing) {
+        currentSongSpan.innerText = `${currentTrack.item.name} by ${currentTrack.item.artists[0].name}`;
+    } else {
+        currentSongSpan.innerText = 'Nothing playing';
+    }
+}
+
 function formatDuration(ms) {
     // Convert milliseconds to total seconds
     let totalSeconds = Math.floor(ms / 1000);
@@ -78,11 +91,13 @@ function formatDuration(ms) {
 
 async function search(minutes, seconds) {
     const albums = await getAlbums();
+    console.debug(albums);
 
     const foundTracks = [];
     for (let i = 0; i < albums.items.length; i++) {
         const album = albums.items[i];
         const tracks = await getTracks(album.album.id);
+        console.debug(tracks)
 
         for (let j = 0; j < tracks.items.length; j++) {
             const track = tracks.items[j];
@@ -98,4 +113,14 @@ async function search(minutes, seconds) {
         }
     }
     return foundTracks;
+}
+
+async function pauseOrPlay() {
+    const currentTrack = await getPlayingTrack();
+    if (currentTrack && currentTrack.is_playing) {
+        await pause();
+    } else {
+        await play();
+    }
+    setCurrentPlayingSong();
 }
\ No newline at end of file
diff --git a/public/signin.js b/public/signin.js
index c344fe2..c36d78f 100644
--- a/public/signin.js
+++ b/public/signin.js
@@ -1,5 +1,5 @@
 const clientId = '6c4c04a3751b4e409ddad7d71aad9949';
-const scope = 'user-library-read user-modify-playback-state';
+const scope = 'user-library-read user-modify-playback-state user-read-playback-state';
 const prematureRefreshSeconds = 60 * 5; // 5 minutes before expiration
 
 function getSpotifyRedirectURL() {
diff --git a/public/style.css b/public/style.css
index ba46adf..3f74433 100644
--- a/public/style.css
+++ b/public/style.css
@@ -65,4 +65,27 @@ div {
         font-size: 18px;
         /* increase the font size */
     }
+}
+
+.material-symbols-outlined {
+    font-variation-settings:
+        'FILL' 0,
+        'wght' 400,
+        'GRAD' 0,
+        'opsz' 24
+}
+
+.button {
+    display: flex;
+    align-items: center;
+    cursor: pointer;
+    justify-items: center;
+    padding-left: 1em;
+
+    /* vertical-align: middle; */
+}
+
+.buttons {
+    display: flex;
+    justify-content: start;
 }
\ No newline at end of file