Add files via upload

This commit is contained in:
nuttylmao
2025-03-23 22:04:07 +11:00
committed by GitHub
parent f133722899
commit 6c53d973f7
97 changed files with 15625 additions and 0 deletions
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<head>
<title>Configure</title>
<meta charset="UTF-8">
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="mainContainer">
<div id="connectBox">
<div style="text-align: center;">
<img src="logo.webp" width="60%">
</div>
<label>
<br>
<span style="font-weight: 500;">Client ID</span>
<br>
<input type="text" id="client_id_box">
<br><br>
<span style="font-weight: 500;">Client Secret</span>
<br>
<input type="password" id="client_secret_box">
<br><br>
<button id="authorizeButton" onclick="RequestAuthorization()">Authorize</button>
<br><br>
<button id="instructionsButton" onclick="OpenInstructions()">Instructions</button>
</label>
</div>
<div id="authorizationBox">
<label id="authorizationComplete">
<img src="logo.webp" width="60%">
<br>
<br>
<span style="font-weight: 500; font-size: 30px;">Authorization complete</span>
<br><br>
You now owe me money for all of my hard work.
</label>
<button id="copyURLButton" onclick="CopyToURL()">Click to copy URL</button>
<br><br>
<button id="donateButton" onclick="OpenDonationPage()">Donate out of guilt</button>
</div>
</div>
</body>
<script src="./script.js"></script>
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

+131
View File
@@ -0,0 +1,131 @@
///////////////
// PARAMETRS //
///////////////
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const code = urlParams.get("code") || "";
const baseURL = "https://nuttylmao.github.io/spotify-widget-compact";
const redirect_uri = `${baseURL}/configure`;
let refresh_token = "";
let access_token = "";
let browserSourceURL = "";
/////////////////////////
// AUTHORIZATION STUFF //
/////////////////////////
function RequestAuthorization() {
const client_id = document.getElementById("client_id_box").value;
const client_secret = document.getElementById("client_secret_box").value;
localStorage.setItem("client_id", client_id);
localStorage.setItem("client_secret", client_secret);
let url = "https://accounts.spotify.com/authorize";
url += "?client_id=" + client_id;
url += "&response_type=code";
url += "&redirect_uri=" + encodeURI(redirect_uri);
url += "&show_dialog=true";
url += "&scope=user-read-private user-read-email user-modify-playback-state user-read-playback-position user-library-read streaming user-read-playback-state user-read-recently-played playlist-read-private";
window.location.href = url;
}
// If there is no code in the query string, direct the user to authorize their account
if (code != "") {
FetchAccessToken(code);
}
else {
document.getElementById("connectBox").style.display = 'inline';
}
async function FetchAccessToken(code) {
const client_id = localStorage.getItem("client_id");
const client_secret = localStorage.getItem("client_secret");
console.debug(`Client ID: ${client_id}`);
console.debug(`Client Secret: ${client_secret}`);
let body = "grant_type=authorization_code";
body += "&code=" + code;
body += "&redirect_uri=" + encodeURI(redirect_uri);
body += "&client_id=" + client_id;
body += "&client_secret=" + client_secret;
console.log(body);
// Get the current player information from Spotify
const response = await fetch("https://accounts.spotify.com/api/token", {
method: "POST",
headers: {
'Authorization': `Basic ${btoa(client_id + ":" + client_secret)}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body
});
// If we got a response, save the access token
if (response.ok)
{
const responseData = await response.json();
console.debug(responseData);
refresh_token = responseData.refresh_token; // Unsure if we need to replace the refresh_token but do it just in case
access_token = responseData.access_token; // Save access token for all future API calls
browserSourceURL = `${baseURL}?client_id=${client_id}&client_secret=${client_secret}&refresh_token=${refresh_token}`;
document.getElementById("authorizationBox").style.display = 'inline';
}
else
{
console.error(`${response.status}`);
}
}
///////////////////////////
// BUTTON CLICK HANDLERS //
///////////////////////////
const clientIdBox = document.getElementById('client_id_box');
const clientSecretBox = document.getElementById('client_secret_box');
const authorizeButton = document.getElementById('authorizeButton');
// Function to check if either input is empty
function checkInputs() {
if (clientIdBox.value.trim() === '' || clientSecretBox.value.trim() === '') {
authorizeButton.disabled = true; // Disable the button
} else {
authorizeButton.disabled = false; // Enable the button
}
}
// Listen for changes in the input boxes
clientIdBox.addEventListener('input', checkInputs);
clientSecretBox.addEventListener('input', checkInputs);
// Initial check when the page loads, just in case
checkInputs();
function CopyToURL() {
navigator.clipboard.writeText(browserSourceURL);
document.getElementById("copyURLButton").innerText = "Copied to clipboard";
document.getElementById("copyURLButton").style.backgroundColor = "#00dd63"
document.getElementById("copyURLButton").style.color = "#ffffff";
setTimeout(() => {
document.getElementById("copyURLButton").innerText = "Click to copy URL";
document.getElementById("copyURLButton").style.backgroundColor = "#ffffff";
document.getElementById("copyURLButton").style.color = "#181818";
}, 3000);
}
function OpenInstructions() {
window.open("https://nuttylmao.notion.site/Spotify-Widget-Compact-Edition-1ac19969b23780ae8c63c4472db0ee6b", '_blank').focus();
}
function OpenDonationPage() {
window.open("http://nutty.gg/pages/donate", '_blank').focus();
}
+104
View File
@@ -0,0 +1,104 @@
* {
--corner-radius: 10px;
--padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
}
body {
margin: 0px;
color: white;
font-size: 24px;
}
#mainContainer {
position: absolute;
width: 100vw;
height: 100vh;
margin: 0px;
background-color: #282828;
}
#connectBox{
background-color: #181818;
border-radius: var(--corner-radius);
padding: calc(var(--padding)*3) calc(var(--padding)*3);
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
display: flex;
filter: drop-shadow(0px 0px 4px rgba(0, 0, 0, 1));
width: 400px;
display: none;
}
#authorizationBox {
background-color: #252525;
border-radius: var(--corner-radius);
padding: calc(var(--padding)*3) calc(var(--padding)*3);
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
filter: drop-shadow(0px 0px 4px rgba(0, 0, 0, 1));
width: 400px;
display: none;
}
#authorizationComplete {
display: block;
text-align: center;
padding-bottom: 40px;
}
button {
font-size: 24px;
font-weight: 500;
background-color: #3be477;
color: white;
opacity: 0.8;
border-width: 0;
border-radius: var(--corner-radius);
padding: calc(var(--padding)/2) var(--padding);
width: 100%;
}
button:hover {
opacity: 1;
cursor: pointer;
}
button:disabled {
opacity: 0.5;
cursor: inherit;
}
#instructionsButton, #donateButton {
background-color: #2e2e2e;
}
#copyURLButton {
background-color: #ffffff;
color: #181818;
width: 100%;
transition: all 0.2s ease;
}
#copyURLButton:disabled {
background-color: #636363;
}
input {
border-radius: var(--corner-radius);
width: calc(100% - 20px);
font-size: 20px;
margin: 10px 0px;
padding: 10px 10px;
background-color: #ffffff05;
border-width: 0px;
color: white;
}
textarea:focus, input:focus {
outline: none;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

+46
View File
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<head>
<title>Spotify Widget</title>
<meta charset="UTF-8">
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./style.css" />
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"
integrity="sha384-2huaZvOR9iDzHqslqwpR87isEmrfxqyWOF7hr7BY6KG0+hVKLoEXMPUJw3ynWuhO"
crossorigin="anonymous"></script>
</head>
<body>
<div id="statusContainer">Connecting...</div>
<div id="mainContainer">
<!-- <div id="albumArtBox">
<img id="albumArt"
src="https://static.wikia.nocookie.net/dreamcatcherwiki/images/d/dc/Dystopia_Lose_Myself_Digital_Cover.jpg"></img>
<img id="albumArtBack"
src="https://cdns-images.dzcdn.net/images/cover/762f7ef8a0c20e91304a3fae47449ccd/1900x1900-000000-80-0-0.jpg"></img>
</div> -->
<div id="songInfoBox">
<div id="songInfo">
<div id="IAmRunningOutOfNamesForTheseBoxes">
<div id="songLabel">Can't get you out of my mind</div>
<div id="artistLabel">Dreamcatcher</div>
<!-- <div id="times">
<div id="progressTime">2:29</div>
<div id="timeRemaining">3:43</div>
</div>
<div id="progressBg">
<div id="progressBar"></div>
</div> -->
</div>
</div>
<div id="backgroundArt">
<img id="backgroundImage"
src="https://static.wikia.nocookie.net/dreamcatcherwiki/images/d/dc/Dystopia_Lose_Myself_Digital_Cover.jpg"></img>
<img id="backgroundImageBack"
src="https://cdns-images.dzcdn.net/images/cover/762f7ef8a0c20e91304a3fae47449ccd/1900x1900-000000-80-0-0.jpg"></img>
</div>
</div>
</div>
</body>
<script src="./script.js"></script>
+270
View File
@@ -0,0 +1,270 @@
///////////////
// PARAMETRS //
///////////////
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const client_id = urlParams.get("client_id") || "";
const client_secret = urlParams.get("client_secret") || "";
let refresh_token = urlParams.get("refresh_token") || "";
let access_token = "";
const visibilityDuration = urlParams.get("duration") || 0;
// const hideAlbumArt = urlParams.has("hideAlbumArt");
let currentState = false;
let currentSongUri = "";
/////////////////
// SPOTIFY API //
/////////////////
// Update the access token - this expires so needs to be refreshed with refresh_token
async function RefreshAccessToken() {
console.debug(`Client ID: ${client_id}`);
console.debug(`Client Secret: ${client_secret}`);
console.debug(`Refresh Token: ${refresh_token}`);
let body = "grant_type=refresh_token";
body += "&refresh_token=" + refresh_token;
body += "&client_id=" + client_id;
const response = await fetch("https://accounts.spotify.com/api/token", {
method: "POST",
headers: {
'Authorization': `Basic ${btoa(client_id + ":" + client_secret)}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body
});
// If we got a response, save the access token
if (response.ok)
{
const responseData = await response.json();
console.debug(responseData);
//refresh_token = responseData.refresh_token; // Unsure if we need to replace the refresh_token but do it just in case
access_token = responseData.access_token; // Save access token for all future API calls
}
else
{
console.error(`${response.status}`);
}
}
async function GetCurrentlyPlaying(refreshInterval) {
try {
// Get the current player information from Spotify
const response = await fetch("https://api.spotify.com/v1/me/player/currently-playing", {
method: "GET",
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
}
})
// If we got a response, save the access token
if (response.ok)
{
const responseData = await response.json();
console.debug(responseData);
UpdatePlayer(responseData);
}
else
{
switch (response.status)
{
case 401:
console.debug(`${response.status}`)
RefreshAccessToken();
break;
default:
console.error(`${response.status}`)
}
}
// Refresh
setTimeout(() => {
GetCurrentlyPlaying()
}, 1000);
}
catch (error)
{
console.debug(error);
SetVisibility(false);
// Try again in 2 seconds
setTimeout(() => {
GetCurrentlyPlaying()
}, 2000);
}
}
function UpdatePlayer(data) {
const isPlaying = data.is_playing; // The play/pause state of the player
const songUri = data.item.uri;
const albumArt = data.item.album.images.length > 0 ?
`${data.item.album.images[0].url}`
: `images/placeholder-album-art.png`; // The album art URL
const artist = `${data.item.artists[0].name}`; // Name of the artist
const name = `${data.item.name}`; // Name of the song
const duration = `${data.item.duration_ms/1000}`; // The duration of the song in seconds
const progress = `${data.progress_ms/1000}`; // The current position in seconds
// Set the visibility of the player, but only if the state is different than the last time we checked
if (isPlaying != currentState) {
// Set player visibility
if (!isPlaying)
{
console.debug("Hiding player...");
SetVisibility(false);
}
else
{
console.debug("Showing player...");
setTimeout(() => {
SetVisibility(true);
if (visibilityDuration > 0) {
setTimeout(() => {
SetVisibility(false, false);
}, visibilityDuration * 1000);
}
}, 500);
}
}
if (songUri != currentSongUri) {
if (isPlaying) {
console.debug("Showing player...");
setTimeout(() => {
SetVisibility(true);
if (visibilityDuration > 0) {
setTimeout(() => {
SetVisibility(false, false);
}, visibilityDuration * 1000);
}
}, 500);
currentSongUri = songUri;
}
}
// Set thumbnail
// UpdateAlbumArt(document.getElementById("albumArt"), albumArt);
UpdateAlbumArt(document.getElementById("backgroundImage"), albumArt);
// Set song info
UpdateTextLabel(document.getElementById("artistLabel"), artist);
UpdateTextLabel(document.getElementById("songLabel"), name);
// Set progressbar
const progressPerc = ((progress / duration) * 100); // Progress expressed as a percentage
const progressTime = ConvertSecondsToMinutesSoThatItLooksBetterOnTheOverlay(progress);
const timeRemaining = ConvertSecondsToMinutesSoThatItLooksBetterOnTheOverlay(duration - progress);
console.debug(`Progress: ${progressTime}`);
console.debug(`Time Remaining: ${timeRemaining}`);
// document.getElementById("progressBar").style.width = `${progressPerc}%`;
// document.getElementById("progressTime").innerHTML = progressTime;
// document.getElementById("timeRemaining").innerHTML = `-${timeRemaining}`;
document.getElementById("backgroundImage").style.clipPath = `inset(0 ${100 - progressPerc}% 0 0)`;
setTimeout(() => {
// document.getElementById("albumArtBack").src = albumArt;
document.getElementById("backgroundImageBack").src = albumArt;
}, 1000);
}
function UpdateTextLabel(div, text) {
if (div.innerText != text) {
div.setAttribute("class", "text-fade");
setTimeout(() => {
div.innerText = text;
div.setAttribute("class", ".text-show");
}, 500);
}
}
function UpdateAlbumArt(div, imgsrc) {
if (div.src != imgsrc) {
div.setAttribute("class", "text-fade");
setTimeout(() => {
div.src = imgsrc;
div.setAttribute("class", "text-show");
}, 500);
}
}
//////////////////////
// HELPER FUNCTIONS //
//////////////////////
function ConvertSecondsToMinutesSoThatItLooksBetterOnTheOverlay(time) {
const minutes = Math.floor(time / 60);
const seconds = Math.trunc(time - minutes * 60);
return `${minutes}:${('0' + seconds).slice(-2)}`;
}
function SetVisibility(isVisible, updateCurrentState = true) {
widgetVisibility = isVisible;
const mainContainer = document.getElementById("mainContainer");
if (isVisible) {
mainContainer.style.opacity = 1;
mainContainer.style.bottom = "50%";
}
else {
mainContainer.style.opacity = 0;
mainContainer.style.bottom = "calc(50% - 20px)";
}
if (updateCurrentState)
currentState = isVisible;
}
//////////////////////////////////////////////////////////////////////////////////////////
// RESIZER THING BECAUSE I THINK I KNOW HOW RESPONSIVE DESIGN WORKS EVEN THOUGH I DON'T //
//////////////////////////////////////////////////////////////////////////////////////////
let outer = document.getElementById('mainContainer'),
maxWidth = outer.clientWidth+100,
maxHeight = outer.clientHeight;
window.addEventListener("resize", resize);
resize();
function resize() {
const scale = window.innerWidth / maxWidth;
outer.style.transform = 'translate(-50%, 50%) scale(' + scale + ')';
}
/////////////////////////////////////////////////////////////////////
// IF THE USER PUT IN THE HIDEALBUMART PARAMATER, THEN YOU SHOULD //
// HIDE THE ALBUM ART, BECAUSE THAT'S WHAT IT'S SUPPOSED TO DO //
/////////////////////////////////////////////////////////////////////
// if (hideAlbumArt) {
// document.getElementById("albumArtBox").style.display = "none";
// document.getElementById("songInfoBox").style.width = "calc(100% - 20px)";
// }
////////////////////////////////
// KICK OFF THE WHOLE WIDGET //
////////////////////////////////
RefreshAccessToken();
GetCurrentlyPlaying(); // This is a recursive function, so just run it once
+191
View File
@@ -0,0 +1,191 @@
* {
--corner-radius: 25px;
--album-art-size: 50px;
}
body {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
}
#statusContainer {
font-weight: 500;
font-size: 30px;
text-align: center;
background-color: #D12025;
color: white;
padding: 10px;
border-radius: 10px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0;
}
#mainContainer {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
display: flex;
position: absolute;
height: var(--album-art-size);
margin: 20px;
filter: drop-shadow(15px 15px 7px rgba(0, 0, 0, 1));
width: 100%;
max-width: 400px;
bottom: calc(50% - 20px);
left: 50%;
opacity: 0;
transition: all 0.5s ease;
}
/* #albumArtBox {
background: rgba(0, 0, 0, 0.5);
position: relative;
border-radius: var(--corner-radius);
overflow: hidden;
margin: 0px 8px 0px 0px;
object-fit: cover;
width: var(--album-art-size);
}
#albumArt {
width: 100%;
height: 100%;
position: absolute;
top: 0%;
object-fit: cover;
}
#albumArtBack {
width: 100%;
height: 100%;
object-fit: cover;
} */
#songInfoBox {
position: relative;
color: white;
/* width: calc(100% - 125px); */
width: calc(100%);
display: flex;
flex-direction: column;
flex: 0 1 auto;
justify-content: center;
z-index: 1;
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5);
overflow: hidden;
z-index: 4;
}
#songInfo {
background: rgba(0, 0, 0, 0.5);
opacity: 0.9;
position: relative;
border-radius: var(--corner-radius);
padding: 0px 20px;
height: 100%;
overflow: hidden;
}
#IAmRunningOutOfNamesForTheseBoxes {
position: absolute;
width: calc(100% - 40px);
position: absolute;
top: 50%;
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
#backgroundArt {
position: absolute;
height: 100%;
width: 100%;
border-radius: var(--corner-radius);
overflow: hidden;
z-index: -1;
opacity: 0.9;
}
#backgroundImage {
filter: blur(20px);
position: absolute;
width: 100%;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
transition: all 2s ease;
}
#backgroundImageBack {
filter: blur(20px) grayscale(75%);
opacity: 0.5;
position: absolute;
width: 100%;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
z-index: -1;
}
#artistLabel {
font-size: 16px;
font-weight: 400;
line-height: 1;
white-space: nowrap;
transition: all 0.5s ease;
text-align: center;
}
#songLabel {
font-weight: bold;
font-size: 20px;
width: calc(100%);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: all 0.5s ease;
text-align: center;
}
/* #progressBg {
margin-top: 15px;
width: 100%;
height: auto;
border-radius: 5px;
background-color: #1F1F1F;
} */
/* #progressBar {
border-radius: 5px;
height: 5px;
width: 20%;
background-color: #ffffff;
margin: 10px 0px;
transition: all 1s ease;
} */
/* #times {
position: relative;
height: 16px;
font-size: 16px;
font-weight: 700;
line-height: 2.2;
} */
/* #progressTime {
position: absolute;
} */
/* #timeRemaining {
position: absolute;
width: 100%;
text-align: right;
} */
.text-show {
opacity: 1;
transition: all 0.25s ease;
}
.text-fade {
opacity: 0;
transition: all 0.25s ease;
}