From 462ece3c6a1bf40bcf4fdc7b8fabb82bc116645f Mon Sep 17 00:00:00 2001 From: busybox11 Date: Thu, 9 Feb 2023 16:24:37 +0100 Subject: [PATCH 1/5] playing: Use of TailwindCSS and AlpineJS (WIP) spotify-web-api: New endpoint scripts: Improvements --- assets/js/playing.js | 50 +++++ assets/js/scripts.js | 34 ++-- assets/js/spotify-web-api.js | 15 ++ assets/styles/playing.css | 41 ++++ playing.php | 383 +++++++++++++++-------------------- 5 files changed, 289 insertions(+), 234 deletions(-) create mode 100644 assets/js/playing.js diff --git a/assets/js/playing.js b/assets/js/playing.js new file mode 100644 index 0000000..3461aa2 --- /dev/null +++ b/assets/js/playing.js @@ -0,0 +1,50 @@ +// Check if cookie refreshToken is set +let cookie = document.cookie; +if (!cookie.includes("refreshToken")) { window.location.replace('login.php'); } + +const refreshTime = readCookie('refreshTime'); +let spotifyApi; + +document.addEventListener('alpine:init', x => { + Alpine.store('player', { + init() { + spotifyApi = new SpotifyWebApi(); + spotifyApi.setAccessToken(readCookie('accessToken')); + + this.poolingLoop(); + }, + + playbackObj: {}, + lastPlaybackObj: {}, + + targetImg: 'assets/images/no_song.png', + + async poolingLoop() { + setInterval(async () => { + if (Math.floor(Date.now() / 1000) >= refreshTime) { + window.location.replace('token.php?action=refresh'); + } + + const response = await spotifyApi.getMyCurrentPlaybackState(); + if (response) { + this.lastPlaybackObj = this.playbackObj; + this.playbackObj = response; + + if (this.playbackObj.item?.name) { + document.title = `${this.playbackObj.item?.name} - ${this.playbackObj.item?.artists[0].name} - NowPlaying`; + } + + // Fetch album art + if (this.playbackObj.item?.album?.images[0]?.url) { + // Load image in new element and then set it on the target + const img = new Image(); + img.src = this.playbackObj.item?.album?.images[0]?.url; + img.onload = () => { + this.targetImg = this.playbackObj.item?.album?.images[0]?.url; + } + } + } + }, 1000) + } + }) +}) \ No newline at end of file diff --git a/assets/js/scripts.js b/assets/js/scripts.js index cfa1d3f..3be5884 100644 --- a/assets/js/scripts.js +++ b/assets/js/scripts.js @@ -1,24 +1,28 @@ if (localStorage.getItem('deviceId') == null) { - function makeId(length) { - var out = ''; - var numbers = '0123456789'; - for ( let i = 0; i < length; i++ ) { - out += numbers.charAt(Math.floor(Math.random() * numbers.length)); - } - return out; - } + function makeId(length) { + var out = ''; + var numbers = '0123456789'; + for ( let i = 0; i < length; i++ ) { + out += numbers.charAt(Math.floor(Math.random() * numbers.length)); + } + return out; + } - localStorage.setItem('deviceId', makeId(4)) + localStorage.setItem('deviceId', makeId(4)) } function msToTime(duration) { - var milliseconds = parseInt((duration % 1000) / 100), - seconds = parseInt((duration / 1000) % 60), + let seconds = parseInt((duration / 1000) % 60), minutes = parseInt((duration / (1000 * 60)) % 60) + minutes = (minutes < 10) ? "0" + minutes : minutes; seconds = (seconds < 10) ? "0" + seconds : seconds; - return minutes + ":" + seconds; + if (minutes !== NaN && seconds !== NaN) { + return minutes + ":" + seconds; + } else { + return undefined; + } } function readCookie(name) { @@ -70,11 +74,7 @@ function closeFullscreen() { function fullscreen() { let isFullscreen = document.fullscreen; - if (isFullscreen == true) { - closeFullscreen(); - } else { - openFullscreen(); - } + (isFullscreen) ? closeFullscreen() : openFullscreen() } function theme() { diff --git a/assets/js/spotify-web-api.js b/assets/js/spotify-web-api.js index 58168a4..0e06b46 100644 --- a/assets/js/spotify-web-api.js +++ b/assets/js/spotify-web-api.js @@ -1687,6 +1687,21 @@ var SpotifyWebApi = (function() { return _checkParamsAndPerformRequest(requestData, options, callback); }; + /** + * Gets the user's playing queue + * + * See [Get the User's Queue](https://developer.spotify.com/documentation/web-api/reference/#/operations/get-queue) on + * the Spotify Developer site for more information about the endpoint. + * + * @return {Object} Null if a callback is provided, a `Promise` object otherwise + */ + Constr.prototype.getMyCurrentQueue = function(options, callback) { + var requestData = { + url: _baseUri + '/me/player/queue' + }; + return _checkParamsAndPerformRequest(requestData, options, callback); + } + /** * Gets the access token in use. * diff --git a/assets/styles/playing.css b/assets/styles/playing.css index 9e6306c..0322e28 100644 --- a/assets/styles/playing.css +++ b/assets/styles/playing.css @@ -115,6 +115,47 @@ body { z-index: 3; } +#up-next-div { + position: absolute; + bottom: 0; + left: 0; + right: 0; + z-index: 3; + + display: flex; + align-items: center; + gap: 32px; + padding: 16px 32px; + + background: #00000030; +} + +#up-next-song { + display: flex; + align-items: center; + gap: 24px; +} + +#up-next-img { + height: 100px; + width: 100px; + + border-radius: 12px; + + box-shadow: 0px 4px 8px #00000030; +} + +#up-next-song-title { + font-size: 30px; + font-weight: bold; + margin: 0px; +} + +#up-next-song-artist { + font-size: 26px; + margin: 0px; +} + .fadeInOut { transition-duration: 200ms; transition-property: visibility, opacity; diff --git a/playing.php b/playing.php index 4692810..1842791 100644 --- a/playing.php +++ b/playing.php @@ -2,242 +2,191 @@ include_once('lang.php'); ?> - + - Spotify Connect - Now Playing + Spotify Connect - Now Playing - - - - - - - - - + - // declare all variables - let response; - let parsedResult; - let idSong; - let currentlyPlayingType; - let refreshTime; - let iCast = 0; - const AVAILABLE_DEVICES = ['Computer', 'Tablet', 'Smartphone', 'Speaker', 'TV', 'AVR', 'STB', 'AudioDongle', 'GameConsole', 'CastVideo', 'CastAudio', 'Automobile', 'Unknown'] - const DEVICES_ICON = ['computer', 'tablet_android', 'smartphone', 'speaker', 'tv', 'speaker_group', 'speaker_group', 'cast_connected', 'gamepad', 'cast_connected', 'cast_connected', 'directions_car', 'device_unknown'] - refreshTime = readCookie('refreshTime'); - var spotifyApi = new SpotifyWebApi(); - spotifyApi.setAccessToken(readCookie('accessToken')); - loopForever(); - - // loop function - function loopForever () { - setInterval(function() { - let promise = Promise.resolve(spotifyApi.getMyCurrentPlaybackState(null)); - promise.then(function(value) { - response = value; - }); + - if (Math.floor(Date.now() / 1000) >= refreshTime) { - window.location.replace('token.php?action=refresh'); - } - - if (response != "") { - getInformations(response); - } else { - noInformations(); - } - - function getInformations (response) { - currentlyPlayingType = response.currently_playing_type; - progressSong = response.progress_ms; - progressSongFormatted = msToTime(response.progress_ms); - deviceName = response["device"].name; - deviceType = response["device"].type; - if (response.is_playing == true) { - $("#activeicon").text(DEVICES_ICON[AVAILABLE_DEVICES.indexOf(deviceType)]); - // $("#pause-button").text("pause"); - $("#device-name").text(deviceName); - if (iCast < 11) { - iCast = 0; - } - } else { - $("#activeicon").text("pause"); - // $("#pause-button").text("play_arrow"); - if (iCast <= 4) { - $("#device-name").text('Ready to cast on NowPlaying for Spotify #' + localStorage.getItem('deviceId')); - } else { - $("#device-name").text(deviceName); - if (iCast == 9) { - iCast = -1; - } - } - iCast++; - } - if (currentlyPlayingType != "ad") { - lenghtSong = response["item"].duration_ms; - lenghtSongFormatted = msToTime(response["item"].duration_ms); - seekbarProgress = Math.round(progressSong * 100 / lenghtSong); - titleSong = response["item"].name; - let tempArtist = ""; - for (let i = 0; i < response["item"]["artists"].length; i++) { - tempArtist = tempArtist + response["item"]["artists"][i].name; - if (i != response["item"]["artists"].length - 1) { - tempArtist = tempArtist + ", "; - } - } - artistSong = tempArtist; - albumSong = response["item"]["album"].name; - title = "".replace("%title%", titleSong).replace("%artist%", artistSong); - albumPicture = response["item"]["album"]["images"]["0"].url; - $("#time-song").text(progressSongFormatted + " ยท " + lenghtSongFormatted); - } else { - titleSong = ""; - artistSong = "Spotify"; - albumSong = ""; - title = " -" + deviceName + "- Now Playing for Spotify"; - albumPicture = "no_song.png"; - lenghtSong = " "; - lenghtSongFormatted = " "; - seekbarProgress = 0; - $("#time-song").text(progressSongFormatted); - } - let img = new Image(); - $(img).on("load", function() { - $("#playing-div img").attr("src", albumPicture); - $("#background-image-div").attr("style", "background: url('" + albumPicture + "');background-size:cover;background-position: center center;"); - }); - img.src = albumPicture; - - $("#seekbar-now").attr("style", "width : " + seekbarProgress + "%"); - - if ($("#song-title").text() == "" || response["item"].id != idSong) { - $("#song-title").text(titleSong); - $("#song-artist").text(artistSong); - $("#song-album").text(albumSong); - document.title = title; - idSong = response["item"].id; - } - } - - function noInformations () { - titleSong = ""; - artistSong = ""; - albumSong = ""; - title = ""; - albumPicture = "assets/images/no_song.png"; - lenghtSong = " "; - lenghtSongFormatted = " "; - progressSong = " "; - progressSongFormatted = " "; - seekbarProgress = 0; - $("#activeicon").text("pause"); - // $("#pause-button").text(""); - } - - }, 1000); - } + + + + + - - + + + + - -
- fullscreen - palette + +
+
-
-
- + +
+
+ + + +
-
-

-

-
-
+
+ +
+
+ + +
+

+

+

+ +
+
+ + +
+ +
+
+
+ +
+
+ +
+ + +
+
-

pauseSpotify Connect

-
+ + From 94c77cdd1ea12fca0c1444e5d7f9f06b098bf613 Mon Sep 17 00:00:00 2001 From: busybox11 Date: Sat, 11 Nov 2023 13:39:28 +0000 Subject: [PATCH 2/5] playing: Fix blur --- playing.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/playing.php b/playing.php index 1842791..9bf7801 100644 --- a/playing.php +++ b/playing.php @@ -49,11 +49,11 @@ class="flex h-screen w-screen overflow-hidden" >
From 8b9ab74a77375fef2a40fcca56e6da9f5f53b1eb Mon Sep 17 00:00:00 2001 From: busybox11 Date: Sat, 11 Nov 2023 13:39:35 +0000 Subject: [PATCH 3/5] devcontainer: Init --- .devcontainer/devcontainer.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..2e25b3a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/php +{ + "name": "PHP", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/php:1-8.2-bullseye", + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Configure tool-specific properties. + // "customizations": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [ + 8080 + ], + "features": { + "ghcr.io/devcontainers-contrib/features/zsh-plugins:0": {}, + "ghcr.io/stuartleeks/dev-container-features/shell-history:0": {} + }, + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "sudo a2enmod rewrite && sudo chmod a+x \"$(pwd)\" && sudo rm -rf /var/www/html && sudo ln -s \"$(pwd)\" /var/www/html && service apache2 restart" + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} From 42013d497e50686aae2c8d1feb093f21b56b2bac Mon Sep 17 00:00:00 2001 From: busybox11 Date: Sat, 11 Nov 2023 16:48:51 +0000 Subject: [PATCH 4/5] playing: - Continue alpine rewrite - Reimplement WebPlayback SDK --- assets/js/cursor.js | 30 ------ assets/js/playing.js | 53 +++++---- playing.php | 251 +++++++++++++++++++++++++++++-------------- 3 files changed, 203 insertions(+), 131 deletions(-) delete mode 100644 assets/js/cursor.js diff --git a/assets/js/cursor.js b/assets/js/cursor.js deleted file mode 100644 index 269223e..0000000 --- a/assets/js/cursor.js +++ /dev/null @@ -1,30 +0,0 @@ -$(document).ready(function() { - var idleMouseTimer; - var forceMouseHide = false; - var canHide = true; - - $("body").css('cursor', 'none'); - - $("body").mousemove(function(ev) { - if(!forceMouseHide) { - $("body").css('cursor', ''); - $(".settings-div").removeClass('hidden'); - $("#pause-button").removeClass('hidden'); - $("#previous-button").removeClass('hidden'); - $("#next-button").removeClass('hidden'); - clearTimeout(idleMouseTimer); - - idleMouseTimer = setTimeout(function() { - $("body").css('cursor', 'none'); - $(".settings-div").addClass('hidden'); - $("#pause-button").addClass('hidden'); - $("#previous-button").addClass('hidden'); - $("#next-button").addClass('hidden'); - forceMouseHide = true; - setTimeout(function() { - forceMouseHide = false; - }, 500); - }, 2000); - } - }); -}); diff --git a/assets/js/playing.js b/assets/js/playing.js index 3461aa2..7c952c9 100644 --- a/assets/js/playing.js +++ b/assets/js/playing.js @@ -21,30 +21,39 @@ document.addEventListener('alpine:init', x => { async poolingLoop() { setInterval(async () => { - if (Math.floor(Date.now() / 1000) >= refreshTime) { - window.location.replace('token.php?action=refresh'); - } + await this.fetchState(); + }, 1000) + }, + + async fetchState() { + if (Math.floor(Date.now() / 1000) >= refreshTime) { + window.location.replace('token.php?action=refresh'); + } - const response = await spotifyApi.getMyCurrentPlaybackState(); - if (response) { - this.lastPlaybackObj = this.playbackObj; - this.playbackObj = response; - - if (this.playbackObj.item?.name) { - document.title = `${this.playbackObj.item?.name} - ${this.playbackObj.item?.artists[0].name} - NowPlaying`; - } - - // Fetch album art - if (this.playbackObj.item?.album?.images[0]?.url) { - // Load image in new element and then set it on the target - const img = new Image(); - img.src = this.playbackObj.item?.album?.images[0]?.url; - img.onload = () => { - this.targetImg = this.playbackObj.item?.album?.images[0]?.url; - } - } + const response = await spotifyApi.getMyCurrentPlaybackState(); + if (response) { + this.handleChange(response); + } + }, + + handleChange(obj) { + this.lastPlaybackObj = this.playbackObj; + this.playbackObj = obj; + + if (this.playbackObj.item?.name) { + document.title = `${this.playbackObj.item?.name} - ${this.playbackObj.item?.artists[0].name} - NowPlaying`; + } + + // Fetch album art + const imgUrl = this.playbackObj.item?.album?.images[0]?.url; + if (imgUrl !== this.lastPlaybackObj.item?.album?.images[0]?.url) { + // Load image in new element and then set it on the target + const img = new Image(); + img.src = (imgUrl !== undefined) ? imgUrl : 'assets/images/no_song.png' + img.onload = () => { + this.targetImg = this.playbackObj.item?.album?.images[0]?.url; } - }, 1000) + } } }) }) \ No newline at end of file diff --git a/playing.php b/playing.php index 9bf7801..3ed1067 100644 --- a/playing.php +++ b/playing.php @@ -26,6 +26,8 @@ .custom-img-shadow { box-shadow: 0 19px 38px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.22); } + + [x-cloak] { display: none !important; } @@ -36,30 +38,52 @@ -
-
-
+
+
@@ -77,21 +101,64 @@ class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[max(115vh,
- +
+ + + + +
+ +
+ + + + + +
+ + +
+
-

-

-

+

+

+

- - + +
@@ -119,75 +186,101 @@ class="h-full bg-white"
+
+ + +
+ From f1ce2434aae4f34e6a8098228811f73d8c5fa637 Mon Sep 17 00:00:00 2001 From: busybox11 Date: Sat, 11 Nov 2023 16:53:46 +0000 Subject: [PATCH 5/5] htaccess: Better rules --- .htaccess | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.htaccess b/.htaccess index 2d97d14..f99583e 100644 --- a/.htaccess +++ b/.htaccess @@ -2,3 +2,18 @@ ErrorDocument 404 /404.php ErrorDocument 403 /403.php RedirectMatch 403 /.env + +# vendor +RewriteRule ^vendor/.*$ - [F,L,NC] + +# disable .git folder +RedirectMatch 404 /\.git + +# disable .devcontainer, .vscode, .github folders +RedirectMatch 404 /(\.devcontainer|\.vscode|\.github) + +# disable directory browsing +Options -Indexes + +# disable server signature +ServerSignature Off \ No newline at end of file