fix(js-helper): ignore empty bearer and remove local version fallback

- wait for a non-empty bearer token before starting and stop capture after 5 empty attempts or 30s
- use the worker latest endpoint as the final fallback and stop when no remote version is available
This commit is contained in:
amd64fox
2026-04-07 08:34:45 +03:00
parent ffd4a6b897
commit ef6601d186
+64 -40
View File
@@ -2,12 +2,11 @@
if (window.oneTime) return; if (window.oneTime) return;
window.oneTime = true; window.oneTime = true;
const REPORT_BASE_URL = "https://spotify-ingest-admin.amd64fox1.workers.dev"; const WORKER_BASE_URL = "https://spotify-ingest-admin.amd64fox1.workers.dev";
const SCRIPT_VERSION = "1.1.0"; const SCRIPT_VERSION = "1.2.0";
const SOURCE_LABELS = { const SOURCE_LABELS = {
REMOTE: "latest.json", REMOTE: "latest.json",
LOCAL: "window",
FIXED: "fixed-version" FIXED: "fixed-version"
}; };
@@ -56,19 +55,25 @@
? [String(window.__spotifyLatestUrl).trim()] ? [String(window.__spotifyLatestUrl).trim()]
: [ : [
"https://raw.githubusercontent.com/LoaderSpot/table/refs/heads/main/latest.json", "https://raw.githubusercontent.com/LoaderSpot/table/refs/heads/main/latest.json",
"https://raw.githack.com/LoaderSpot/table/main/latest.json" "https://raw.githack.com/LoaderSpot/table/main/latest.json",
`${WORKER_BASE_URL}/api/client/latest`
], ],
updateUrl: "https://spclient.wg.spotify.com/desktop-update/v2/update", updateUrl: "https://spclient.wg.spotify.com/desktop-update/v2/update",
reportEndpoint: `${REPORT_BASE_URL}/api/client/report`, reportEndpoint: `${WORKER_BASE_URL}/api/client/report`,
errorEndpoint: `${REPORT_BASE_URL}/api/client/error`, errorEndpoint: `${WORKER_BASE_URL}/api/client/error`,
reportTimeoutMs: 15000, reportTimeoutMs: 15000,
versionTimeoutMs: 10000, versionTimeoutMs: 10000,
desktopUpdateTimeoutMs: 8000, desktopUpdateTimeoutMs: 8000,
desktopUpdateMaxRetries: 1 desktopUpdateMaxRetries: 1,
tokenCaptureMaxAttempts: 5,
tokenCaptureTimeoutMs: 30000
}; };
const originalFetch = window.fetch; const originalFetch = window.fetch;
let runStarted = false; let runStarted = false;
let tokenCaptureStopped = false;
let tokenCaptureAttempts = 0;
let tokenCaptureTimeoutId = 0;
const SPOTIFY_VERSION_RE = /Spotify\/(\d+\.\d+\.\d+\.\d+)/; const SPOTIFY_VERSION_RE = /Spotify\/(\d+\.\d+\.\d+\.\d+)/;
@@ -88,24 +93,6 @@
}; };
} }
function getLocalSpotifyVersion() {
const versionSources = readVersionSourceSnapshot();
const sources = [
versionSources.clientInformationAppVersion,
versionSources.userAgent,
versionSources.navigatorAppVersion
];
for (const source of sources) {
const version = String(source || "").match(SPOTIFY_VERSION_RE)?.[1];
if (version) {
return version;
}
}
return "";
}
function readClientVersionSources() { function readClientVersionSources() {
const versionSources = readVersionSourceSnapshot(); const versionSources = readVersionSourceSnapshot();
@@ -195,12 +182,11 @@
} }
} }
const localVersion = getLocalSpotifyVersion();
return { return {
shortVersion: localVersion, shortVersion: "",
fullVersion: "", fullVersion: "",
spotifyAppVersion: buildSpotifyAppVersion(localVersion, SOURCE_LABELS.LOCAL), spotifyAppVersion: "",
sourceLabel: SOURCE_LABELS.LOCAL, sourceLabel: "",
remoteVersionFailed: true, remoteVersionFailed: true,
remoteShortVersion: "", remoteShortVersion: "",
remoteFullVersion: "" remoteFullVersion: ""
@@ -905,24 +891,62 @@
); );
} }
function extractBearerToken(authorization) {
const match = String(authorization || "").match(/^Bearer\s+(.+)$/i);
if (!match) {
return "";
}
return String(match[1] || "").trim();
}
function stopTokenCapture(reason) {
if (tokenCaptureStopped) {
return;
}
tokenCaptureStopped = true;
window.fetch = originalFetch;
if (tokenCaptureTimeoutId) {
clearTimeout(tokenCaptureTimeoutId);
tokenCaptureTimeoutId = 0;
}
if (reason === "max_attempts") {
console.warn(`Spotify token capture stopped after ${CONFIG.tokenCaptureMaxAttempts} empty bearer attempts.`);
} else if (reason === "timeout") {
console.warn(`Spotify token capture stopped after ${CONFIG.tokenCaptureTimeoutMs}ms timeout.`);
}
}
tokenCaptureTimeoutId = setTimeout(() => {
if (!runStarted) {
stopTokenCapture("timeout");
}
}, CONFIG.tokenCaptureTimeoutMs);
window.fetch = async function (...args) { window.fetch = async function (...args) {
const [input, init] = args; const [input, init] = args;
const headers = init?.headers || (input instanceof Request ? input.headers : null); const headers = init?.headers || (input instanceof Request ? input.headers : null);
const authorization = getHeaderValue(headers, "authorization"); const authorization = getHeaderValue(headers, "authorization");
if (!runStarted && isSpotifyAuthorizedRequest(getRequestUrl(input), authorization)) { if (!runStarted && !tokenCaptureStopped && isSpotifyAuthorizedRequest(getRequestUrl(input), authorization)) {
runStarted = true; tokenCaptureAttempts += 1;
window.fetch = originalFetch; const token = extractBearerToken(authorization);
if (token) {
runStarted = true;
stopTokenCapture("success");
const token = String(authorization).replace(/^Bearer\s+/i, ""); void runOnce(token).catch((error) => {
void runOnce(token).catch((error) => { const state = createState(token);
const state = createState(token); sendError(state, "uncaught", {
sendError(state, "uncaught", { phase: "uncaught_runOnce",
phase: "uncaught_runOnce", message: error?.message || String(error),
message: error?.message || String(error), stack: error?.stack || ""
stack: error?.stack || "" });
}); });
}); } else if (tokenCaptureAttempts >= CONFIG.tokenCaptureMaxAttempts) {
stopTokenCapture("max_attempts");
}
} }
return originalFetch.apply(this, args); return originalFetch.apply(this, args);