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

View File

@@ -2,12 +2,11 @@
if (window.oneTime) return;
window.oneTime = true;
const REPORT_BASE_URL = "https://spotify-ingest-admin.amd64fox1.workers.dev";
const SCRIPT_VERSION = "1.1.0";
const WORKER_BASE_URL = "https://spotify-ingest-admin.amd64fox1.workers.dev";
const SCRIPT_VERSION = "1.2.0";
const SOURCE_LABELS = {
REMOTE: "latest.json",
LOCAL: "window",
FIXED: "fixed-version"
};
@@ -56,19 +55,25 @@
? [String(window.__spotifyLatestUrl).trim()]
: [
"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",
reportEndpoint: `${REPORT_BASE_URL}/api/client/report`,
errorEndpoint: `${REPORT_BASE_URL}/api/client/error`,
reportEndpoint: `${WORKER_BASE_URL}/api/client/report`,
errorEndpoint: `${WORKER_BASE_URL}/api/client/error`,
reportTimeoutMs: 15000,
versionTimeoutMs: 10000,
desktopUpdateTimeoutMs: 8000,
desktopUpdateMaxRetries: 1
desktopUpdateMaxRetries: 1,
tokenCaptureMaxAttempts: 5,
tokenCaptureTimeoutMs: 30000
};
const originalFetch = window.fetch;
let runStarted = false;
let tokenCaptureStopped = false;
let tokenCaptureAttempts = 0;
let tokenCaptureTimeoutId = 0;
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() {
const versionSources = readVersionSourceSnapshot();
@@ -195,12 +182,11 @@
}
}
const localVersion = getLocalSpotifyVersion();
return {
shortVersion: localVersion,
shortVersion: "",
fullVersion: "",
spotifyAppVersion: buildSpotifyAppVersion(localVersion, SOURCE_LABELS.LOCAL),
sourceLabel: SOURCE_LABELS.LOCAL,
spotifyAppVersion: "",
sourceLabel: "",
remoteVersionFailed: true,
remoteShortVersion: "",
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) {
const [input, init] = args;
const headers = init?.headers || (input instanceof Request ? input.headers : null);
const authorization = getHeaderValue(headers, "authorization");
if (!runStarted && isSpotifyAuthorizedRequest(getRequestUrl(input), authorization)) {
runStarted = true;
window.fetch = originalFetch;
if (!runStarted && !tokenCaptureStopped && isSpotifyAuthorizedRequest(getRequestUrl(input), authorization)) {
tokenCaptureAttempts += 1;
const token = extractBearerToken(authorization);
if (token) {
runStarted = true;
stopTokenCapture("success");
const token = String(authorization).replace(/^Bearer\s+/i, "");
void runOnce(token).catch((error) => {
const state = createState(token);
sendError(state, "uncaught", {
phase: "uncaught_runOnce",
message: error?.message || String(error),
stack: error?.stack || ""
void runOnce(token).catch((error) => {
const state = createState(token);
sendError(state, "uncaught", {
phase: "uncaught_runOnce",
message: error?.message || String(error),
stack: error?.stack || ""
});
});
});
} else if (tokenCaptureAttempts >= CONFIG.tokenCaptureMaxAttempts) {
stopTokenCapture("max_attempts");
}
}
return originalFetch.apply(this, args);