6 Commits
1.4.0 ... main

Author SHA1 Message Date
575bf35e7f fix the repeat requests for the same URL 2025-06-25 17:13:46 -07:00
a599a5b246 forgot to bump version 2025-06-23 15:55:39 -07:00
89c26b2fd1 Changed up redundant code, and fixed the pause bug
The issue with the 5 min pause bug was that the pause function was not properly resetting the state of the artwork, So it would say its not playing, while showing the artwork as if it was playing.

And the redundant code was just a few lines that were repeated in multiple places, so I consolidated them into a single function to improve maintainability.

Who made Javascript? Why is it so hard. 😭
2025-06-23 13:57:41 -07:00
4d267d2568 Changed default artwork handling, and added some more functions to make my life better 2025-06-22 21:41:26 -07:00
acd2b7c244 missed a NOTHING_PLAYING_ARTWORK_BASE64 function, and removed some stuff from artworkep that wasnt needed 2025-06-22 13:34:59 -07:00
65b5dfd6f1 make the placeholder artwork a function 2025-06-22 12:51:41 -07:00
2 changed files with 65 additions and 61 deletions

View File

@ -5,6 +5,16 @@ let token = null;
let intervalId = null;
let lastSentArtworkKey = null;
const defaultArtwork = "https://placehold.co/150/1E1F22/FFF?text=Nothing%20playing";
const pausedTimeout = 300000; // 5 minutes
const metadataFetchInterval = 1000; // 1 second
// Cache for artwork base64 by URL
const artworkBase64Cache = {
lastArtworkUrl: null,
lastArtworkBase64: null
};
async function imageUrlToBase64(url) {
try {
const response = await fetch(url);
@ -21,74 +31,83 @@ async function imageUrlToBase64(url) {
}
}
// Cache for artwork base64 by URL
const artworkBase64Cache = {
lastArtworkUrl: null,
lastArtworkBase64: null
};
async function getNowPlayingData() {
const audio = document.querySelector('audio#apple-music-player');
// From PreMiD Apple Music Presence
const video = document.querySelector('apple-music-video-player')?.shadowRoot
?.querySelector('amp-window-takeover > .container > amp-video-player-internal')
?.shadowRoot?.querySelector('amp-video-player')?.shadowRoot
?.querySelector('div#video-container')?.querySelector('video#apple-music-video-player');
?.querySelector('amp-window-takeover > .container > amp-video-player-internal')?.shadowRoot
?.querySelector('amp-video-player')?.shadowRoot
?.querySelector('div#video-container > video#apple-music-video-player');
const media = video || audio;
const timestampInput = document.querySelector('amp-lcd.lcd.lcd__music')?.shadowRoot
?.querySelector('input#playback-progress[aria-valuenow][aria-valuemax]');
if (!artworkBase64Cache.defaultArtworkBase64) {
artworkBase64Cache.defaultArtworkBase64 = await imageUrlToBase64(defaultArtwork);
}
const defaultArtworkBase64 = artworkBase64Cache.defaultArtworkBase64;
if (!media) {
pauseStartTime = null;
return {
details: "Nothing playing",
state: "",
artworkBase64: "data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22150%22%20height%3D%22150%22%20viewBox%3D%220%200%20150%20150%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%231E1F22%22%2F%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M31.66%2073.75h1.61q.59%200%201.03-.15t.73-.42q.29-.28.44-.68.14-.39.14-.88%200-.46-.14-.84-.15-.38-.43-.64-.29-.26-.73-.4t-1.04-.14h-1.61zm-2.15-5.82h3.76q1.16%200%202.01.27.84.28%201.4.76.55.49.82%201.17.26.68.26%201.49%200%20.84-.28%201.54t-.84%201.2q-.56.51-1.4.79t-1.97.28h-1.61v4.07h-2.15zm9.61-.32h1.98V79.5h-1.98zm8.29%209.77V76q-.85.04-1.44.14-.58.11-.93.28-.36.17-.51.39-.15.23-.15.49%200%20.52.31.74.31.23.8.23.61%200%201.05-.22.45-.22.87-.67m-4.17-4.31-.36-.63q1.42-1.3%203.41-1.3.72%200%201.29.24.57.23.96.65t.6%201.01q.2.58.2%201.28v5.18h-.9q-.28%200-.43-.08-.15-.09-.24-.34l-.17-.6q-.32.28-.61.5-.3.21-.62.35-.32.15-.68.22-.37.08-.81.08-.52%200-.96-.14t-.76-.42-.49-.7q-.18-.41-.18-.97%200-.31.11-.62.1-.3.34-.58.23-.28.61-.53.37-.25.92-.43.55-.19%201.28-.3.73-.12%201.66-.14v-.48q0-.83-.35-1.22-.35-.4-1.02-.4-.48%200-.79.11-.32.11-.56.25t-.43.26q-.2.11-.44.11-.21%200-.35-.11-.15-.11-.23-.25m13.83-1.78h1.58l-4.47%2010.47q-.09.2-.23.31t-.43.11h-1.47l1.54-3.3-3.32-7.59H52q.24%200%20.37.11.13.12.19.26l1.75%204.26q.09.21.15.43.06.21.11.44.07-.23.15-.45.07-.22.16-.43l1.64-4.25q.07-.16.22-.26.14-.11.33-.11m2.63%200h1.98v8.21H59.7zm2.26-2.39q0%20.26-.11.48-.1.22-.27.39-.18.17-.41.27t-.49.1-.49-.1-.39-.27q-.17-.17-.27-.39t-.1-.48.1-.5q.1-.23.27-.4.16-.16.39-.26t.49-.1.49.1.41.26q.17.17.27.4.11.24.11.5m3.45%202.75.14.65q.25-.25.52-.46.28-.21.59-.36.3-.15.66-.23.35-.09.76-.09.68%200%201.2.23t.86.64q.35.41.53.99.18.57.18%201.26v5.22h-1.97v-5.22q0-.76-.35-1.17-.34-.41-1.05-.41-.51%200-.96.23-.44.23-.84.63v5.94H63.7v-8.21h1.21q.38%200%20.5.36m10.31%203.55q.37%200%20.64-.1.28-.1.46-.28t.27-.42q.09-.25.09-.55%200-.61-.36-.96-.37-.36-1.1-.36-.72%200-1.09.36-.36.35-.36.96%200%20.29.09.54t.27.43.46.28q.27.1.63.1m2.24%204.67q0-.24-.15-.39-.14-.16-.39-.24t-.58-.12-.7-.06l-.77-.04q-.4-.02-.77-.06-.33.18-.54.43-.2.25-.2.57%200%20.22.11.41t.34.32q.24.14.61.21.38.08.92.08.56%200%20.96-.08.4-.09.66-.24.26-.14.38-.35.12-.2.12-.44m-.4-8.3h2.36v.74q0%20.35-.42.43l-.74.14q.17.42.17.92%200%20.61-.24%201.1-.25.5-.68.84t-1.02.53-1.27.19q-.24%200-.46-.02-.22-.03-.44-.07-.38.23-.38.52%200%20.25.22.37.23.11.61.16.37.05.85.06t.99.05q.5.04.98.14t.86.32q.37.21.6.59.23.37.23.95%200%20.55-.27%201.06t-.77.91q-.51.4-1.25.64-.74.25-1.69.25-.92%200-1.61-.18t-1.15-.48q-.45-.3-.68-.69-.22-.4-.22-.82%200-.58.35-.96.35-.39.95-.62-.32-.17-.52-.45-.19-.28-.19-.74%200-.18.07-.38.07-.19.2-.39.13-.19.33-.36t.47-.31q-.62-.33-.98-.89-.35-.56-.35-1.32%200-.6.24-1.1.25-.49.68-.84.44-.34%201.03-.53.6-.18%201.3-.18.53%200%201%20.11.46.1.84.31m15.67-3.64h1.89V79.5h-1.1q-.26%200-.43-.08-.17-.09-.33-.29l-6.04-7.71q.05.53.05.98v7.1h-1.9V67.93h1.13q.14%200%20.24.01.1.02.17.05.08.04.15.11.07.06.16.18l6.06%207.74-.04-.55q-.01-.27-.01-.51zm7.79%203.23q.91%200%201.66.3t1.28.84.82%201.33q.29.78.29%201.75%200%20.98-.29%201.76t-.82%201.34q-.53.55-1.28.84-.75.3-1.66.3-.92%200-1.67-.3-.75-.29-1.29-.84-.53-.56-.82-1.34t-.29-1.76q0-.97.29-1.75.29-.79.82-1.33.54-.54%201.29-.84t1.67-.3m0%206.94q1.02%200%201.52-.69.49-.69.49-2.01%200-1.33-.49-2.03-.5-.69-1.52-.69-1.04%200-1.54.7t-.5%202.02.5%202.01%201.54.69m10.66-6.81h1.97v8.21h-1.21q-.39%200-.49-.36l-.14-.66q-.5.52-1.11.83-.61.32-1.43.32-.67%200-1.19-.23t-.87-.64q-.35-.42-.53-.99t-.18-1.26v-5.22h1.98v5.22q0%20.75.34%201.16.35.41%201.05.41.51%200%20.96-.22.45-.23.85-.63zm6.66%208.34q-1.07%200-1.64-.61-.58-.6-.58-1.66v-4.59h-.84q-.16%200-.27-.1-.11-.11-.11-.31v-.79l1.32-.21.42-2.24q.04-.16.15-.25t.29-.09h1.02v2.58h2.19v1.41h-2.19v4.45q0%20.38.19.6t.51.22q.19%200%20.31-.05.13-.04.22-.09l.16-.09q.07-.05.15-.05t.14.05q.06.04.12.13l.59.96q-.43.36-.99.54-.56.19-1.16.19%22%2F%3E%3C%2Fsvg%3E",
paused: true
paused: true,
artworkBase64: defaultArtworkBase64
};
}
// From PreMiD Apple Music Presence
const timestampInput = document.querySelector('amp-lcd.lcd.lcd__music')?.shadowRoot
?.querySelector('input#playback-progress[aria-valuenow][aria-valuemax]');
const paused = media.paused || media.readyState <= 2;
const metadata = navigator.mediaSession?.metadata || {};
const title = metadata.title || media.title || "Unknown Title";
const album = metadata.album || "Unknown Album";
const artist = metadata.artist || "Unknown Artist";
const artworkSrc = metadata.artwork?.[0]?.src || null;
const largeImageKey = artworkSrc?.replace(/\d+x\d+[a-z]*/i, '150x150bb'); // More robust for Apple Music artwork URLs
const album = metadata.album || "Unknown Album";
const artworkSrc = Array.isArray(metadata.artwork) && metadata.artwork[0]?.src
? metadata.artwork[0].src
: null;
let artworkBase64 = null;
if (artworkSrc) {
if (artworkBase64Cache.lastArtworkUrl === largeImageKey && artworkBase64Cache.lastArtworkBase64) {
artworkBase64 = artworkBase64Cache.lastArtworkBase64;
} else {
const largeImageKey = artworkSrc?.replace(/\d+x\d+[a-z]*/i, '150x150bb'); // More robust for Apple Music artwork URLs
if (largeImageKey) {
if (artworkBase64Cache.lastArtworkUrl !== largeImageKey) {
artworkBase64 = await imageUrlToBase64(largeImageKey);
artworkBase64Cache.lastArtworkUrl = largeImageKey;
artworkBase64Cache.lastArtworkBase64 = artworkBase64;
} else {
artworkBase64 = artworkBase64Cache.lastArtworkBase64;
}
}
const currentTime = timestampInput ? Number(timestampInput.getAttribute('aria-valuenow')) : media.currentTime;
const duration = timestampInput ? Number(timestampInput.getAttribute('aria-valuemax')) : media.duration;
const commonData={details:title,state:artist,album,smallImageKey:'play',smallImageText:'Playing',startTimestamp:Math.floor(Date.now()/1000)-Math.floor(currentTime),endTimestamp:Math.floor(Date.now()/1000)+Math.floor(duration-currentTime),paused,currentTime,duration};
if (paused) {
if (!pauseStartTime) pauseStartTime = Date.now();
if (Date.now() - pauseStartTime >= 300000) {
pauseStartTime ??= Date.now();
if (Date.now() - pauseStartTime >= pausedTimeout) {
return {
details: "Not playing",
details: "Nothing playing",
state: "",
paused: true
paused: true,
artworkBase64: defaultArtworkBase64
};
}
} else {
pauseStartTime = null;
}
const currentTime = timestampInput ? Number(timestampInput.getAttribute('aria-valuenow')) : media.currentTime || 0;
const duration = timestampInput ? Number(timestampInput.getAttribute('aria-valuemax')) : media.duration || 0;
const nowUnix = Math.floor(Date.now() / metadataFetchInterval);
return {
...commonData,
details: title,
state: artist,
album,
paused,
currentTime,
startTimestamp: nowUnix - Math.floor(currentTime),
endTimestamp: nowUnix + Math.floor(Math.max(0, duration - currentTime)),
duration,
artworkBase64
};
}
@ -112,11 +131,7 @@ async function sendNowPlaying() {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
...noArtData,
details,
state
})
body: JSON.stringify({ ...noArtData, details, state })
});
console.log('Now playing data sent:', details, '-', state);
} catch (e) {
@ -125,11 +140,7 @@ async function sendNowPlaying() {
// Only send artwork when song changes
const currentTrackKey = `${details} - ${state}`;
if (
artworkEndpoint &&
artworkBase64 &&
currentTrackKey !== lastSentArtworkKey
) {
if (artworkEndpoint && artworkBase64 && currentTrackKey !== lastSentArtworkKey) {
try {
await fetch(artworkEndpoint, {
method: 'POST',
@ -138,9 +149,7 @@ async function sendNowPlaying() {
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
image: artworkBase64,
details,
state
image: artworkBase64
})
});
lastSentArtworkKey = currentTrackKey;
@ -166,27 +175,24 @@ function safeCall(fn) {
}
}
function startNowPlayingLoop() {
if (window._nowPlayingIntervalSet) return;
window._nowPlayingIntervalSet = true;
intervalId = setInterval(() => safeCall(sendNowPlaying), 1000);
if (!window._nowPlayingIntervalSet) {
window._nowPlayingIntervalSet = true;
intervalId = setInterval(() => safeCall(sendNowPlaying), metadataFetchInterval);
}
}
if (!window._nowPlayingExtensionLogged) {
console.log('Apple Music Now Playing Extension made by Sophia Atkinson with help from PreMiD contributors');
window._nowPlayingExtensionLogged = true;
}
if (typeof chrome !== "undefined" && chrome.storage && chrome.storage.sync) {
chrome.storage.sync.get(['endpoint', 'artworkEndpoint', 'token'], (result) => {
if (chrome?.storage?.sync) {
chrome.storage.sync.get(['endpoint', 'artworkEndpoint', 'token'], result => {
if (chrome.runtime.lastError) {
console.error("Failed to load settings:", chrome.runtime.lastError.message);
return;
}
endpoint = result.endpoint;
artworkEndpoint = result.artworkEndpoint;
token = result.token;
({ endpoint, artworkEndpoint, token } = result);
if (!endpoint || !token) {
console.error("No endpoint/token configured.");
@ -209,9 +215,7 @@ new MutationObserver(() => {
window._nowPlayingIntervalSet = false;
setTimeout(() => {
if (!window._nowPlayingIntervalSet) {
startNowPlayingLoop();
}
}, 1000);
if (!window._nowPlayingIntervalSet) startNowPlayingLoop();
}, metadataFetchInterval);
}
}).observe(document, { subtree: true, childList: true });

View File

@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Apple Music Now Playing",
"version": "1.4.0",
"version": "1.4.2",
"description": "Easily display your currently playing Apple Music track on your website with this extension.",
"permissions": [
"storage",