update now-playing widget to use listenbrainz
This commit is contained in:
parent
8eee6a1529
commit
6460a513d7
5 changed files with 140 additions and 23 deletions
17
index.html
17
index.html
|
|
@ -5,7 +5,7 @@
|
||||||
<title>Penelope Gwen</title>
|
<title>Penelope Gwen</title>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<link rel="stylesheet" href="/style/css/style.css">
|
<link rel="stylesheet" href="/style/css/style.css">
|
||||||
<script src="/style/js/lastfm-nowplaying.js"></script>
|
<!-- <script src="/style/js/lastfm-nowplaying.js"></script>-->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Penelope Gwen</h1>
|
<h1>Penelope Gwen</h1>
|
||||||
|
|
@ -24,7 +24,17 @@
|
||||||
<div id='random-buttons'>
|
<div id='random-buttons'>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="nowplaying-placeholder"></div>
|
<div id="now-playing" hidden="true">
|
||||||
|
<a class="flexrow now-playing" href="https://listenbrainz.org/user/pogmommy/">
|
||||||
|
<img id="np-cover" src="" hidden="true">
|
||||||
|
<div class="np-metadata sidebar-caption">
|
||||||
|
<span id="np-heading">Now Playing</span>
|
||||||
|
<span id="np-timestamp" hidden="true"></span>
|
||||||
|
<span id="np-title" class="np-title">Title</span>
|
||||||
|
<span id="np-artist">Artist</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="sidebar-caption">Check out my friends!</h3>
|
<h3 class="sidebar-caption">Check out my friends!</h3>
|
||||||
<a href="https://cassiecandles.net/">
|
<a href="https://cassiecandles.net/">
|
||||||
|
|
@ -73,7 +83,7 @@
|
||||||
<script type="module" src="https://esm.sh/emfed@1"></script>
|
<script type="module" src="https://esm.sh/emfed@1"></script>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById("emfed-placeholder").innerHTML = "";
|
document.getElementById("emfed-placeholder").innerHTML = "";
|
||||||
getNowPlaying();
|
// getNowPlaying();
|
||||||
</script>
|
</script>
|
||||||
<div class="pagecontent">
|
<div class="pagecontent">
|
||||||
<div class="bubble">
|
<div class="bubble">
|
||||||
|
|
@ -116,5 +126,6 @@
|
||||||
<!-- <div id="footer"><p>Click <a href="https://pogmom.me/links.html">here</a> or enable javascript to view my social links</p></div>
|
<!-- <div id="footer"><p>Click <a href="https://pogmom.me/links.html">here</a> or enable javascript to view my social links</p></div>
|
||||||
<script src="/style/js/views.js"></script>-->
|
<script src="/style/js/views.js"></script>-->
|
||||||
<script src="/style/js/buttons.js"></script>
|
<script src="/style/js/buttons.js"></script>
|
||||||
|
<script src="/style/js/listenbrainz-nowplaying.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -98,12 +98,13 @@ p {
|
||||||
.button {
|
.button {
|
||||||
margin: 1vh 1vw;
|
margin: 1vh 1vw;
|
||||||
transition: 0.5s;
|
transition: 0.5s;
|
||||||
|
background-color: rgba(from var(--secondary-bg-color) r g b / 0.2);
|
||||||
display:block;
|
display:block;
|
||||||
& > p {
|
& > p {
|
||||||
margin: 1vh 0vw;
|
margin: 1vh 0vw;
|
||||||
}
|
}
|
||||||
&:hover{
|
&:hover{
|
||||||
box-shadow: 0px 0px 30px rgba(from var(--main-fg-color) r g b / 0.8);
|
box-shadow: 0px 0px 30px rgba(from var(--secondary-bg-color) r g b / 0.6);
|
||||||
transition: 0.25s;
|
transition: 0.25s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,23 +198,21 @@ p {
|
||||||
font-family: 'Overpass';
|
font-family: 'Overpass';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url(/style/fonts/overpass/latin-ext.woff2) format('woff2');
|
src: url(/style/fonts/overpass/OverpassNerdFont-Regular.otf) format('opentype');
|
||||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
/*unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
/*@font-face {
|
||||||
font-family: 'Overpass';
|
font-family: 'Overpass';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url(/style/fonts/overpass/latin.woff2) format('woff2');
|
src: url(/style/fonts/overpass/latin.woff2) format('woff2');
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
/*LASTFM NOWPLAYING*/
|
/*LISTENBRAINZ NOWPLAYING*/
|
||||||
|
|
||||||
a.now-playing {
|
a.now-playing {
|
||||||
opacity: 1;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.now-playing .np-metadata > span {
|
.now-playing .np-metadata > span {
|
||||||
|
|
@ -224,18 +223,24 @@ a.now-playing {
|
||||||
.now-playing .np-metadata {
|
.now-playing .np-metadata {
|
||||||
margin: auto 1vw auto;
|
margin: auto 1vw auto;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.4s ease-out;
|
transition: opacity 0.4s ease-out;
|
||||||
transition-delay: 0.5s;
|
transition-delay: 0.5s;
|
||||||
}
|
}
|
||||||
.now-playing.loaded .np-metadata,.now-playing.loaded img {
|
.now-playing.loaded .np-metadata,.now-playing.loaded img {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
.np-title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
.now-playing img {
|
.now-playing img {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
color: transparent;
|
||||||
|
background-color: rgba(from var(--secondary-bg-color) r g b / 0.1);
|
||||||
|
font-size: 0px;
|
||||||
|
aspect-ratio: 1;
|
||||||
width: 25%;
|
width: 25%;
|
||||||
}
|
}
|
||||||
.now-playing .np-metadata .np-heading {
|
#np-heading, #np-timestamp {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
|
@ -243,15 +248,16 @@ a.now-playing {
|
||||||
letter-spacing: 0.11em;
|
letter-spacing: 0.11em;
|
||||||
}
|
}
|
||||||
.now-playing .np-metadata .breather {
|
.now-playing .np-metadata .breather {
|
||||||
margin-right: 8px;
|
margin-right: 4px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
animation: pulsate 5s linear 0s infinite;
|
animation: pulsate 5s linear 0s infinite;
|
||||||
}
|
}
|
||||||
@keyframes pulsate {
|
@keyframes pulsate {
|
||||||
0% { transform: scale(0.2, 0.2) rotate(0deg); opacity: 0.0; }
|
0% { transform: scale(0.1, 0.1); opacity: 0.0; }
|
||||||
50% { transform: scale(1, 1) rotate(50deg); opacity: 1; }
|
50% { transform: scale(1, 1); opacity: 1; }
|
||||||
80% { transform: scale(1.8, 1.8) rotate(80deg); opacity: 0.2; }
|
80% { transform: scale(1.5, 1.5); opacity: 0.2; }
|
||||||
100% { transform: scale(4, 4) rotate(100deg); opacity: 0; }
|
90% { transform: scale(2, 2); opacity: 0; }
|
||||||
|
100% { transform: scale(2, 2); opacity: 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/*EMFED*/
|
/*EMFED*/
|
||||||
|
|
|
||||||
BIN
style/fonts/overpass/OverpassNerdFont-Regular.otf
Normal file
BIN
style/fonts/overpass/OverpassNerdFont-Regular.otf
Normal file
Binary file not shown.
105
style/js/listenbrainz-nowplaying.js
Normal file
105
style/js/listenbrainz-nowplaying.js
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Set the Listenbrainz username
|
||||||
|
const username = "pogmommy";
|
||||||
|
|
||||||
|
// Set displayElement to the now-playing widget text from the now_playing.html file
|
||||||
|
const displayTitle = document.getElementById("np-title");
|
||||||
|
//displayTitle.href = ""
|
||||||
|
const displayArtist = document.getElementById("np-artist");
|
||||||
|
const displayCover = document.getElementById("np-cover");
|
||||||
|
const displayStatus = document.getElementById("np-heading");
|
||||||
|
const displayTimestamp = document.getElementById("np-timestamp");
|
||||||
|
|
||||||
|
const widget = document.getElementById("now-playing");
|
||||||
|
|
||||||
|
// Update the widget attributes for the username set above
|
||||||
|
//displayTitle.href = `https://listenbrainz.org/user/${username}/`;
|
||||||
|
//displayElement.dataset.username = username;
|
||||||
|
|
||||||
|
// Define endpoints from Listenbrainz API
|
||||||
|
const endpoints = {
|
||||||
|
nowPlaying: `https://api.listenbrainz.org/1/user/${username}/playing-now`,
|
||||||
|
recentTrack: `https://api.listenbrainz.org/1/user/${username}/listens?count=1`,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to check the API response and fail gracefully if it doesn't work
|
||||||
|
async function fetchJson(url) {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get the artwork via API response and fail gracefully if it doesn't work
|
||||||
|
async function fetchArtwork(url) {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to set the artist and track title returned from Listenbrainz (or set it to Unknown if nothing is found)
|
||||||
|
function renderTrack(nowPlaying, track, cover, timestamp) {
|
||||||
|
const artist = track?.artist_name || "Unknown Artist";
|
||||||
|
const title = track?.track_name || "Unknown Track";
|
||||||
|
displayTitle.textContent = `${title}`;
|
||||||
|
displayArtist.textContent = `${artist}`;
|
||||||
|
|
||||||
|
if (nowPlaying) {
|
||||||
|
displayStatus.innerHTML = "<span class=\"breather\"></span>Now Playing";
|
||||||
|
displayTimestamp.hidden = true;
|
||||||
|
} else {
|
||||||
|
displayStatus.innerHTML = "Last Played"
|
||||||
|
displayTimestamp.textContent = `${timestamp}`;
|
||||||
|
displayTimestamp.hidden = false;
|
||||||
|
}
|
||||||
|
if (cover) {
|
||||||
|
const coverUrl = cover.images?.[0]?.thumbnails?.[500] || "";
|
||||||
|
displayCover.setAttribute("alt", `Album artwork for ${title} by ${artist}`);
|
||||||
|
displayCover.setAttribute("src", coverUrl);
|
||||||
|
displayCover.hidden = false;
|
||||||
|
} else {
|
||||||
|
displayCover.hidden = true;
|
||||||
|
}
|
||||||
|
widget.hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update the widget
|
||||||
|
async function updateNowPlayingWidget() {
|
||||||
|
try {
|
||||||
|
// Fetch and parse data from the nowPlaying endpoint
|
||||||
|
const nowPlayingData = await fetchJson(endpoints.nowPlaying);
|
||||||
|
const playingNow = nowPlayingData?.payload?.playing_now;
|
||||||
|
const currentTrack = nowPlayingData?.payload?.listens?.[0]?.track_metadata;
|
||||||
|
|
||||||
|
// If something is currently playing, update the widget with Now Playing: currentTrack
|
||||||
|
if (playingNow && currentTrack) {
|
||||||
|
const coverData = await fetchJson(`https://coverartarchive.org/release/${currentTrack?.additional_info.release_mbid}`);
|
||||||
|
renderTrack(true, currentTrack, coverData, "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch and parse data from the recentTrack endpoint
|
||||||
|
const recentData = await fetchJson(endpoints.recentTrack);
|
||||||
|
const recentTrack = recentData?.payload?.listens?.[0]?.track_metadata;
|
||||||
|
const listenTimestamp = new Date(recentData?.payload?.listens?.[0]?.listened_at * 1000);
|
||||||
|
const timestampFormatted = `(${listenTimestamp.getUTCFullYear()}-${listenTimestamp.getMonth() + 1}-${listenTimestamp.getUTCDate()})`;
|
||||||
|
|
||||||
|
// If nothing is currently playing, update the widget with Last Played: recentTrack
|
||||||
|
if (recentTrack) {
|
||||||
|
const coverData = await fetchJson(`https://coverartarchive.org/release/${recentTrack?.additional_info.release_mbid}`);
|
||||||
|
renderTrack(false, recentTrack, coverData, timestampFormatted);
|
||||||
|
}
|
||||||
|
// If there is no recent track, update the widget to say This user has no listens yet
|
||||||
|
else {
|
||||||
|
displayStatus.textContent = "🎧 This user has no listens yet";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the script can't fetch the data, update the widget to say Error loading track info.
|
||||||
|
// Also give more details in the console
|
||||||
|
catch (error) {
|
||||||
|
console.error("Failed to update Now Playing widget:", error);
|
||||||
|
displayStatus.textContent = "🎧 Error loading track info";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the widget when the page loads, and then every minute after
|
||||||
|
updateNowPlayingWidget();
|
||||||
|
setInterval(updateNowPlayingWidget, 60000);
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
fetch('/assets/views/footer.html')
|
|
||||||
.then(response => response.text())
|
|
||||||
.then( resultText => document.getElementById('footer').innerHTML = resultText );
|
|
||||||
|
|
||||||
getNowPlaying();
|
|
||||||
Loading…
Add table
Reference in a new issue