Queue Suno WAV candidates with local retry
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name ChatGPT to Suno Prompt Copier
|
// @name ChatGPT to Suno Prompt Copier
|
||||||
// @namespace local.suno-helper
|
// @namespace local.suno-helper
|
||||||
// @version 0.7.7
|
// @version 0.7.8
|
||||||
// @description Copy structured ChatGPT song prompt versions into Suno fields.
|
// @description Copy structured ChatGPT song prompt versions into Suno fields.
|
||||||
// @author local
|
// @author local
|
||||||
// @match https://chatgpt.com/*
|
// @match https://chatgpt.com/*
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const SCRIPT_VERSION = "0.7.7";
|
const SCRIPT_VERSION = "0.7.8";
|
||||||
const STORAGE_KEY = "chatgptSunoPromptSlots";
|
const STORAGE_KEY = "chatgptSunoPromptSlots";
|
||||||
const SLOT_COUNT = 2;
|
const SLOT_COUNT = 2;
|
||||||
const isChatGPT = location.hostname.includes("chatgpt.com") || location.hostname.includes("chat.openai.com");
|
const isChatGPT = location.hostname.includes("chatgpt.com") || location.hostname.includes("chat.openai.com");
|
||||||
@@ -992,6 +992,25 @@
|
|||||||
return extractWavUrl(track.wavUrl) || ((await canReadUrl(cdnWavUrl)) ? cdnWavUrl : "");
|
return extractWavUrl(track.wavUrl) || ((await canReadUrl(cdnWavUrl)) ? cdnWavUrl : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 cdnWavUrl = `https://cdn1.suno.ai/${encodeURIComponent(track.id)}.wav`;
|
||||||
|
try {
|
||||||
|
await callSunoJson(`${base}/convert_wav/`, { method: "POST" });
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("[ChatGPT to Suno] convert_wav failed; local retry will continue", error);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const data = await callSunoJson(`${base}/wav_file/`, { method: "GET" });
|
||||||
|
const direct = extractWavUrl(data);
|
||||||
|
if (direct) return direct;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("[ChatGPT to Suno] wav_file probe failed; local retry will continue", error);
|
||||||
|
}
|
||||||
|
return extractWavUrl(track.wavUrl) || cdnWavUrl;
|
||||||
|
}
|
||||||
|
|
||||||
function downloadTextFile(text, name) {
|
function downloadTextFile(text, name) {
|
||||||
const blob = new Blob([text], { type: "application/json;charset=utf-8" });
|
const blob = new Blob([text], { type: "application/json;charset=utf-8" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
@@ -1111,11 +1130,11 @@
|
|||||||
let wavPending = 0;
|
let wavPending = 0;
|
||||||
for (let index = 0; index < missingTracks.length; index += 1) {
|
for (let index = 0; index < missingTracks.length; index += 1) {
|
||||||
const track = { ...missingTracks[index] };
|
const track = { ...missingTracks[index] };
|
||||||
setStatus(statusTarget, `${index + 1}/${missingTracks.length} 없는 곡 WAV 준비 중: ${track.title || track.id}`);
|
setStatus(statusTarget, `${index + 1}/${missingTracks.length} 없는 곡 WAV 변환 요청 중: ${track.title || track.id}`);
|
||||||
track.wavUrl = await requestSunoWavUrl(track);
|
track.wavUrl = await prepareSunoWavCandidate(track);
|
||||||
if (!track.wavUrl) {
|
if (!track.wavUrl) {
|
||||||
wavPending += 1;
|
wavPending += 1;
|
||||||
setStatus(statusTarget, `${track.title || track.id} WAV 변환 URL이 아직 안 나와서 보류 (${wavPending}곡)`);
|
setStatus(statusTarget, `${track.title || track.id} WAV 후보 URL을 못 만들어서 보류 (${wavPending}곡)`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const enqueueManifest = {
|
const enqueueManifest = {
|
||||||
@@ -1138,10 +1157,10 @@
|
|||||||
setStatus(statusTarget, `${enqueued}/${missingTracks.length}곡 큐 추가: ${track.title || track.id} / 대기 ${data.queued ?? "?"}개`);
|
setStatus(statusTarget, `${enqueued}/${missingTracks.length}곡 큐 추가: ${track.title || track.id} / 대기 ${data.queued ?? "?"}개`);
|
||||||
}
|
}
|
||||||
if (!enqueued) {
|
if (!enqueued) {
|
||||||
setStatus(statusTarget, `큐에 넣은 곡 없음. 새 곡 ${missingTracks.length}개 중 ${wavPending}개가 아직 SUNO WAV 변환 URL을 안 줬어.`);
|
setStatus(statusTarget, `큐에 넣은 곡 없음. 새 곡 ${missingTracks.length}개 중 ${wavPending}개가 WAV 후보 URL 생성 실패.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setStatus(statusTarget, `큐 전송 완료: 새 곡 ${enqueued}개, WAV 보류 ${wavPending}개. 로컬 프로그램이 순서대로 처리 중.`);
|
setStatus(statusTarget, `큐 전송 완료: 새 곡 ${enqueued}개, 보류 ${wavPending}개. 로컬 프로그램이 WAV 준비를 기다리며 처리 중.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function trackOptionLabel(track, index) {
|
function trackOptionLabel(track, index) {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ param(
|
|||||||
[string]$OutputRoot = "\\VaultOfData\AKAMedia\음악\처리됨",
|
[string]$OutputRoot = "\\VaultOfData\AKAMedia\음악\처리됨",
|
||||||
[string]$ArtistName = "SUNO",
|
[string]$ArtistName = "SUNO",
|
||||||
[string]$FfmpegPath = "",
|
[string]$FfmpegPath = "",
|
||||||
|
[int]$WavRetrySeconds = 300,
|
||||||
|
[int]$WavRetryIntervalSeconds = 8,
|
||||||
[switch]$KeepWorkFiles,
|
[switch]$KeepWorkFiles,
|
||||||
[switch]$Force
|
[switch]$Force
|
||||||
)
|
)
|
||||||
@@ -233,6 +235,37 @@ function Invoke-Download {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Invoke-DownloadWithRetry {
|
||||||
|
param(
|
||||||
|
[string[]]$Urls,
|
||||||
|
[string]$OutFile,
|
||||||
|
[int]$RetrySeconds,
|
||||||
|
[int]$IntervalSeconds
|
||||||
|
)
|
||||||
|
|
||||||
|
$candidates = @($Urls | Where-Object { $_ } | Select-Object -Unique)
|
||||||
|
if (-not $candidates.Count) { return $false }
|
||||||
|
|
||||||
|
$deadline = (Get-Date).AddSeconds([Math]::Max(1, $RetrySeconds))
|
||||||
|
$attempt = 0
|
||||||
|
do {
|
||||||
|
$attempt++
|
||||||
|
foreach ($candidate in $candidates) {
|
||||||
|
Write-Host "WAV 후보 시도 #$attempt`: $candidate"
|
||||||
|
if (Invoke-Download $candidate $OutFile) {
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Get-Date) -lt $deadline) {
|
||||||
|
Write-Host "WAV 아직 준비 안 됨. $IntervalSeconds초 후 재시도..."
|
||||||
|
Start-Sleep -Seconds ([Math]::Max(1, $IntervalSeconds))
|
||||||
|
}
|
||||||
|
} while ((Get-Date) -lt $deadline)
|
||||||
|
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
function Add-MetadataArg {
|
function Add-MetadataArg {
|
||||||
param(
|
param(
|
||||||
[System.Collections.Generic.List[string]]$Args,
|
[System.Collections.Generic.List[string]]$Args,
|
||||||
@@ -392,12 +425,7 @@ foreach ($track in $tracks) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
$audioOk = $false
|
$audioOk = Invoke-DownloadWithRetry $wavCandidates $inputAudio $WavRetrySeconds $WavRetryIntervalSeconds
|
||||||
foreach ($candidate in $wavCandidates) {
|
|
||||||
Write-Host "WAV 후보 시도: $candidate"
|
|
||||||
$audioOk = Invoke-Download $candidate $inputAudio
|
|
||||||
if ($audioOk) { break }
|
|
||||||
}
|
|
||||||
if (-not $audioOk) {
|
if (-not $audioOk) {
|
||||||
Write-Warning "WAV를 받지 못해서 건너뜀: $title"
|
Write-Warning "WAV를 받지 못해서 건너뜀: $title"
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user