diff --git a/content.js b/content.js index 29d3612..60a21ba 100644 --- a/content.js +++ b/content.js @@ -9,6 +9,12 @@ const defaultArtwork = "https://placehold.co/150/1E1F22/FFF?text=Nothing%20playi 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); @@ -25,63 +31,72 @@ 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]'); + + const defaultArtworkBase64 = await imageUrlToBase64(defaultArtwork); if (!media) { pauseStartTime = null; return { details: "Nothing playing", state: "", - image: defaultArtwork, - 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 album = metadata.album || "Unknown Album"; const artworkSrc = Array.isArray(metadata.artwork) && metadata.artwork[0]?.src ? metadata.artwork[0].src : null; + let artworkBase64 = null; const largeImageKey = artworkSrc?.replace(/\d+x\d+[a-z]*/i, '150x150bb'); // More robust for Apple Music artwork URLs - let artworkBase64 = null; - if (artworkSrc) { - if (artworkBase64Cache.lastArtworkUrl === largeImageKey && artworkBase64Cache.lastArtworkBase64) { - artworkBase64 = artworkBase64Cache.lastArtworkBase64; - } else { + 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 || 0); - const duration = timestampInput ? Number(timestampInput.getAttribute('aria-valuemax')) : (media.duration || 0); + if (paused) { + pauseStartTime ??= Date.now(); + if (Date.now() - pauseStartTime >= pausedTimeout) { + return { + details: "Nothing playing", + state: "", + 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); - const commonData = { + + return { details: title, state: artist, album, @@ -89,23 +104,7 @@ async function getNowPlayingData() { currentTime, startTimestamp: nowUnix - Math.floor(currentTime), endTimestamp: nowUnix + Math.floor(Math.max(0, duration - currentTime)), - duration - }; - - if (paused) { - if (!pauseStartTime) pauseStartTime = Date.now(); - if (Date.now() - pauseStartTime >= pausedTimeout) { - return { - details: "Not playing", - image: defaultArtwork, - state: "", - paused: true - }; - } - } - - return { - ...commonData, + duration, artworkBase64 }; } @@ -129,11 +128,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) { @@ -142,11 +137,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', @@ -181,27 +172,24 @@ function safeCall(fn) { } } function startNowPlayingLoop() { - if (window._nowPlayingIntervalSet) return; - - window._nowPlayingIntervalSet = true; - - intervalId = setInterval(() => safeCall(sendNowPlaying), metadataFetchInterval); + 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."); @@ -224,9 +212,7 @@ new MutationObserver(() => { window._nowPlayingIntervalSet = false; setTimeout(() => { - if (!window._nowPlayingIntervalSet) { - startNowPlayingLoop(); - } + if (!window._nowPlayingIntervalSet) startNowPlayingLoop(); }, metadataFetchInterval); } }).observe(document, { subtree: true, childList: true });