Fix authenticated Suno WAV download flow

This commit is contained in:
DESKTOP-KSVGT20\shkim
2026-05-02 01:59:56 +09:00
parent bf60f3357d
commit 0e09a422bc
+18 -11
View File
@@ -30,7 +30,7 @@
(function () {
"use strict";
const SCRIPT_VERSION = "0.9.5";
const SCRIPT_VERSION = "0.9.6";
const STORAGE_KEY = "chatgptSunoPromptSlots";
const SLOT_COUNT = 2;
const isChatGPT = location.hostname.includes("chatgpt.com") || location.hostname.includes("chat.openai.com");
@@ -1024,6 +1024,15 @@
return data;
}
function hasHttpStatus(error, statuses) {
const message = String(error?.message || error || "");
return statuses.some((status) => message.startsWith(`${status} `) || message.includes(` ${status} `));
}
function shouldUseTampermonkeyFirst(url) {
return /^https?:\/\/cdn[0-9]*\.suno\.ai\//i.test(url);
}
async function callSunoText(url, options = {}) {
const page = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const headers = {
@@ -1031,7 +1040,7 @@
...(options.method && options.method !== "GET" && options.method !== "HEAD" ? { "content-type": "application/json" } : {}),
...(options.headers || {}),
};
if (typeof GM_xmlhttpRequest === "function" && /^https?:\/\/[^/]*suno\./i.test(url)) {
if (typeof GM_xmlhttpRequest === "function" && shouldUseTampermonkeyFirst(url)) {
return callTampermonkeyRequest(url, { ...options, headers });
}
try {
@@ -1207,6 +1216,7 @@
try {
await callSunoJson(`${base}/convert_wav/`, { method: "POST" });
} catch (error) {
if (hasHttpStatus(error, [401, 403])) throw new Error("SUNO WAV 변환 권한이 없어. SUNO 페이지를 새로고침한 뒤 다시 시도해줘.");
console.warn("[ChatGPT to Suno] convert_wav failed", error);
}
@@ -1217,6 +1227,7 @@
if (direct) return direct;
if (await canReadUrl(cdnWavUrl)) return cdnWavUrl;
} catch (error) {
if (hasHttpStatus(error, [401, 403])) throw new Error("SUNO WAV URL 권한이 없어. SUNO 페이지를 새로고침한 뒤 다시 시도해줘.");
console.warn("[ChatGPT to Suno] wav_file failed", error);
if (await canReadUrl(cdnWavUrl)) return cdnWavUrl;
}
@@ -1229,11 +1240,11 @@
async function prepareSunoWavCandidate(track) {
if (!track.id) return extractWavUrl(track.wavUrl);
const base = `https://studio-api-prod.suno.com/api/gen/${encodeURIComponent(track.id)}`;
const billingDownloadUrl = `https://studio-api-prod.suno.com/api/billing/clips/${encodeURIComponent(track.id)}/download/`;
const cdnWavUrl = `https://cdn1.suno.ai/${encodeURIComponent(track.id)}.wav`;
try {
await callSunoJson(`${base}/convert_wav/`, { method: "POST" });
} catch (error) {
if (hasHttpStatus(error, [401, 403])) throw new Error("SUNO WAV 변환 권한이 없어. SUNO 페이지를 새로고침한 뒤 다시 시도해줘.");
console.warn("[ChatGPT to Suno] convert_wav failed; local retry will continue", error);
}
try {
@@ -1241,15 +1252,9 @@
const direct = extractWavUrl(data);
if (direct) return direct;
} catch (error) {
if (hasHttpStatus(error, [401, 403])) throw new Error("SUNO WAV URL 권한이 없어. SUNO 페이지를 새로고침한 뒤 다시 시도해줘.");
console.warn("[ChatGPT to Suno] wav_file probe failed; local retry will continue", error);
}
try {
const data = await callSunoJson(billingDownloadUrl, { method: "GET" });
const direct = extractWavUrl(data);
if (direct) return direct;
} catch (error) {
console.warn("[ChatGPT to Suno] billing download probe failed; local retry will continue", error);
}
return extractWavUrl(track.wavUrl) || cdnWavUrl;
}
@@ -1262,18 +1267,20 @@
try {
await callSunoJson(`${base}/convert_wav/`, { method: "POST" });
} catch (error) {
if (hasHttpStatus(error, [401, 403])) throw new Error("SUNO WAV 변환 권한이 없어. SUNO 페이지를 새로고침한 뒤 다시 시도해줘.");
console.warn("[ChatGPT to Suno] convert_wav failed before browser download", error);
}
for (let attempt = 0; attempt < 20; attempt += 1) {
try {
setStatus(statusTarget, `SUNO WAV URL 확인 중 ${attempt + 1}/20: ${track.title || track.id}`);
const data = await callSunoJson(`${base}/wav_file/`, { method: "GET" });
const direct = extractWavUrl(data);
if (direct) candidates.push(direct);
} catch (error) {
if (hasHttpStatus(error, [401, 403])) throw new Error("SUNO WAV URL 권한이 없어. SUNO 페이지를 새로고침한 뒤 다시 시도해줘.");
console.warn("[ChatGPT to Suno] wav_file probe failed before browser download", error);
}
candidates.push(`https://studio-api-prod.suno.com/api/billing/clips/${encodeURIComponent(track.id)}/download/`);
candidates.push(`https://cdn1.suno.ai/${encodeURIComponent(track.id)}.wav`);
for (const url of unique(candidates)) {