From a637f76a1379469ccbe86b6019d2ff629a01905e Mon Sep 17 00:00:00 2001 From: Super_man2775 Date: Tue, 21 Apr 2026 10:04:21 +0200 Subject: [PATCH 1/2] Add spotify widget --- extension/background-scripts/index.js | 137 ++++++++ extension/index.css | 205 ++++++++++++ extension/index.js | 313 ++++++++++++++++++ extension/manifest.json | 3 +- src/background-scripts/index.ts | 6 + src/background-scripts/spotify.ts | 185 +++++++++++ src/index.ts | 1 + src/main-features/main.ts | 1 + src/styles/smpp-styles/spotify-widget.css | 232 +++++++++++++ src/widgets/spotify.ts | 380 ++++++++++++++++++++++ 10 files changed, 1462 insertions(+), 1 deletion(-) create mode 100644 src/background-scripts/spotify.ts create mode 100644 src/styles/smpp-styles/spotify-widget.css create mode 100644 src/widgets/spotify.ts diff --git a/extension/background-scripts/index.js b/extension/background-scripts/index.js index 4a3861fb..fea7629c 100644 --- a/extension/background-scripts/index.js +++ b/extension/background-scripts/index.js @@ -2329,6 +2329,140 @@ } } + // src/background-scripts/spotify.ts + async function spotifyController(command) { + const delay = (ms) => new Promise((r2) => setTimeout(r2, ms)); + const nowPlayingRoot = () => document.querySelector('aside[data-testid="now-playing-bar"]'); + const getButton = (testId2) => document.querySelector(`button[data-testid="${testId2}"]`); + const getText = (testId2) => { + const el = document.querySelector(`[data-testid="${testId2}"]`); + const t2 = el?.innerText?.trim(); + return t2 ? t2 : null; + }; + const inferIsPlayingFromLabel = (label) => { + if (!label) return null; + const l2 = label.toLowerCase(); + if (l2.includes("pause") || l2.includes("pauze") || l2.includes("pausa")) { + return true; + } + if (l2.includes("play") || l2.includes("afspelen") || l2.includes("lecture")) { + return false; + } + return null; + }; + const getState = () => { + const root = nowPlayingRoot(); + if (!root) { + return { + ok: false, + humanError: "Spotify player not found on this page." + }; + } + const title = (root.querySelector('[data-testid="context-item-info-title"]')?.textContent || "").trim() || null; + const artist = (root.querySelector('[data-testid="context-item-info-subtitles"]')?.textContent || "").trim() || null; + const cover = root.querySelector( + 'img[data-testid="cover-art-image"]' + )?.src || null; + const nextBtn = getButton("control-button-skip-forward"); + const prevBtn = getButton("control-button-skip-back"); + const playPauseBtn = getButton("control-button-playpause"); + const playPauseLabel = playPauseBtn?.getAttribute("aria-label") || null; + return { + ok: true, + title, + artist, + cover, + canNext: nextBtn ? !nextBtn.disabled : null, + canPrevious: prevBtn ? !prevBtn.disabled : null, + playPauseLabel, + isPlaying: inferIsPlayingFromLabel(playPauseLabel), + positionText: getText("playback-position"), + durationText: getText("playback-duration") + }; + }; + if (!command) { + return getState(); + } + const before = getState(); + if (!before || before.ok === false) { + return before; + } + const testId = command === "next" ? "control-button-skip-forward" : command === "previous" ? "control-button-skip-back" : "control-button-playpause"; + const button = getButton(testId); + if (!button) { + return { + ok: false, + humanError: `Spotify control button not found (${command}).` + }; + } + if (button.disabled) { + return { + ok: false, + humanError: `Spotify control button is disabled (${command}).` + }; + } + button.click(); + const changed = (after) => { + if (!after || after.ok === false) return false; + if (command === "playPause") { + if (before.isPlaying !== null && after.isPlaying !== null) { + return after.isPlaying !== before.isPlaying; + } + return after.playPauseLabel !== before.playPauseLabel; + } + return after.title !== before.title || after.cover !== before.cover; + }; + for (let i2 = 0; i2 < 10; i2++) { + await delay(150); + const after = getState(); + if (changed(after)) { + return after; + } + } + return getState(); + } + async function runOnSpotifyTab(command) { + const tabs = await browser.tabs.query({ url: ["*://open.spotify.com/*"] }); + if (!tabs || tabs.length === 0) { + return { + ok: false, + humanError: "No Spotify tab found. Open https://open.spotify.com in another tab." + }; + } + const targetTab = tabs.find((t2) => t2.audible) || tabs.find((t2) => t2.active) || tabs[0]; + try { + const results = await browser.scripting.executeScript({ + target: { tabId: targetTab.id }, + func: spotifyController, + args: [command] + }); + return results?.[0]?.result ?? { ok: false, humanError: "Spotify returned no data." }; + } catch (e2) { + console.error("[smpp][spotify] executeScript failed", e2); + return { + ok: false, + humanError: "Couldn't run the Spotify controller. Make sure the Spotify tab is open on open.spotify.com." + }; + } + } + async function handleSpotifyMessage(message, sendResponse) { + if (message.action === "spotifyGetState") { + const state = await runOnSpotifyTab(null); + sendResponse(state); + return true; + } + if (message.action === "spotifyCommand") { + const state = await runOnSpotifyTab(message.command); + if (!state || state.ok === false) { + sendResponse(state); + return true; + } + sendResponse({ ok: true, state }); + return true; + } + return false; + } + // src/background-scripts/index.ts browser.runtime.onMessage.addListener( (message, _sender, sendResponse) => { @@ -2504,6 +2638,9 @@ await browser.storage.local.set(data); sendResponse({ success: true }); } + if (await handleSpotifyMessage(message, sendResponse)) { + return; + } if (message.action === "getDataVersion") { let dataVersion = await browser.storage.local.get("dataVersion"); console.log(dataVersion); diff --git a/extension/index.css b/extension/index.css index f528f3c1..da47be80 100644 --- a/extension/index.css +++ b/extension/index.css @@ -1962,6 +1962,211 @@ svg .pencil { fill: var(--color-accent); } +/* src/styles/smpp-styles/spotify-widget.css */ +.smpp-spotify-widget { + display: flex; + flex-direction: column; + gap: 0.75rem; + color: var(--color-text); +} +.smpp-spotify-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; +} +.smpp-spotify-header-left { + display: flex; + align-items: center; + gap: 0.6rem; +} +.smpp-spotify-title { + font-weight: 700; + font-size: 1.1rem; +} +.smpp-spotify-icon { + display: grid; + place-items: center; +} +.smpp-spotify-logo { + display: block; + width: 100%; + height: 100%; +} +.smpp-spotify-icon-small { + width: 28px; + height: 28px; +} +.smpp-spotify-icon-large { + width: 128px; + height: 128px; + margin-bottom: 0.5rem; + filter: drop-shadow(0px 2px 10px var(--color-shadow)); +} +.smpp-spotify-body { + display: grid; + grid-template-columns: 104px 1fr; + gap: 0.75rem; + align-items: center; +} +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-body { + grid-template-columns: 1fr; +} +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-cover { + display: none; +} +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-time { + display: none; +} +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-progress-wrap { + display: none; +} +.smpp-spotify-cover { + width: 104px; + height: 104px; + border-radius: 1.25rem; + object-fit: cover; + background: var(--color-base02); + visibility: hidden; +} +.smpp-spotify-meta { + min-width: 0; + display: flex; + flex-direction: column; + gap: 0.15rem; +} +.smpp-spotify-track { + font-weight: 650; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-track { + white-space: normal; + overflow: visible; + text-overflow: unset; +} +.smpp-spotify-artist, +.smpp-spotify-time { + font-size: 0.9rem; + color: var(--color-text-subdued, rgba(255, 255, 255, 0.7)); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.smpp-spotify-help { + display: none; + margin-top: 0.25rem; + padding: 0.65rem 0.8rem; + border-radius: 1rem; + border: 2px solid var(--color-base02); + background: var(--color-base01); + color: var(--color-text-subdued, rgba(255, 255, 255, 0.7)); + font-size: 0.9rem; +} +.smpp-spotify-help.show { + display: block; +} +.smpp-spotify-help-title { + font-weight: 700; + color: var(--color-text); + margin-bottom: 0.25rem; +} +.smpp-spotify-help-list { + margin: 0; + padding-left: 1.1rem; +} +.smpp-spotify-help-mono { + font-family: + ui-monospace, + SFMono-Regular, + Menlo, + Monaco, + Consolas, + "Liberation Mono", + "Courier New", + monospace; +} +.smpp-spotify-progress-wrap { + margin-top: 0.35rem; +} +.smpp-spotify-progress { + height: 10px; + border-radius: 9999px; + background: var(--color-base02); + border: 2px solid var(--color-base03); + overflow: hidden; +} +.smpp-spotify-progress-fill { + height: 100%; + width: 100%; + background: var(--color-accent); + border-radius: 9999px; + transform: scaleX(0); + transform-origin: left center; +} +.smpp-spotify-progress.smpp-spotify-progress-unknown { + opacity: 0.5; +} +.smpp-spotify-controls { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 0.5rem; + align-items: center; +} +.smpp-spotify-btn { + padding: 0.45rem 0.6rem; + border-radius: 0.9rem; + border: 3px solid var(--color-base03); + background: var(--color-base02); + color: var(--color-text); + cursor: pointer; + font-size: 1.1rem; + line-height: 1; + box-shadow: 0px 2px 10px 0px var(--color-shadow); +} +.smpp-spotify-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} +.enableAnimations .smpp-spotify-btn { + transition: + transform 0.15s ease, + filter 0.15s ease, + border-color 0.15s ease, + background-color 0.15s ease; +} +.smpp-spotify-btn:hover { + filter: brightness(115%); + border-color: var(--color-base04); +} +.smpp-spotify-btn:active { + scale: 0.96; +} +.smpp-spotify-btn-primary { + background: var(--color-accent); + border-color: var(--color-base03); + color: var(--color-black); +} +.smpp-spotify-open { + grid-column: 1 / -1; + padding: 0.5rem 0.75rem; + border-radius: 0.9rem; + border: 3px dashed var(--color-base03); + background: var(--color-base01); + color: var(--color-text); + cursor: pointer; + box-shadow: 0px 2px 10px 0px var(--color-shadow); +} +.smpp-spotify-preview { + gap: 0.25rem; +} +.smpp-spotify-preview-title { + font-size: 2rem; + font-weight: 700; + color: var(--color-base05); +} + /* src/styles/smpp-styles/tutorial-widget.css */ .lang-buttons { flex-direction: row; diff --git a/extension/index.js b/extension/index.js index 61a0adf4..524e7442 100644 --- a/extension/index.js +++ b/extension/index.js @@ -13467,6 +13467,319 @@ ${code}`; }; registerWidget(new CalendarWidget()); + // src/widgets/spotify.ts + var spotifyLogoSvg = ` + +`; + var SpotifyWidget = class extends WidgetBase { + #poll; + #rafId; + #els; + get category() { + return "other"; + } + get name() { + return "SpotifyWidget"; + } + async createContent() { + const root = document.createElement("div"); + root.classList.add("smpp-spotify-widget"); + const header = document.createElement("div"); + header.classList.add("smpp-spotify-header"); + root.appendChild(header); + const headerLeft = document.createElement("div"); + headerLeft.classList.add("smpp-spotify-header-left"); + header.appendChild(headerLeft); + const headerIcon = document.createElement("div"); + headerIcon.classList.add("smpp-spotify-icon", "smpp-spotify-icon-small"); + headerIcon.innerHTML = spotifyLogoSvg; + headerLeft.appendChild(headerIcon); + const title = document.createElement("div"); + title.classList.add("smpp-spotify-title"); + title.innerText = "Spotify"; + headerLeft.appendChild(title); + const body = document.createElement("div"); + body.classList.add("smpp-spotify-body"); + root.appendChild(body); + const cover = document.createElement("img"); + cover.classList.add("smpp-spotify-cover"); + cover.alt = ""; + cover.loading = "lazy"; + body.appendChild(cover); + const meta = document.createElement("div"); + meta.classList.add("smpp-spotify-meta"); + body.appendChild(meta); + const track = document.createElement("div"); + track.classList.add("smpp-spotify-track"); + track.innerText = "Open Spotify\u2026"; + meta.appendChild(track); + const artist = document.createElement("div"); + artist.classList.add("smpp-spotify-artist"); + artist.innerText = ""; + meta.appendChild(artist); + const time = document.createElement("div"); + time.classList.add("smpp-spotify-time"); + time.innerText = ""; + meta.appendChild(time); + const help = document.createElement("div"); + help.classList.add("smpp-spotify-help"); + help.innerHTML = ` +
Spotify not showing up?
+
    +
  1. Open open.spotify.com in another tab and start playing a song.
  2. +
  3. Try reloading the Spotify page.
  4. +
  5. Make sure you didn't accidently open the tab in a different browser or window.
  6. +
  7. Check if the Spotify tab isn't sleeping.
  8. +
+ `; + meta.appendChild(help); + const progressWrap = document.createElement("div"); + progressWrap.classList.add("smpp-spotify-progress-wrap"); + meta.appendChild(progressWrap); + const progressBar = document.createElement("div"); + progressBar.classList.add("smpp-spotify-progress"); + progressWrap.appendChild(progressBar); + const progressFill = document.createElement("div"); + progressFill.classList.add("smpp-spotify-progress-fill"); + progressBar.appendChild(progressFill); + const controls = document.createElement("div"); + controls.classList.add("smpp-spotify-controls"); + root.appendChild(controls); + const prevBtn = document.createElement("button"); + prevBtn.classList.add("smpp-spotify-btn"); + prevBtn.type = "button"; + prevBtn.innerText = "\u23EE"; + prevBtn.title = "Previous"; + controls.appendChild(prevBtn); + const playPauseBtn = document.createElement("button"); + playPauseBtn.classList.add("smpp-spotify-btn", "smpp-spotify-btn-primary"); + playPauseBtn.type = "button"; + playPauseBtn.innerText = "\u23EF"; + playPauseBtn.title = "Play/Pause"; + controls.appendChild(playPauseBtn); + const nextBtn = document.createElement("button"); + nextBtn.classList.add("smpp-spotify-btn"); + nextBtn.type = "button"; + nextBtn.innerText = "\u23ED"; + nextBtn.title = "Next"; + controls.appendChild(nextBtn); + const openBtn = document.createElement("button"); + openBtn.classList.add("smpp-spotify-open"); + openBtn.type = "button"; + openBtn.innerText = "Open Spotify for me"; + controls.appendChild(openBtn); + const upgradeCoverUrl = (url) => { + if (!url) return null; + return url.replace(/(ab67616d|ab676161)[0-9a-f]{8}/i, "$10000b273"); + }; + const parseTimeToSeconds = (t5) => { + if (!t5) return null; + const parts = t5.trim().split(":").map((p4) => Number(p4)); + if (parts.some((p4) => Number.isNaN(p4))) return null; + if (parts.length === 2) { + const [m2, s4] = parts; + return m2 * 60 + s4; + } + if (parts.length === 3) { + const [h4, m2, s4] = parts; + return h4 * 3600 + m2 * 60 + s4; + } + return null; + }; + const formatSeconds = (totalSeconds) => { + if (totalSeconds === null || totalSeconds === void 0) return ""; + const clamped = Math.max(0, Math.floor(totalSeconds)); + const h4 = Math.floor(clamped / 3600); + const m2 = Math.floor(clamped % 3600 / 60); + const s4 = clamped % 60; + if (h4 > 0) { + return `${h4}:${String(m2).padStart(2, "0")}:${String(s4).padStart(2, "0")}`; + } + return `${m2}:${String(s4).padStart(2, "0")}`; + }; + let playback = null; + let lastShownSecond = null; + const updatePlaybackUI = () => { + if (!playback) return; + const now = Date.now(); + const elapsedSeconds = playback.isPlaying ? (now - playback.syncedAtMs) / 1e3 : 0; + const positionSeconds = playback.basePositionSeconds + elapsedSeconds; + if (playback.durationSeconds && playback.durationSeconds > 0) { + const pct = Math.max( + 0, + Math.min(1, positionSeconds / playback.durationSeconds) + ); + progressFill.style.transform = `scaleX(${pct})`; + progressBar.classList.remove("smpp-spotify-progress-unknown"); + const wholeSecond = Math.floor(positionSeconds); + if (wholeSecond !== lastShownSecond) { + lastShownSecond = wholeSecond; + time.innerText = `${formatSeconds(positionSeconds)} / ${formatSeconds( + playback.durationSeconds + )}`; + } + } else { + progressFill.style.transform = "scaleX(0)"; + progressBar.classList.add("smpp-spotify-progress-unknown"); + const wholeSecond = Math.floor(positionSeconds); + if (wholeSecond !== lastShownSecond) { + lastShownSecond = wholeSecond; + time.innerText = formatSeconds(positionSeconds); + } + } + }; + const applyState = (state) => { + root.classList.remove("smpp-spotify-disconnected"); + help.classList.remove("show"); + track.innerText = state?.title || "Spotify"; + artist.innerText = state?.artist || ""; + if (state?.cover) { + cover.src = upgradeCoverUrl(state.cover) || state.cover; + cover.style.visibility = "visible"; + } else { + cover.removeAttribute("src"); + cover.style.visibility = "hidden"; + } + prevBtn.disabled = state?.canPrevious === false; + nextBtn.disabled = state?.canNext === false; + const pos = parseTimeToSeconds(state?.positionText); + const dur = parseTimeToSeconds(state?.durationText); + playback = pos !== null ? { + basePositionSeconds: pos, + durationSeconds: dur !== null ? dur : null, + isPlaying: state?.isPlaying === true, + syncedAtMs: Date.now() + } : null; + lastShownSecond = null; + updatePlaybackUI(); + }; + const setDisconnected = (msg) => { + root.classList.add("smpp-spotify-disconnected"); + help.classList.add("show"); + track.innerText = msg || "Open Spotify in another tab"; + artist.innerText = ""; + time.innerText = ""; + progressFill.style.transform = "scaleX(0)"; + progressBar.classList.add("smpp-spotify-progress-unknown"); + playback = null; + lastShownSecond = null; + cover.removeAttribute("src"); + cover.style.visibility = "hidden"; + prevBtn.disabled = true; + playPauseBtn.disabled = true; + nextBtn.disabled = true; + }; + const refresh = async () => { + try { + const resp = await browser.runtime.sendMessage({ + action: "spotifyGetState" + }); + if (!resp || resp.ok === false) { + setDisconnected(resp?.humanError); + return; + } + playPauseBtn.disabled = false; + applyState(resp); + } catch (e5) { + console.error("[smpp][spotify-widget] refresh failed", e5); + setDisconnected("Spotify not reachable"); + } + }; + const run = async (command) => { + if (command === "playPause" && playback) { + playback = { + ...playback, + isPlaying: !playback.isPlaying, + syncedAtMs: Date.now() + }; + lastShownSecond = null; + updatePlaybackUI(); + } + if ((command === "next" || command === "previous") && playback) { + playback = null; + lastShownSecond = null; + time.innerText = ""; + progressFill.style.transform = "scaleX(0)"; + progressBar.classList.add("smpp-spotify-progress-unknown"); + } + try { + const resp = await browser.runtime.sendMessage({ + action: "spotifyCommand", + command + }); + if (!resp || resp.ok === false) { + setDisconnected(resp?.humanError); + return; + } + if (resp?.state?.ok) { + playPauseBtn.disabled = false; + applyState(resp.state); + } else { + await refresh(); + } + setTimeout(() => this.#els?.refresh?.(), 300); + } catch (e5) { + console.error("[smpp][spotify-widget] command failed", e5); + setDisconnected("Spotify not reachable"); + } + }; + prevBtn.addEventListener("click", () => run("previous")); + playPauseBtn.addEventListener("click", () => run("playPause")); + nextBtn.addEventListener("click", () => run("next")); + openBtn.addEventListener("click", () => { + window.open("https://open.spotify.com", "_blank", "noopener,noreferrer"); + }); + this.#els = { refresh }; + setDisconnected(); + await refresh(); + this.#poll = window.setInterval(refresh, 5e3); + const rafTick = () => { + updatePlaybackUI(); + this.#rafId = requestAnimationFrame(rafTick); + }; + this.#rafId = requestAnimationFrame(rafTick); + return root; + } + async createPreview() { + const div = document.createElement("div"); + div.classList.add("smpp-spotify-widget", "smpp-spotify-preview"); + div.style.background = "none"; + div.style.display = "flex"; + div.style.flexDirection = "column"; + div.style.alignItems = "center"; + div.style.justifyContent = "center"; + div.style.gap = "12px"; + div.style.padding = "12px"; + div.style.minHeight = "auto"; + const title = document.createElement("div"); + title.classList.add("smpp-spotify-preview-title"); + title.innerText = "Spotify"; + div.appendChild(title); + const icon = document.createElement("div"); + icon.classList.add("smpp-spotify-icon", "smpp-spotify-icon-large"); + icon.innerHTML = spotifyLogoSvg; + div.appendChild(icon); + return div; + } + async onRemove() { + if (this.#poll) { + clearInterval(this.#poll); + this.#poll = null; + } + if (this.#rafId) { + cancelAnimationFrame(this.#rafId); + this.#rafId = null; + } + } + }; + registerWidget(new SpotifyWidget()); + // src/fixes-utils/migration.ts async function updateSettings() { let data2 = await browser.runtime.sendMessage({ diff --git a/extension/manifest.json b/extension/manifest.json index c80cce67..dd92d764 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -50,5 +50,6 @@ "scripts": ["background-scripts/index.js"], "type": "module" }, - "permissions": ["storage", "unlimitedStorage"] + "permissions": ["storage", "unlimitedStorage", "tabs", "scripting"], + "host_permissions": ["https://open.spotify.com/*"] } diff --git a/src/background-scripts/index.ts b/src/background-scripts/index.ts index 9a1da513..ce5af956 100644 --- a/src/background-scripts/index.ts +++ b/src/background-scripts/index.ts @@ -27,6 +27,7 @@ import { getSharedTheme, purgeThemeShareCache, } from "./themes.js"; +import { handleSpotifyMessage } from "./spotify.js"; browser.runtime.onMessage.addListener( (message: any, _sender: any, sendResponse: (resp: any) => void) => { @@ -226,6 +227,11 @@ async function handleMessage(message: any, sendResponse: (resp: any) => void) { await browser.storage.local.set(data); sendResponse({ success: true }); } + + if (await handleSpotifyMessage(message, sendResponse)) { + return; + } + // Migration if (message.action === "getDataVersion") { let dataVersion = await browser.storage.local.get("dataVersion"); diff --git a/src/background-scripts/spotify.ts b/src/background-scripts/spotify.ts new file mode 100644 index 00000000..bcd2822b --- /dev/null +++ b/src/background-scripts/spotify.ts @@ -0,0 +1,185 @@ +import { browser } from "../common/utils.js"; + +type SpotifyCommand = "next" | "previous" | "playPause"; + +// Runs inside the Spotify tab (injected via browser.scripting.executeScript) +async function spotifyController( + command: SpotifyCommand | null +): Promise { + const delay = (ms: number) => new Promise((r) => setTimeout(r, ms)); + + const nowPlayingRoot = () => + document.querySelector('aside[data-testid="now-playing-bar"]'); + + const getButton = (testId: string) => + document.querySelector(`button[data-testid="${testId}"]`) as + | HTMLButtonElement + | null; + + const getText = (testId: string) => { + const el = document.querySelector(`[data-testid="${testId}"]`) as + | HTMLElement + | null; + const t = el?.innerText?.trim(); + return t ? t : null; + }; + + const inferIsPlayingFromLabel = (label: string | null) => { + if (!label) return null; + const l = label.toLowerCase(); + // aria-label describes the action of the button: + // - "Pauze"/"Pause" => currently playing + // - "Afspelen"/"Play" => currently paused + if (l.includes("pause") || l.includes("pauze") || l.includes("pausa")) { + return true; + } + if (l.includes("play") || l.includes("afspelen") || l.includes("lecture")) { + return false; + } + return null; + }; + + const getState = () => { + const root = nowPlayingRoot() as HTMLElement | null; + if (!root) { + return { + ok: false, + humanError: "Spotify player not found on this page.", + }; + } + + const title = + (root.querySelector('[data-testid="context-item-info-title"]') + ?.textContent || "") + .trim() || null; + const artist = + (root.querySelector('[data-testid="context-item-info-subtitles"]') + ?.textContent || "") + .trim() || null; + const cover = + (root.querySelector( + 'img[data-testid="cover-art-image"]' + ) as HTMLImageElement | null)?.src || null; + + const nextBtn = getButton("control-button-skip-forward"); + const prevBtn = getButton("control-button-skip-back"); + const playPauseBtn = getButton("control-button-playpause"); + const playPauseLabel = playPauseBtn?.getAttribute("aria-label") || null; + + return { + ok: true, + title, + artist, + cover, + canNext: nextBtn ? !nextBtn.disabled : null, + canPrevious: prevBtn ? !prevBtn.disabled : null, + playPauseLabel, + isPlaying: inferIsPlayingFromLabel(playPauseLabel), + positionText: getText("playback-position"), + durationText: getText("playback-duration"), + }; + }; + + if (!command) { + return getState(); + } + + const before: any = getState(); + if (!before || before.ok === false) { + return before; + } + + const testId = + command === "next" + ? "control-button-skip-forward" + : command === "previous" + ? "control-button-skip-back" + : "control-button-playpause"; + + const button = getButton(testId); + if (!button) { + return { + ok: false, + humanError: `Spotify control button not found (${command}).`, + }; + } + if (button.disabled) { + return { + ok: false, + humanError: `Spotify control button is disabled (${command}).`, + }; + } + + button.click(); + + const changed = (after: any) => { + if (!after || after.ok === false) return false; + if (command === "playPause") { + if (before.isPlaying !== null && after.isPlaying !== null) { + return after.isPlaying !== before.isPlaying; + } + return after.playPauseLabel !== before.playPauseLabel; + } + return after.title !== before.title || after.cover !== before.cover; + }; + + for (let i = 0; i < 10; i++) { + await delay(150); + const after: any = getState(); + if (changed(after)) { + return after; + } + } + + return getState(); +} + +async function runOnSpotifyTab(command: SpotifyCommand | null) { + const tabs = await browser.tabs.query({ url: ["*://open.spotify.com/*"] }); + if (!tabs || tabs.length === 0) { + return { + ok: false, + humanError: "No Spotify tab found. Open https://open.spotify.com in another tab.", + }; + } + + const targetTab = + tabs.find((t: any) => t.audible) || tabs.find((t: any) => t.active) || tabs[0]; + + try { + const results = await browser.scripting.executeScript({ + target: { tabId: targetTab.id }, + func: spotifyController as any, + args: [command], + }); + return results?.[0]?.result ?? { ok: false, humanError: "Spotify returned no data." }; + } catch (e) { + console.error("[smpp][spotify] executeScript failed", e); + return { + ok: false, + humanError: + "Couldn't run the Spotify controller. Make sure the Spotify tab is open on open.spotify.com.", + }; + } +} + +export async function handleSpotifyMessage(message: any, sendResponse: (resp: any) => void) { + if (message.action === "spotifyGetState") { + const state = await runOnSpotifyTab(null); + sendResponse(state); + return true; + } + + if (message.action === "spotifyCommand") { + const state = await runOnSpotifyTab(message.command); + if (!state || state.ok === false) { + sendResponse(state); + return true; + } + sendResponse({ ok: true, state }); + return true; + } + + return false; +} + diff --git a/src/index.ts b/src/index.ts index 84de5061..a0f9c09c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import "./styles/smpp-styles/plant.css"; import "./styles/smpp-styles/clock.css"; import "./styles/smpp-styles/calendar.css"; import "./styles/smpp-styles/widgets.css"; +import "./styles/smpp-styles/spotify-widget.css"; import "./styles/smpp-styles/tutorial-widget.css"; import "./styles/smpp-styles/settings.css"; import "./styles/smpp-styles/assignments.css"; diff --git a/src/main-features/main.ts b/src/main-features/main.ts index de00b4e3..dd19a781 100644 --- a/src/main-features/main.ts +++ b/src/main-features/main.ts @@ -24,6 +24,7 @@ import "../widgets/planner.ts"; import "../widgets/weather.ts"; import "../widgets/clock.ts"; import "../widgets/calendar.ts"; +import "../widgets/spotify.ts"; import { browser, getExtensionImage, randomChance } from "../common/utils.js"; import { getPfpLink } from "../fixes-utils/utils.js"; diff --git a/src/styles/smpp-styles/spotify-widget.css b/src/styles/smpp-styles/spotify-widget.css new file mode 100644 index 00000000..81554110 --- /dev/null +++ b/src/styles/smpp-styles/spotify-widget.css @@ -0,0 +1,232 @@ +.smpp-spotify-widget { + display: flex; + flex-direction: column; + gap: 0.75rem; + color: var(--color-text); +} + +.smpp-spotify-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; +} + +.smpp-spotify-header-left { + display: flex; + align-items: center; + gap: 0.6rem; +} + +.smpp-spotify-title { + font-weight: 700; + font-size: 1.1rem; +} + +.smpp-spotify-icon { + display: grid; + place-items: center; +} + +.smpp-spotify-logo { + display: block; + width: 100%; + height: 100%; +} + +.smpp-spotify-icon-small { + width: 28px; + height: 28px; +} + +.smpp-spotify-icon-large { + width: 128px; + height: 128px; + margin-bottom: 0.5rem; + filter: drop-shadow(0px 2px 10px var(--color-shadow)); +} + +.smpp-spotify-body { + display: grid; + grid-template-columns: 104px 1fr; + gap: 0.75rem; + align-items: center; +} + +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-body { + grid-template-columns: 1fr; +} + +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-cover { + display: none; +} + +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-time { + display: none; +} + +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-progress-wrap { + display: none; +} + +.smpp-spotify-cover { + width: 104px; + height: 104px; + border-radius: 1.25rem; + object-fit: cover; + background: var(--color-base02); + visibility: hidden; +} + +.smpp-spotify-meta { + min-width: 0; + display: flex; + flex-direction: column; + gap: 0.15rem; +} + +.smpp-spotify-track { + font-weight: 650; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.smpp-spotify-widget.smpp-spotify-disconnected .smpp-spotify-track { + white-space: normal; + overflow: visible; + text-overflow: unset; +} + +.smpp-spotify-artist, +.smpp-spotify-time { + font-size: 0.9rem; + color: var(--color-text-subdued, rgba(255, 255, 255, 0.7)); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.smpp-spotify-help { + display: none; + margin-top: 0.25rem; + padding: 0.65rem 0.8rem; + border-radius: 1rem; + border: 2px solid var(--color-base02); + background: var(--color-base01); + color: var(--color-text-subdued, rgba(255, 255, 255, 0.7)); + font-size: 0.9rem; +} + +.smpp-spotify-help.show { + display: block; +} + +.smpp-spotify-help-title { + font-weight: 700; + color: var(--color-text); + margin-bottom: 0.25rem; +} + +.smpp-spotify-help-list { + margin: 0; + padding-left: 1.1rem; +} + +.smpp-spotify-help-mono { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; +} + +.smpp-spotify-progress-wrap { + margin-top: 0.35rem; +} + +.smpp-spotify-progress { + height: 10px; + border-radius: 9999px; + background: var(--color-base02); + border: 2px solid var(--color-base03); + overflow: hidden; +} + +.smpp-spotify-progress-fill { + height: 100%; + width: 100%; + background: var(--color-accent); + border-radius: 9999px; + transform: scaleX(0); + transform-origin: left center; +} + +.smpp-spotify-progress.smpp-spotify-progress-unknown { + opacity: 0.5; +} + +.smpp-spotify-controls { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 0.5rem; + align-items: center; +} + +.smpp-spotify-btn { + padding: 0.45rem 0.6rem; + border-radius: 0.9rem; + border: 3px solid var(--color-base03); + background: var(--color-base02); + color: var(--color-text); + cursor: pointer; + font-size: 1.1rem; + line-height: 1; + box-shadow: 0px 2px 10px 0px var(--color-shadow); +} + +.smpp-spotify-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.enableAnimations .smpp-spotify-btn { + transition: + transform 0.15s ease, + filter 0.15s ease, + border-color 0.15s ease, + background-color 0.15s ease; +} + +.smpp-spotify-btn:hover { + filter: brightness(115%); + border-color: var(--color-base04); +} + +.smpp-spotify-btn:active { + scale: 0.96; +} + +.smpp-spotify-btn-primary { + background: var(--color-accent); + border-color: var(--color-base03); + color: var(--color-black); +} + +.smpp-spotify-open { + grid-column: 1 / -1; + padding: 0.5rem 0.75rem; + border-radius: 0.9rem; + border: 3px dashed var(--color-base03); + background: var(--color-base01); + color: var(--color-text); + cursor: pointer; + box-shadow: 0px 2px 10px 0px var(--color-shadow); +} + +.smpp-spotify-preview { + gap: 0.25rem; +} + +.smpp-spotify-preview-title { + font-size: 2rem; + font-weight: 700; + color: var(--color-base05); +} diff --git a/src/widgets/spotify.ts b/src/widgets/spotify.ts new file mode 100644 index 00000000..016f9b4c --- /dev/null +++ b/src/widgets/spotify.ts @@ -0,0 +1,380 @@ +// @ts-nocheck +import { browser } from "../common/utils.js"; +import { WidgetBase, registerWidget } from "./widgets.js"; + +const spotifyLogoSvg = ` + +`; + +class SpotifyWidget extends WidgetBase { + #poll; + #rafId; + #els; + + get category() { + return "other"; + } + + get name() { + return "SpotifyWidget"; + } + + async createContent() { + const root = document.createElement("div"); + root.classList.add("smpp-spotify-widget"); + + const header = document.createElement("div"); + header.classList.add("smpp-spotify-header"); + root.appendChild(header); + + const headerLeft = document.createElement("div"); + headerLeft.classList.add("smpp-spotify-header-left"); + header.appendChild(headerLeft); + + const headerIcon = document.createElement("div"); + headerIcon.classList.add("smpp-spotify-icon", "smpp-spotify-icon-small"); + headerIcon.innerHTML = spotifyLogoSvg; + headerLeft.appendChild(headerIcon); + + const title = document.createElement("div"); + title.classList.add("smpp-spotify-title"); + title.innerText = "Spotify"; + headerLeft.appendChild(title); + + const body = document.createElement("div"); + body.classList.add("smpp-spotify-body"); + root.appendChild(body); + + const cover = document.createElement("img"); + cover.classList.add("smpp-spotify-cover"); + cover.alt = ""; + cover.loading = "lazy"; + body.appendChild(cover); + + const meta = document.createElement("div"); + meta.classList.add("smpp-spotify-meta"); + body.appendChild(meta); + + const track = document.createElement("div"); + track.classList.add("smpp-spotify-track"); + track.innerText = "Open Spotify…"; + meta.appendChild(track); + + const artist = document.createElement("div"); + artist.classList.add("smpp-spotify-artist"); + artist.innerText = ""; + meta.appendChild(artist); + + const time = document.createElement("div"); + time.classList.add("smpp-spotify-time"); + time.innerText = ""; + meta.appendChild(time); + + const help = document.createElement("div"); + help.classList.add("smpp-spotify-help"); + help.innerHTML = ` +
Spotify not showing up?
+
    +
  1. Open open.spotify.com in another tab and start playing a song.
  2. +
  3. Try reloading the Spotify page.
  4. +
  5. Make sure you didn't accidently open the tab in a different browser or window.
  6. +
  7. Check if the Spotify tab isn't sleeping.
  8. +
+ `; + meta.appendChild(help); + + const progressWrap = document.createElement("div"); + progressWrap.classList.add("smpp-spotify-progress-wrap"); + meta.appendChild(progressWrap); + + const progressBar = document.createElement("div"); + progressBar.classList.add("smpp-spotify-progress"); + progressWrap.appendChild(progressBar); + + const progressFill = document.createElement("div"); + progressFill.classList.add("smpp-spotify-progress-fill"); + progressBar.appendChild(progressFill); + + const controls = document.createElement("div"); + controls.classList.add("smpp-spotify-controls"); + root.appendChild(controls); + + const prevBtn = document.createElement("button"); + prevBtn.classList.add("smpp-spotify-btn"); + prevBtn.type = "button"; + prevBtn.innerText = "⏮"; + prevBtn.title = "Previous"; + controls.appendChild(prevBtn); + + const playPauseBtn = document.createElement("button"); + playPauseBtn.classList.add("smpp-spotify-btn", "smpp-spotify-btn-primary"); + playPauseBtn.type = "button"; + playPauseBtn.innerText = "⏯"; + playPauseBtn.title = "Play/Pause"; + controls.appendChild(playPauseBtn); + + const nextBtn = document.createElement("button"); + nextBtn.classList.add("smpp-spotify-btn"); + nextBtn.type = "button"; + nextBtn.innerText = "⏭"; + nextBtn.title = "Next"; + controls.appendChild(nextBtn); + + const openBtn = document.createElement("button"); + openBtn.classList.add("smpp-spotify-open"); + openBtn.type = "button"; + openBtn.innerText = "Open Spotify for me"; + controls.appendChild(openBtn); + + const upgradeCoverUrl = (url?: string | null) => { + if (!url) return null; + // Spotify image URLs include a size segment like: + // https://i.scdn.co/image/ab67616d00004851 + // Upgrade to 640x640 (0000b273) when possible. + return url.replace(/(ab67616d|ab676161)[0-9a-f]{8}/i, "$10000b273"); + }; + + const parseTimeToSeconds = (t?: string | null) => { + if (!t) return null; + const parts = t.trim().split(":").map((p) => Number(p)); + if (parts.some((p) => Number.isNaN(p))) return null; + if (parts.length === 2) { + const [m, s] = parts; + return m * 60 + s; + } + if (parts.length === 3) { + const [h, m, s] = parts; + return h * 3600 + m * 60 + s; + } + return null; + }; + + const formatSeconds = (totalSeconds?: number | null) => { + if (totalSeconds === null || totalSeconds === undefined) return ""; + const clamped = Math.max(0, Math.floor(totalSeconds)); + const h = Math.floor(clamped / 3600); + const m = Math.floor((clamped % 3600) / 60); + const s = clamped % 60; + if (h > 0) { + return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`; + } + return `${m}:${String(s).padStart(2, "0")}`; + }; + + let playback: null | { + basePositionSeconds: number; + durationSeconds: number | null; + isPlaying: boolean; + syncedAtMs: number; + } = null; + + let lastShownSecond: number | null = null; + + const updatePlaybackUI = () => { + if (!playback) return; + + const now = Date.now(); + const elapsedSeconds = playback.isPlaying + ? (now - playback.syncedAtMs) / 1000 + : 0; + const positionSeconds = playback.basePositionSeconds + elapsedSeconds; + + if (playback.durationSeconds && playback.durationSeconds > 0) { + const pct = Math.max( + 0, + Math.min(1, positionSeconds / playback.durationSeconds) + ); + progressFill.style.transform = `scaleX(${pct})`; + progressBar.classList.remove("smpp-spotify-progress-unknown"); + const wholeSecond = Math.floor(positionSeconds); + if (wholeSecond !== lastShownSecond) { + lastShownSecond = wholeSecond; + time.innerText = `${formatSeconds(positionSeconds)} / ${formatSeconds( + playback.durationSeconds + )}`; + } + } else { + progressFill.style.transform = "scaleX(0)"; + progressBar.classList.add("smpp-spotify-progress-unknown"); + const wholeSecond = Math.floor(positionSeconds); + if (wholeSecond !== lastShownSecond) { + lastShownSecond = wholeSecond; + time.innerText = formatSeconds(positionSeconds); + } + } + }; + + const applyState = (state: any) => { + root.classList.remove("smpp-spotify-disconnected"); + help.classList.remove("show"); + track.innerText = state?.title || "Spotify"; + artist.innerText = state?.artist || ""; + if (state?.cover) { + cover.src = upgradeCoverUrl(state.cover) || state.cover; + cover.style.visibility = "visible"; + } else { + cover.removeAttribute("src"); + cover.style.visibility = "hidden"; + } + + prevBtn.disabled = state?.canPrevious === false; + nextBtn.disabled = state?.canNext === false; + + const pos = parseTimeToSeconds(state?.positionText); + const dur = parseTimeToSeconds(state?.durationText); + playback = + pos !== null + ? { + basePositionSeconds: pos, + durationSeconds: dur !== null ? dur : null, + isPlaying: state?.isPlaying === true, + syncedAtMs: Date.now(), + } + : null; + + lastShownSecond = null; + updatePlaybackUI(); + }; + + const setDisconnected = (msg?: string) => { + root.classList.add("smpp-spotify-disconnected"); + help.classList.add("show"); + track.innerText = msg || "Open Spotify in another tab"; + artist.innerText = ""; + time.innerText = ""; + progressFill.style.transform = "scaleX(0)"; + progressBar.classList.add("smpp-spotify-progress-unknown"); + playback = null; + lastShownSecond = null; + cover.removeAttribute("src"); + cover.style.visibility = "hidden"; + prevBtn.disabled = true; + playPauseBtn.disabled = true; + nextBtn.disabled = true; + }; + + const refresh = async () => { + try { + const resp = await browser.runtime.sendMessage({ + action: "spotifyGetState", + }); + if (!resp || resp.ok === false) { + setDisconnected(resp?.humanError); + return; + } + playPauseBtn.disabled = false; + applyState(resp); + } catch (e) { + console.error("[smpp][spotify-widget] refresh failed", e); + setDisconnected("Spotify not reachable"); + } + }; + + const run = async (command: "previous" | "playPause" | "next") => { + // Optimistic UI updates so we don't keep progressing on stale state + if (command === "playPause" && playback) { + playback = { + ...playback, + isPlaying: !playback.isPlaying, + syncedAtMs: Date.now(), + }; + lastShownSecond = null; + updatePlaybackUI(); + } + if ((command === "next" || command === "previous") && playback) { + playback = null; + lastShownSecond = null; + time.innerText = ""; + progressFill.style.transform = "scaleX(0)"; + progressBar.classList.add("smpp-spotify-progress-unknown"); + } + + try { + const resp = await browser.runtime.sendMessage({ + action: "spotifyCommand", + command, + }); + if (!resp || resp.ok === false) { + setDisconnected(resp?.humanError); + return; + } + if (resp?.state?.ok) { + playPauseBtn.disabled = false; + applyState(resp.state); + } else { + await refresh(); + } + // One extra quick resync, because Spotify UI sometimes updates slightly after the click. + setTimeout(() => this.#els?.refresh?.(), 300); + } catch (e) { + console.error("[smpp][spotify-widget] command failed", e); + setDisconnected("Spotify not reachable"); + } + }; + + prevBtn.addEventListener("click", () => run("previous")); + playPauseBtn.addEventListener("click", () => run("playPause")); + nextBtn.addEventListener("click", () => run("next")); + openBtn.addEventListener("click", () => { + window.open("https://open.spotify.com", "_blank", "noopener,noreferrer"); + }); + + this.#els = { refresh }; + setDisconnected(); + await refresh(); + + this.#poll = window.setInterval(refresh, 5000); + const rafTick = () => { + updatePlaybackUI(); + this.#rafId = requestAnimationFrame(rafTick); + }; + this.#rafId = requestAnimationFrame(rafTick); + + return root; + } + + async createPreview() { + const div = document.createElement("div"); + div.classList.add("smpp-spotify-widget", "smpp-spotify-preview"); + + div.style.background = "none"; + div.style.display = "flex"; + div.style.flexDirection = "column"; + div.style.alignItems = "center"; + div.style.justifyContent = "center"; + div.style.gap = "12px"; + div.style.padding = "12px"; + div.style.minHeight = "auto"; + + const title = document.createElement("div"); + title.classList.add("smpp-spotify-preview-title"); + title.innerText = "Spotify"; + div.appendChild(title); + + const icon = document.createElement("div"); + icon.classList.add("smpp-spotify-icon", "smpp-spotify-icon-large"); + icon.innerHTML = spotifyLogoSvg; + div.appendChild(icon); + + return div; + } + + async onRemove() { + if (this.#poll) { + clearInterval(this.#poll); + this.#poll = null; + } + if (this.#rafId) { + cancelAnimationFrame(this.#rafId); + this.#rafId = null; + } + } +} + +registerWidget(new SpotifyWidget()); From 014564c06b3d4eb7c8c715cc1504a40cffde467b Mon Sep 17 00:00:00 2001 From: Super_man2775 Date: Tue, 21 Apr 2026 15:59:19 +0200 Subject: [PATCH 2/2] fix spotify widget error handling --- extension/background-scripts/index.js | 8 +++++++- extension/index.js | 4 ++-- src/background-scripts/spotify.ts | 5 ++--- src/widgets/spotify.ts | 6 +++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/extension/background-scripts/index.js b/extension/background-scripts/index.js index fea7629c..55aecfe0 100644 --- a/extension/background-scripts/index.js +++ b/extension/background-scripts/index.js @@ -1757,6 +1757,7 @@ imageCompression.getDataUrlFromFile = getDataUrlFromFile, imageCompression.getFilefromDataUrl = getFilefromDataUrl, imageCompression.loadImage = loadImage, imageCompression.drawImageInCanvas = drawImageInCanvas, imageCompression.drawFileInCanvas = drawFileInCanvas, imageCompression.canvasToFile = canvasToFile, imageCompression.getExifOrientation = getExifOrientation, imageCompression.handleMaxWidthOrHeight = handleMaxWidthOrHeight, imageCompression.followExifOrientation = followExifOrientation, imageCompression.cleanupCanvasMemory = cleanupCanvasMemory, imageCompression.isAutoOrientationInBrowser = isAutoOrientationInBrowser, imageCompression.approximateBelowMaximumCanvasSizeOfBrowser = approximateBelowMaximumCanvasSizeOfBrowser, imageCompression.copyExifWithoutOrientation = copyExifWithoutOrientation, imageCompression.getBrowserName = getBrowserName, imageCompression.version = "2.0.2"; // src/common/utils.ts + var DEBUG = false; var browser; if (browser == void 0) { browser = chrome; @@ -1799,6 +1800,11 @@ } return object; } + function sendDebug(...messages) { + if (DEBUG) { + console.log(...messages); + } + } var FNV_PRIME = 0x0100000001b3n; var FNV_OFFSET = 0xcbf29ce484222325n; function fnv1aHash(byteArray) { @@ -2438,7 +2444,7 @@ }); return results?.[0]?.result ?? { ok: false, humanError: "Spotify returned no data." }; } catch (e2) { - console.error("[smpp][spotify] executeScript failed", e2); + sendDebug("[smpp][spotify] executeScript failed", e2); return { ok: false, humanError: "Couldn't run the Spotify controller. Make sure the Spotify tab is open on open.spotify.com." diff --git a/extension/index.js b/extension/index.js index 524e7442..c746fede 100644 --- a/extension/index.js +++ b/extension/index.js @@ -13687,7 +13687,7 @@ ${code}`; playPauseBtn.disabled = false; applyState(resp); } catch (e5) { - console.error("[smpp][spotify-widget] refresh failed", e5); + sendDebug("[smpp][spotify-widget] refresh failed", e5); setDisconnected("Spotify not reachable"); } }; @@ -13725,7 +13725,7 @@ ${code}`; } setTimeout(() => this.#els?.refresh?.(), 300); } catch (e5) { - console.error("[smpp][spotify-widget] command failed", e5); + sendDebug("[smpp][spotify-widget] command failed", e5); setDisconnected("Spotify not reachable"); } }; diff --git a/src/background-scripts/spotify.ts b/src/background-scripts/spotify.ts index bcd2822b..34725d3f 100644 --- a/src/background-scripts/spotify.ts +++ b/src/background-scripts/spotify.ts @@ -1,4 +1,4 @@ -import { browser } from "../common/utils.js"; +import { browser, sendDebug } from "../common/utils.js"; type SpotifyCommand = "next" | "previous" | "playPause"; @@ -154,7 +154,7 @@ async function runOnSpotifyTab(command: SpotifyCommand | null) { }); return results?.[0]?.result ?? { ok: false, humanError: "Spotify returned no data." }; } catch (e) { - console.error("[smpp][spotify] executeScript failed", e); + sendDebug("[smpp][spotify] executeScript failed", e); return { ok: false, humanError: @@ -182,4 +182,3 @@ export async function handleSpotifyMessage(message: any, sendResponse: (resp: an return false; } - diff --git a/src/widgets/spotify.ts b/src/widgets/spotify.ts index 016f9b4c..bccbdec0 100644 --- a/src/widgets/spotify.ts +++ b/src/widgets/spotify.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import { browser } from "../common/utils.js"; +import { browser, sendDebug } from "../common/utils.js"; import { WidgetBase, registerWidget } from "./widgets.js"; const spotifyLogoSvg = ` @@ -271,7 +271,7 @@ class SpotifyWidget extends WidgetBase { playPauseBtn.disabled = false; applyState(resp); } catch (e) { - console.error("[smpp][spotify-widget] refresh failed", e); + sendDebug("[smpp][spotify-widget] refresh failed", e); setDisconnected("Spotify not reachable"); } }; @@ -313,7 +313,7 @@ class SpotifyWidget extends WidgetBase { // One extra quick resync, because Spotify UI sometimes updates slightly after the click. setTimeout(() => this.#els?.refresh?.(), 300); } catch (e) { - console.error("[smpp][spotify-widget] command failed", e); + sendDebug("[smpp][spotify-widget] command failed", e); setDisconnected("Spotify not reachable"); } };