From 8d577d872474afe86dfbe69981e17c8c46de3f90 Mon Sep 17 00:00:00 2001 From: "DESKTOP-KSVGT20\\shkim" Date: Sat, 2 May 2026 00:12:25 +0900 Subject: [PATCH] Improve Suno WAV URL queue detection --- chatgpt-suno-tampermonkey.user.js | 73 ++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/chatgpt-suno-tampermonkey.user.js b/chatgpt-suno-tampermonkey.user.js index 6b5f962..fb18106 100644 --- a/chatgpt-suno-tampermonkey.user.js +++ b/chatgpt-suno-tampermonkey.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name ChatGPT to Suno Prompt Copier // @namespace local.suno-helper -// @version 0.7.1 +// @version 0.7.2 // @description Copy structured ChatGPT song prompt versions into Suno fields. // @author local // @match https://chatgpt.com/* @@ -835,8 +835,51 @@ return data; } + function extractWavUrl(value) { + if (!value) return ""; + if (typeof value === "string") { + const direct = value.match(/https?:\/\/[^"' <>)]+?\.wav(?:\?[^"' <>)]+)?/i)?.[0] || ""; + if (direct) return normalizeSunoMediaUrl(direct); + return /\.wav(?:\?|$)/i.test(value) ? normalizeSunoMediaUrl(value) : ""; + } + if (Array.isArray(value)) { + for (const item of value) { + const found = extractWavUrl(item); + if (found) return found; + } + return ""; + } + if (typeof value === "object") { + const directKeys = [ + "url", + "download_url", + "downloadUrl", + "wav_url", + "wavUrl", + "wav_file_url", + "wavFileUrl", + "file_url", + "fileUrl", + "signed_url", + "signedUrl", + "audio_url", + "audioUrl", + "file", + ]; + for (const key of directKeys) { + const found = extractWavUrl(value[key]); + if (found) return found; + } + for (const item of Object.values(value)) { + const found = extractWavUrl(item); + if (found) return found; + } + } + return ""; + } + async function requestSunoWavUrl(track) { - if (!track.id) return track.wavUrl || ""; + if (!track.id) return extractWavUrl(track.wavUrl); const base = `https://studio-api-prod.suno.com/api/gen/${encodeURIComponent(track.id)}`; try { await callSunoJson(`${base}/convert_wav/`, { method: "POST" }); @@ -844,24 +887,18 @@ console.warn("[ChatGPT to Suno] convert_wav failed", error); } - for (let attempt = 0; attempt < 8; attempt += 1) { + for (let attempt = 0; attempt < 20; attempt += 1) { try { const data = await callSunoJson(`${base}/wav_file/`, { method: "GET" }); - const direct = - data?.url || - data?.download_url || - data?.wav_url || - data?.audio_url || - data?.file || - (typeof data === "string" && /^https?:\/\//i.test(data) ? data : ""); - if (direct) return normalizeSunoMediaUrl(direct); + const direct = extractWavUrl(data); + if (direct) return direct; } catch (error) { console.warn("[ChatGPT to Suno] wav_file failed", error); } - await new Promise((resolve) => setTimeout(resolve, 1300)); + await new Promise((resolve) => setTimeout(resolve, 1500)); } - return track.wavUrl || ""; + return extractWavUrl(track.wavUrl); } function downloadTextFile(text, name) { @@ -980,12 +1017,14 @@ } let enqueued = 0; + let wavPending = 0; for (let index = 0; index < missingTracks.length; index += 1) { const track = { ...missingTracks[index] }; setStatus(statusTarget, `${index + 1}/${missingTracks.length} 없는 곡 WAV 준비 중: ${track.title || track.id}`); track.wavUrl = await requestSunoWavUrl(track); if (!track.wavUrl) { - setStatus(statusTarget, `${track.title || track.id} WAV 변환 URL이 아직 안 나와서 큐에 넣지 않음`); + wavPending += 1; + setStatus(statusTarget, `${track.title || track.id} WAV 변환 URL이 아직 안 나와서 보류 (${wavPending}곡)`); continue; } const enqueueManifest = { @@ -1007,7 +1046,11 @@ const data = tryParseJson(text) || {}; setStatus(statusTarget, `${enqueued}/${missingTracks.length}곡 큐 추가: ${track.title || track.id} / 대기 ${data.queued ?? "?"}개`); } - setStatus(statusTarget, `큐 전송 완료: 새 곡 ${enqueued}개. 로컬 프로그램이 순서대로 처리 중.`); + if (!enqueued) { + setStatus(statusTarget, `큐에 넣은 곡 없음. 새 곡 ${missingTracks.length}개 중 ${wavPending}개가 아직 SUNO WAV 변환 URL을 안 줬어.`); + return; + } + setStatus(statusTarget, `큐 전송 완료: 새 곡 ${enqueued}개, WAV 보류 ${wavPending}개. 로컬 프로그램이 순서대로 처리 중.`); } async function testCompanion(statusTarget) {