Restore MP3 auto and show visible Suno tracks
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.10.1
|
// @version 0.10.3
|
||||||
// @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/*
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const SCRIPT_VERSION = "0.10.1";
|
const SCRIPT_VERSION = "0.10.3";
|
||||||
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");
|
||||||
@@ -1878,11 +1878,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="cgs-track-list" data-cgs-track-list></div>
|
<div class="cgs-track-list" data-cgs-track-list></div>
|
||||||
<div class="cgs-row">
|
<div class="cgs-row">
|
||||||
<button class="cgs-btn cgs-wide" type="button" data-cgs-process-one-mp3>선택 1곡 MP3 대기</button>
|
<button class="cgs-btn cgs-wide" type="button" data-cgs-process-one-mp3>선택 1곡 MP3 자동</button>
|
||||||
<button class="cgs-btn cgs-wide" type="button" data-cgs-process-one-wav>선택 1곡 WAV 대기</button>
|
<button class="cgs-btn cgs-wide" type="button" data-cgs-process-one-wav>선택 1곡 WAV 대기</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="cgs-row">
|
<div class="cgs-row">
|
||||||
<button class="cgs-btn cgs-primary cgs-wide" type="button" data-cgs-process-selected-mp3>선택항목 MP3 대기</button>
|
<button class="cgs-btn cgs-primary cgs-wide" type="button" data-cgs-process-selected-mp3>선택항목 MP3 자동</button>
|
||||||
<button class="cgs-btn cgs-primary cgs-wide" type="button" data-cgs-process-selected-wav>선택항목 WAV 대기</button>
|
<button class="cgs-btn cgs-primary cgs-wide" type="button" data-cgs-process-selected-wav>선택항목 WAV 대기</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="cgs-row">
|
<div class="cgs-row">
|
||||||
@@ -1910,12 +1910,14 @@
|
|||||||
const slotSelect = body.querySelector("[data-cgs-slot]");
|
const slotSelect = body.querySelector("[data-cgs-slot]");
|
||||||
const trackList = body.querySelector("[data-cgs-track-list]");
|
const trackList = body.querySelector("[data-cgs-track-list]");
|
||||||
let libraryTracks = [];
|
let libraryTracks = [];
|
||||||
|
let visibleTrackIds = new Set();
|
||||||
const libraryTrackMap = new Map();
|
const libraryTrackMap = new Map();
|
||||||
const selectedTrackIds = new Set();
|
const selectedTrackIds = new Set();
|
||||||
let lastSelectedIndex = -1;
|
let lastSelectedIndex = -1;
|
||||||
let autoScanTimer = 0;
|
let autoScanTimer = 0;
|
||||||
|
|
||||||
const getSelectedTracks = () => libraryTracks.filter((track) => selectedTrackIds.has(track.id));
|
const getVisibleTracks = () => libraryTracks.filter((track) => visibleTrackIds.has(track.id));
|
||||||
|
const getSelectedTracks = () => getVisibleTracks().filter((track) => selectedTrackIds.has(track.id));
|
||||||
const mergeLibraryTracks = (tracks) => {
|
const mergeLibraryTracks = (tracks) => {
|
||||||
for (const track of tracks) {
|
for (const track of tracks) {
|
||||||
if (!track.id) continue;
|
if (!track.id) continue;
|
||||||
@@ -1929,24 +1931,25 @@
|
|||||||
};
|
};
|
||||||
const renderLibraryTrackList = () => {
|
const renderLibraryTrackList = () => {
|
||||||
try {
|
try {
|
||||||
|
const visibleTracks = getVisibleTracks();
|
||||||
for (const id of [...selectedTrackIds]) {
|
for (const id of [...selectedTrackIds]) {
|
||||||
if (!libraryTrackMap.has(id)) selectedTrackIds.delete(id);
|
if (!visibleTrackIds.has(id)) selectedTrackIds.delete(id);
|
||||||
}
|
}
|
||||||
const selectedCount = selectedTrackIds.size;
|
const selectedCount = selectedTrackIds.size;
|
||||||
selectionSummary.textContent = libraryTracks.length
|
selectionSummary.textContent = visibleTracks.length
|
||||||
? `누적 ${libraryTracks.length}곡 / 선택 ${selectedCount}곡`
|
? `현재 화면 ${visibleTracks.length}곡 / 선택 ${selectedCount}곡 / 누적 ${libraryTracks.length}곡`
|
||||||
: "누적된 곡 없음. 스크롤하면서 곡 감지를 눌러줘.";
|
: `현재 화면 감지 없음 / 누적 ${libraryTracks.length}곡`;
|
||||||
|
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
if (!libraryTracks.length) {
|
if (!visibleTracks.length) {
|
||||||
const empty = document.createElement("div");
|
const empty = document.createElement("div");
|
||||||
empty.className = "cgs-track-item";
|
empty.className = "cgs-track-item";
|
||||||
empty.innerHTML = `<div></div><div class="cgs-track-meta">감지된 곡 없음</div><div></div>`;
|
empty.innerHTML = `<div></div><div class="cgs-track-meta">현재 화면에 감지된 곡 없음</div><div></div>`;
|
||||||
trackList.replaceChildren(empty);
|
trackList.replaceChildren(empty);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
libraryTracks.forEach((track, index) => {
|
visibleTracks.forEach((track, index) => {
|
||||||
const row = document.createElement("div");
|
const row = document.createElement("div");
|
||||||
row.className = `cgs-track-item ${selectedTrackIds.has(track.id) ? "cgs-selected" : ""}`;
|
row.className = `cgs-track-item ${selectedTrackIds.has(track.id) ? "cgs-selected" : ""}`;
|
||||||
row.dataset.cgsTrackIndex = String(index);
|
row.dataset.cgsTrackIndex = String(index);
|
||||||
@@ -1984,15 +1987,17 @@
|
|||||||
};
|
};
|
||||||
const setSelectedRange = (from, to) => {
|
const setSelectedRange = (from, to) => {
|
||||||
const start = Math.max(0, Math.min(from, to));
|
const start = Math.max(0, Math.min(from, to));
|
||||||
const end = Math.min(libraryTracks.length - 1, Math.max(from, to));
|
const visibleTracks = getVisibleTracks();
|
||||||
|
const end = Math.min(visibleTracks.length - 1, Math.max(from, to));
|
||||||
for (let index = start; index <= end; index += 1) {
|
for (let index = start; index <= end; index += 1) {
|
||||||
if (libraryTracks[index]?.id) selectedTrackIds.add(libraryTracks[index].id);
|
if (visibleTracks[index]?.id) selectedTrackIds.add(visibleTracks[index].id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const scanAndMergeVisibleTracks = () => {
|
const scanAndMergeVisibleTracks = () => {
|
||||||
const scannedTracks = scanSunoLibraryTracks();
|
const scannedTracks = scanSunoLibraryTracks();
|
||||||
const before = libraryTracks.length;
|
const before = libraryTracks.length;
|
||||||
mergeLibraryTracks(scannedTracks);
|
mergeLibraryTracks(scannedTracks);
|
||||||
|
visibleTrackIds = new Set(scannedTracks.map((track) => track.id).filter(Boolean));
|
||||||
renderLibraryTrackList();
|
renderLibraryTrackList();
|
||||||
updateSummary();
|
updateSummary();
|
||||||
return { scannedTracks, added: libraryTracks.length - before };
|
return { scannedTracks, added: libraryTracks.length - before };
|
||||||
@@ -2062,7 +2067,8 @@
|
|||||||
const item = event.target.closest("[data-cgs-track-index]");
|
const item = event.target.closest("[data-cgs-track-index]");
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
const index = Number(item.getAttribute("data-cgs-track-index"));
|
const index = Number(item.getAttribute("data-cgs-track-index"));
|
||||||
const track = libraryTracks[index];
|
const visibleTracks = getVisibleTracks();
|
||||||
|
const track = visibleTracks[index];
|
||||||
if (!track?.id) return;
|
if (!track?.id) return;
|
||||||
if (event.shiftKey && lastSelectedIndex >= 0) {
|
if (event.shiftKey && lastSelectedIndex >= 0) {
|
||||||
setSelectedRange(lastSelectedIndex, index);
|
setSelectedRange(lastSelectedIndex, index);
|
||||||
@@ -2085,7 +2091,7 @@
|
|||||||
renderLibraryTrackList();
|
renderLibraryTrackList();
|
||||||
}
|
}
|
||||||
updateSummary();
|
updateSummary();
|
||||||
const track = getSelectedTracks()[0] || libraryTracks[0];
|
const track = getSelectedTracks()[0] || getVisibleTracks()[0];
|
||||||
if (!track) {
|
if (!track) {
|
||||||
setStatus(body, "선택할 감지 곡이 없어. 곡 감지를 먼저 눌러줘.");
|
setStatus(body, "선택할 감지 곡이 없어. 곡 감지를 먼저 눌러줘.");
|
||||||
return;
|
return;
|
||||||
@@ -2099,7 +2105,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
body.querySelector("[data-cgs-process-one-mp3]").addEventListener("click", async () => {
|
body.querySelector("[data-cgs-process-one-mp3]").addEventListener("click", async () => {
|
||||||
await registerSelectedManualDownloads("mp3", { one: true });
|
await processSelectedTrack("mp3");
|
||||||
});
|
});
|
||||||
|
|
||||||
body.querySelector("[data-cgs-process-one-wav]").addEventListener("click", async () => {
|
body.querySelector("[data-cgs-process-one-wav]").addEventListener("click", async () => {
|
||||||
@@ -2107,7 +2113,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
body.querySelector("[data-cgs-process-selected-mp3]").addEventListener("click", async () => {
|
body.querySelector("[data-cgs-process-selected-mp3]").addEventListener("click", async () => {
|
||||||
await registerSelectedManualDownloads("mp3");
|
await processSelectedTracks("mp3");
|
||||||
});
|
});
|
||||||
|
|
||||||
body.querySelector("[data-cgs-process-selected-wav]").addEventListener("click", async () => {
|
body.querySelector("[data-cgs-process-selected-wav]").addEventListener("click", async () => {
|
||||||
@@ -2115,7 +2121,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const registerSelectedManualDownloads = async (format, options = {}) => {
|
const registerSelectedManualDownloads = async (format, options = {}) => {
|
||||||
if (!libraryTracks.length) {
|
if (!getVisibleTracks().length) {
|
||||||
mergeLibraryTracks(scanSunoLibraryTracks());
|
mergeLibraryTracks(scanSunoLibraryTracks());
|
||||||
renderLibraryTrackList();
|
renderLibraryTrackList();
|
||||||
}
|
}
|
||||||
@@ -2132,7 +2138,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const clickSelectedSunoUiDownloads = async (format, options = {}) => {
|
const clickSelectedSunoUiDownloads = async (format, options = {}) => {
|
||||||
if (!libraryTracks.length) {
|
if (!getVisibleTracks().length) {
|
||||||
mergeLibraryTracks(scanSunoLibraryTracks());
|
mergeLibraryTracks(scanSunoLibraryTracks());
|
||||||
renderLibraryTrackList();
|
renderLibraryTrackList();
|
||||||
}
|
}
|
||||||
@@ -2165,20 +2171,24 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const processLibraryTracks = async (format) => {
|
const processLibraryTracks = async (format) => {
|
||||||
if (!libraryTracks.length) {
|
if (!getVisibleTracks().length) {
|
||||||
mergeLibraryTracks(scanSunoLibraryTracks());
|
mergeLibraryTracks(scanSunoLibraryTracks());
|
||||||
renderLibraryTrackList();
|
renderLibraryTrackList();
|
||||||
}
|
}
|
||||||
updateSummary();
|
updateSummary();
|
||||||
try {
|
try {
|
||||||
|
if (format === "wav") {
|
||||||
|
await expectManualDownloads(libraryTracks, body, "wav");
|
||||||
|
} else {
|
||||||
await uploadTracksToCompanion(libraryTracks, body, format);
|
await uploadTracksToCompanion(libraryTracks, body, format);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setStatus(body, `${format.toUpperCase()} 일괄 처리 실패: ${error.message || error}`);
|
setStatus(body, `${format.toUpperCase()} 일괄 처리 실패: ${error.message || error}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const processSelectedTracks = async (format) => {
|
const processSelectedTracks = async (format) => {
|
||||||
if (!libraryTracks.length) {
|
if (!getVisibleTracks().length) {
|
||||||
mergeLibraryTracks(scanSunoLibraryTracks());
|
mergeLibraryTracks(scanSunoLibraryTracks());
|
||||||
renderLibraryTrackList();
|
renderLibraryTrackList();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user