Filter Suno library detection to visible rows

This commit is contained in:
DESKTOP-KSVGT20\shkim
2026-05-01 17:53:48 +09:00
parent d19d93db3a
commit 58965716bf
+63 -7
View File
@@ -1,7 +1,7 @@
// ==UserScript==
// @name ChatGPT to Suno Prompt Copier
// @namespace local.suno-helper
// @version 0.4.0
// @version 0.4.1
// @description Copy structured ChatGPT song prompt versions into Suno fields.
// @author local
// @match https://chatgpt.com/*
@@ -620,6 +620,60 @@
return target;
}
function normalizeMatchText(value) {
return String(value || "")
.toLowerCase()
.replace(/\s+/g, " ")
.trim();
}
function getVisibleLibraryRows() {
const rows = [];
const seen = new Set();
const mediaNodes = [...document.querySelectorAll("img, [style*='background-image']")];
for (const node of mediaNodes) {
const rect = node.getBoundingClientRect();
if (rect.width < 30 || rect.height < 30 || rect.bottom < 0 || rect.top > window.innerHeight) continue;
let current = node;
for (let depth = 0; depth < 8 && current; depth += 1) {
const text = (current.innerText || current.textContent || "").trim();
const currentRect = current.getBoundingClientRect();
if (
text.length > 20 &&
text.length < 1800 &&
currentRect.width > 250 &&
currentRect.height > 45 &&
/v\d|BPM|Korean|rock|pop|metal|랩|가사|female|male|vocal|Suno/i.test(text)
) {
const key = normalizeMatchText(text);
if (!seen.has(key)) {
seen.add(key);
rows.push({
text,
normalized: key,
});
}
break;
}
current = current.parentElement;
}
}
return rows;
}
function trackMatchesVisibleRows(track, rows) {
if (!rows.length) return true;
const title = normalizeMatchText(track.title);
const style = normalizeMatchText(track.style || track.lyrics || "");
if (!title || title.length > 160) return false;
return rows.some((row) => {
if (!row.normalized.includes(title)) return false;
if (!style) return true;
const styleNeedle = style.slice(0, Math.min(42, style.length));
return styleNeedle.length < 16 || row.normalized.includes(styleNeedle);
});
}
function scanSunoLibraryTracks() {
const sources = readSunoDataSources();
const objects = sources.flatMap((item) => flattenObjects(item));
@@ -651,17 +705,18 @@
});
}
const visibleRows = getVisibleLibraryRows();
return [...tracks.values()]
.filter((track) => track.id || track.audioUrl || track.imageUrl)
.filter((track) => trackMatchesVisibleRows(track, visibleRows))
.sort((a, b) => String(a.title).localeCompare(String(b.title), "ko"));
}
function visibleSunoRowsHint() {
const titles = [...document.querySelectorAll("a, h1, h2, h3, [role='button'], div, span")]
.map((node) => (node.innerText || node.textContent || "").trim())
.filter((text) => text && text.length < 80 && !/^(Songs|Playlists|Workspaces|Studio Projects|Voices|Cover Art|Hooks|History|Search|Newest|Liked|Public|Uploads)$/i.test(text));
const likelyTitles = titles.filter((text, index) => titles.indexOf(text) === index).slice(0, 5);
return likelyTitles;
return getVisibleLibraryRows()
.map((row) => row.text.split("\n").map((line) => line.trim()).filter(Boolean)[0])
.filter((text, index, list) => text && list.indexOf(text) === index)
.slice(0, 5);
}
function findCurrentSunoTrack() {
@@ -893,7 +948,8 @@
updateSummary();
const sample = libraryTracks.slice(0, 3).map((track) => track.title || track.id).join(", ");
if (libraryTracks.length) {
setStatus(body, `${libraryTracks.length}곡 감지: ${sample}`);
const visibleCount = getVisibleLibraryRows().length;
setStatus(body, `${libraryTracks.length}곡 감지: ${sample} / 화면 행 ${visibleCount}개 기준`);
return;
}
const visibleHints = visibleSunoRowsHint();