Add local Suno companion service

This commit is contained in:
DESKTOP-KSVGT20\shkim
2026-05-01 22:53:11 +09:00
parent cec4ae3d0c
commit 2e3698b3c6
4 changed files with 276 additions and 16 deletions
+76 -16
View File
@@ -1,7 +1,7 @@
// ==UserScript==
// @name ChatGPT to Suno Prompt Copier
// @namespace local.suno-helper
// @version 0.4.3
// @version 0.5.0
// @description Copy structured ChatGPT song prompt versions into Suno fields.
// @author local
// @match https://chatgpt.com/*
@@ -13,6 +13,8 @@
// @connect cdn*.suno.ai
// @connect *.suno.com
// @connect studio-api-prod.suno.com
// @connect 127.0.0.1
// @connect localhost
// @run-at document-start
// @grant GM_addStyle
// @grant GM_getValue
@@ -953,6 +955,37 @@
setStatus(statusTarget, `${preparedTracks.length}곡 manifest 저장. 이제 run_process_suno_library.ps1 실행하면 정리/FLAC 변환돼.`);
}
async function postToCompanion(tracks, statusTarget) {
if (!tracks.length) {
setStatus(statusTarget, "보낼 곡을 못 찾았어. 라이브러리 곡 감지를 먼저 해줘.");
return;
}
const preparedTracks = [];
for (let index = 0; index < tracks.length; index += 1) {
const track = { ...tracks[index] };
setStatus(statusTarget, `${index + 1}/${tracks.length} WAV 준비 중: ${track.title || track.id}`);
track.wavUrl = await requestSunoWavUrl(track);
preparedTracks.push(track);
}
const manifest = {
pageUrl: location.href,
capturedAt: new Date().toISOString(),
count: preparedTracks.length,
tracks: preparedTracks,
};
const response = await fetch("http://127.0.0.1:17873/process", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(manifest),
});
const text = await response.text();
if (!response.ok) {
throw new Error(text || `${response.status} ${response.statusText}`);
}
const data = tryParseJson(text) || {};
setStatus(statusTarget, `로컬 처리 시작: ${data.jobId || "job"} / ${data.log || "companion console 확인"}`);
}
async function bootSuno() {
const body = createPanel("Suno Prompt Paste");
const slots = await readSlots();
@@ -979,6 +1012,9 @@
<div class="cgs-row">
<button class="cgs-btn cgs-primary cgs-wide" type="button" data-cgs-export-library>처리용 manifest 저장</button>
</div>
<div class="cgs-row">
<button class="cgs-btn cgs-primary cgs-wide" type="button" data-cgs-send-companion>로컬 프로그램으로 처리</button>
</div>
<div class="cgs-row">
<button class="cgs-btn cgs-wide" type="button" data-cgs-download-library>브라우저 일괄 다운로드</button>
</div>
@@ -1063,6 +1099,35 @@
updateSummary();
await exportSunoManifest(libraryTracks, body);
});
body.querySelector("[data-cgs-send-companion]").addEventListener("click", async () => {
if (!libraryTracks.length) libraryTracks = scanSunoLibraryTracks();
updateSummary();
try {
await postToCompanion(libraryTracks, body);
} catch (error) {
setStatus(body, `로컬 프로그램 연결 실패: ${error.message || error}`);
}
});
}
function safeBootSuno() {
try {
bootSuno();
} catch (error) {
console.error("[ChatGPT to Suno]", error);
createErrorPanel(error);
}
}
function safeBootChatGPT() {
try {
const versions = parseVersions(getConversationText());
if (versions.length) bootChatGPT();
} catch (error) {
console.error("[ChatGPT to Suno]", error);
createErrorPanel(error);
}
}
function startApp() {
@@ -1071,23 +1136,18 @@
return;
}
try {
if (isChatGPT) bootChatGPT();
if (isSuno) bootSuno();
} catch (error) {
console.error("[ChatGPT to Suno]", error);
createErrorPanel(error);
}
if (isSuno) safeBootSuno();
if (isChatGPT) safeBootChatGPT();
setInterval(() => {
if (!document.querySelector("#cgs-panel")) {
try {
if (isChatGPT) bootChatGPT();
if (isSuno) bootSuno();
} catch (error) {
console.error("[ChatGPT to Suno]", error);
createErrorPanel(error);
}
if (isSuno && !document.querySelector("#cgs-panel")) {
safeBootSuno();
}
if (isChatGPT) {
const hasPanel = Boolean(document.querySelector("#cgs-panel"));
const hasVersions = parseVersions(getConversationText()).length > 0;
if (hasVersions && !hasPanel) safeBootChatGPT();
if (!hasVersions && hasPanel) document.querySelector("#cgs-panel")?.remove();
}
}, 2000);
}