diff --git a/chatgpt-suno-tampermonkey.user.js b/chatgpt-suno-tampermonkey.user.js index 0f42027..21f07c7 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.9.0 +// @version 0.9.1 // @description Copy structured ChatGPT song prompt versions into Suno fields. // @author local // @match https://chatgpt.com/* @@ -30,7 +30,7 @@ (function () { "use strict"; - const SCRIPT_VERSION = "0.9.0"; + const SCRIPT_VERSION = "0.9.1"; const STORAGE_KEY = "chatgptSunoPromptSlots"; const SLOT_COUNT = 2; const isChatGPT = location.hostname.includes("chatgpt.com") || location.hostname.includes("chat.openai.com"); @@ -726,6 +726,46 @@ .trim(); } + function isBadTrackTitle(value) { + const text = String(value || "").trim(); + if (!text) return true; + if (/^v\d+(?:\.\d+)*$/i.test(text)) return true; + if (/^\d+:\d{2}$/.test(text)) return true; + if (/^(liked|public|uploads|newest|songs|playlists)$/i.test(text)) return true; + return false; + } + + function extractIdsFromText(value) { + return unique([...String(value || "").matchAll(/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi)].map((match) => match[0])); + } + + function readNodeMediaText(node) { + const values = []; + let current = node; + for (let depth = 0; depth < 5 && current; depth += 1) { + if (current.currentSrc || current.src) values.push(current.currentSrc || current.src); + const style = current.getAttribute?.("style") || ""; + if (style) values.push(style); + current = current.parentElement; + } + return values.join("\n"); + } + + function inferVisibleRowTitle(text) { + const lines = String(text || "") + .split("\n") + .map((line) => line.trim()) + .filter(Boolean); + for (const line of lines) { + if (isBadTrackTitle(line)) continue; + if (/^(play|like|liked|public|uploads|newest|filters?)$/i.test(line)) continue; + if (/^\d+$/.test(line)) continue; + if (line.length > 90) continue; + return line; + } + return ""; + } + function getCurrentSunoUsername() { const text = document.body.innerText || ""; const match = /([A-Za-z0-9_.-]{3,40})\s+\d+\s+credits/i.exec(text); @@ -753,9 +793,12 @@ const key = normalizeMatchText(text); if (!seen.has(key)) { seen.add(key); + const mediaText = `${readNodeMediaText(node)}\n${current.innerHTML || ""}`; rows.push({ text, normalized: key, + title: inferVisibleRowTitle(text), + ids: extractIdsFromText(mediaText), }); } break; @@ -774,7 +817,10 @@ if (!rows.length) return -1; const title = normalizeMatchText(track.title); const style = normalizeMatchText(track.style || track.lyrics || ""); - if (!title || title.length > 160) return -1; + const id = String(track.id || ""); + const idIndex = rows.findIndex((row) => id && row.ids?.includes(id)); + if (idIndex >= 0) return idIndex; + if (!title || title.length > 160 || isBadTrackTitle(track.title)) return -1; return rows.findIndex((row) => { if (!row.normalized.includes(title)) return false; if (!style) return true; @@ -820,6 +866,10 @@ } const visibleRows = getVisibleLibraryRows(); + for (const track of tracks.values()) { + const row = visibleRows.find((item) => track.id && item.ids?.includes(track.id)); + if (row?.title && isBadTrackTitle(track.title)) track.title = row.title; + } const currentUser = getCurrentSunoUsername(); return [...tracks.values()] .filter((track) => track.id || track.audioUrl || track.imageUrl) @@ -1543,6 +1593,24 @@ return tryParseJson(text) || {}; } + async function stopCompanionWork(statusTarget) { + try { + const response = await fetch("http://127.0.0.1:17873/stop", { + method: "POST", + cache: "no-store", + }); + const text = await response.text(); + if (!response.ok) { + setStatus(statusTarget, `중단 실패: ${response.status} ${response.statusText}`); + return; + } + const data = tryParseJson(text) || {}; + setStatus(statusTarget, `작업 중단: 실행 ${data.stopped || 0}개 종료, 대기 ${data.dropped || 0}개 제거`); + } catch (error) { + setStatus(statusTarget, `중단 실패: companion 연결 확인 필요. ${error.message || error}`); + } + } + function formatCompanionStatus(data) { if (!data || typeof data !== "object") return "상태 정보 없음"; const active = Number(data.activeCount || 0); @@ -1586,6 +1654,7 @@