Riffusion Multitool

Adds robust song deletion (Selective & Bulk with Stop) and a Download Queue tool with multi-format selection and delay. Features: Main Menu, Independent Lists, Keyword Filters, Liked Filters/Selectors, Draggable & Minimizable UI. USE WITH CAUTION.

当前为 2025-05-14 提交的版本,查看 最新版本

// ==UserScript==
// @name Riffusion Multitool
// @namespace http://tampermonkey.net/
// @version 1.53
// @description Adds robust song deletion (Selective & Bulk with Stop) and a Download Queue tool with multi-format selection and delay. Features: Main Menu, Independent Lists, Keyword Filters, Liked Filters/Selectors, Draggable & Minimizable UI. USE WITH CAUTION.
// @author Graph1ks (assisted by GoogleAI)
// @match https://www.riffusion.com/*
// @grant GM_addStyle
// @grant GM_info
// ==/UserScript==
(function() {
'use strict';

    // --- Configuration ---
    const INITIAL_VIEW = 'menu';
    const DELETION_DELAY = 500;
    const DOWNLOAD_MENU_DELAY = 550;
    const DOWNLOAD_ACTION_DELAY = 500;
    const DEFAULT_INTRA_FORMAT_DELAY_SECONDS = 6;
    const DROPDOWN_DELAY = 400;
    const DEFAULT_INTER_SONG_DELAY_SECONDS = 6;
    const MAX_RETRIES = 3;
    const MAX_SUB_MENU_OPEN_RETRIES = 4;
    const MAX_EMPTY_CHECKS = 3;
    const EMPTY_RETRY_DELAY = 6000;
    const KEYWORD_FILTER_DEBOUNCE = 500;
    const UI_INITIAL_TOP = '60px';
    const UI_INITIAL_RIGHT = '20px';
    const INITIAL_IGNORE_LIKED_DELETE = true;
    const MINIMIZED_ICON_SIZE = '40px';
    const MINIMIZED_ICON_TOP = '15px';
    const MINIMIZED_ICON_RIGHT = '15px';

    // --- State Variables ---
    let debugMode = false;
    let isDeleting = false;
    let isDownloading = false;
    let currentView = INITIAL_VIEW;
    let ignoreLikedSongsDeleteState = INITIAL_IGNORE_LIKED_DELETE;
    let downloadInterSongDelaySeconds = DEFAULT_INTER_SONG_DELAY_SECONDS;
    let downloadIntraFormatDelaySeconds = DEFAULT_INTRA_FORMAT_DELAY_SECONDS;
    let keywordFilterDebounceTimer = null;
    let stopBulkDeletionSignal = false; // True if user clicks "Stop Deletion" during bulk mode

    // --- State for Minimize/Restore ---
    let isMinimized = true;
    let lastUiTop = UI_INITIAL_TOP;
    let lastUiLeft = null;
    let uiElement = null;
    let minimizedIconElement = null;

    // --- Styling (Compact Adjustments) ---
    GM_addStyle(`
        #riffControlUI {
            position: fixed; background: linear-gradient(145deg, #2a2a2a, #1e1e1e); border: 1px solid #444; border-radius: 10px; padding: 0; z-index: 10000; width: 300px; /* Slightly narrower */ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4); color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; user-select: none; overflow: hidden;
            display: ${isMinimized ? 'none' : 'block'};
        }
        #riffControlHeader {
            background: linear-gradient(90deg, #3a3a3a, #2c2c2c); padding: 8px 12px; /* Reduced padding */ cursor: move; border-bottom: 1px solid #444; border-radius: 10px 10px 0 0; position: relative;
        }
        #riffControlHeader h3 { margin: 0; font-size: 15px; /* Slightly smaller */ font-weight: 600; color: #ffffff; text-align: center; text-shadow: 0 1px 1px rgba(0,0,0,0.2); padding-right: 25px; }

        #minimizeButton {
            position: absolute; top: 4px; /* Adjusted */ right: 6px; /* Adjusted */ background: none; border: none; color: #aaa; font-size: 18px; /* Slightly smaller */ font-weight: bold; line-height: 1; cursor: pointer; padding: 2px 4px; border-radius: 4px; transition: color 0.2s, background-color 0.2s;
        }
        #minimizeButton:hover { color: #fff; background-color: rgba(255, 255, 255, 0.1); }

        #riffControlMinimizedIcon {
            position: fixed; top: ${MINIMIZED_ICON_TOP}; right: ${MINIMIZED_ICON_RIGHT}; width: ${MINIMIZED_ICON_SIZE}; height: ${MINIMIZED_ICON_SIZE}; background: linear-gradient(145deg, #3a3a3a, #2c2c2c); border: 1px solid #555; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 16px; font-weight: bold; display: ${isMinimized ? 'flex' : 'none'}; align-items: center; justify-content: center; cursor: pointer; z-index: 10001; transition: background 0.2s; user-select: none;
        }
        #riffControlMinimizedIcon:hover { background: linear-gradient(145deg, #4a4a4a, #3c3c3c); }

        #riffControlContent { padding: 12px; /* Reduced padding */ }
        .riffControlButton { display: block; border: none; border-radius: 6px; /* Slightly smaller radius */ padding: 8px; /* Reduced padding */ font-size: 13px; /* Slightly smaller */ font-weight: 500; text-align: center; cursor: pointer; transition: transform 0.15s, background 0.15s; width: 100%; margin-bottom: 8px; /* Reduced margin */ }
        .riffControlButton:hover:not(:disabled) { transform: translateY(-1px); } /* Less hover effect */
        .riffControlButton:disabled { background: #555 !important; cursor: not-allowed; transform: none; opacity: 0.7; }
        .riffMenuButton { background: linear-gradient(90deg, #4d94ff, #3385ff); color: #fff; }
        .riffMenuButton:hover:not(:disabled) { background: linear-gradient(90deg, #3385ff, #1a75ff); }
        .riffBackButton { background: linear-gradient(90deg, #888, #666); color: #fff; margin-top: 12px; /* Reduced */ margin-bottom: 0; } /* No bottom margin on back */
        .riffBackButton:hover:not(:disabled) { background: linear-gradient(90deg, #666, #444); }
        #deleteAllButton, #deleteButton { background: linear-gradient(90deg, #ff4d4d, #e63939); color: #fff; }
        #deleteAllButton:hover:not(:disabled), #deleteButton:hover:not(:disabled) { background: linear-gradient(90deg, #e63939, #cc3333); }
        #startDownloadQueueButton { background: linear-gradient(90deg, #1db954, #17a34a); color: #fff; }
        #startDownloadQueueButton:hover:not(:disabled) { background: linear-gradient(90deg, #17a34a, #158a3f); }
        #reloadDeleteButton, #reloadDownloadButton { background: linear-gradient(90deg, #ff9800, #e68a00); color: #fff; }
        #reloadDeleteButton:hover:not(:disabled), #reloadDownloadButton:hover:not(:disabled) { background: linear-gradient(90deg, #e68a00, #cc7a00); }
        #debugToggle { background: linear-gradient(90deg, #6666ff, #4d4dff); color: #fff; margin-top: 10px; /* Space before debug */ }
        #debugToggle:hover:not(:disabled) { background: linear-gradient(90deg, #4d4dff, #3333cc); }

        #statusMessage { margin-top: 8px; /* Reduced margin */ font-size: 12px; /* Slightly smaller */ color: #1db954; text-align: center; min-height: 1.1em; word-wrap: break-word; }
        .section-controls { display: none; }
        .songListContainer { margin-bottom: 10px; /* Reduced margin */ max-height: 22vh; /* Reduced height */ overflow-y: auto; padding-right: 5px; border: 1px solid #444; border-radius: 5px; background-color: rgba(0,0,0,0.1); padding: 6px; /* Reduced padding */ }
        .songListContainer label { display: flex; align-items: center; margin: 6px 0; /* Reduced margin */ color: #d0d0d0; font-size: 13px; /* Slightly smaller */ transition: color 0.2s; }
        .songListContainer label:hover:not(.ignored) { color: #ffffff; }
        .songListContainer input[type="checkbox"] { margin-right: 8px; accent-color: #1db954; width: 15px; height: 15px; /* Slightly smaller */ cursor: pointer; flex-shrink: 0; }
        .songListContainer input[type="checkbox"]:disabled { cursor: not-allowed; accent-color: #555; }
        .songListContainer label.ignored { color: #777; cursor: not-allowed; font-style: italic; }
        .songListContainer label.liked { font-weight: bold; color: #8c8cff; }
        .songListContainer label.liked:hover { color: #a0a0ff; }

        .selectAllContainer { margin-bottom: 8px; /* Reduced margin */ display: flex; align-items: center; color: #d0d0d0; font-size: 13px; /* Slightly smaller */ font-weight: 500; cursor: pointer; }
        .selectAllContainer input[type="checkbox"] { margin-right: 8px; accent-color: #1db954; width: 15px; height: 15px; /* Slightly smaller */ }
        .selectAllContainer:hover { color: #ffffff; }
        .counterDisplay { margin-bottom: 8px; /* Reduced margin */ font-size: 13px; /* Slightly smaller */ color: #1db954; text-align: center; }
        .songListContainer::-webkit-scrollbar { width: 6px; /* Narrower scrollbar */ }
        .songListContainer::-webkit-scrollbar-track { background: #333; border-radius: 3px; }
        .songListContainer::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }
        .songListContainer::-webkit-scrollbar-thumb:hover { background: #777; }

        .filterSettings { margin-top: 8px; margin-bottom: 8px; padding-top: 8px; border-top: 1px solid #444; }
        .filterSettings label, .downloadFormatContainer label, .downloadDelayContainer label { display: flex; align-items: center; font-size: 12px; /* Slightly smaller */ color: #ccc; cursor: pointer; margin-bottom: 6px; /* Reduced margin */ }
        .filterSettings label:hover, .downloadFormatContainer label:hover, .downloadDelayContainer label:hover { color: #fff; }
        .filterSettings input[type="checkbox"], .downloadFormatContainer input[type="checkbox"] { margin-right: 6px; accent-color: #1db954; width: 14px; height: 14px; cursor: pointer; }
        .filterSettings input[type="text"], .filterSettings input[type="number"] { width: 100%; background-color: #333; border: 1px solid #555; color: #ddd; padding: 5px 8px; /* Reduced padding */ border-radius: 5px; font-size: 12px; /* Slightly smaller */ box-sizing: border-box; margin-top: 4px; /* Reduced margin */ }
        .filterSettings input[type="text"]:focus, .filterSettings input[type="number"]:focus { outline: none; border-color: #777; }

        #downloadSelectLiked { background: linear-gradient(90deg, #6666ff, #4d4dff); color: #fff; }
        #downloadSelectLiked:hover:not(:disabled) { background: linear-gradient(90deg, #4d4dff, #3333cc); }
        .downloadButtonRow { display: flex; align-items: center; gap: 6px; /* Reduced gap */ margin-bottom: 8px; /* Reduced margin */ }
        #downloadSelectLiked { flex-grow: 1; }
        #downloadClearSelection {
             background: linear-gradient(90deg, #ff4d4d, #e63939); color: #fff;
             width: 28px; height: 28px; /* Slightly smaller */ padding: 0; font-size: 15px; font-weight: bold; line-height: 1; border: none; border-radius: 5px; cursor: pointer; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 0; transition: transform 0.15s, background 0.15s;
        }
         #downloadClearSelection:hover:not(:disabled) { background: linear-gradient(90deg, #e63939, #cc3333); transform: translateY(-1px); }
         #downloadClearSelection:disabled { background: #555 !important; cursor: not-allowed; transform: none; opacity: 0.7; }

         .downloadFormatContainer { margin-top: 8px; padding-top: 8px; border-top: 1px solid #444; }
         .downloadFormatContainer > label { margin-bottom: 4px; /* Reduced margin */ justify-content: center; display: block; text-align: center;}
         .downloadFormatContainer div { display: flex; justify-content: space-around; margin-top: 4px; }
         .downloadFormatContainer label { margin-bottom: 0; }

         .downloadDelayContainer { margin-top: 8px; padding-top: 8px; border-top: 1px solid #444; display: flex; justify-content: space-between; gap: 10px; /* Reduced gap */ }
         .downloadDelayContainer > div { flex: 1; }
         .downloadDelayContainer label { margin-bottom: 2px; display: block; }
         .downloadDelayContainer input[type="number"] { margin-top: 0; }

         #bulkModeControls p { font-size: 11px; color:#aaa; text-align:center; margin-top:4px; margin-bottom: 8px; }
    `);

    // --- Helper Functions ---
    function debounce(func, wait) { let t; return function(...a) { const l=()=> { clearTimeout(t); func.apply(this,a); }; clearTimeout(t); t=setTimeout(l, wait); }; }
    function log(m, l='info') { const p="[RiffTool]"; if(l==='error') console.error(`${p} ${m}`); else if(l==='warn') console.warn(`${p} ${m}`); else console.log(`${p} ${m}`); updateStatusMessage(m); }
    function logDebug(m, e=null) { if(!debugMode) return; console.log(`[RiffTool DEBUG] ${m}`, e instanceof Element ? e.outerHTML.substring(0,250)+'...' : e !== null ? e : ''); }
    function logWarn(m, e=null) { console.warn(`[RiffTool WARN] ${m}`, e instanceof Element ? e.outerHTML.substring(0,250)+'...' : e !== null ? e : ''); }
    function updateStatusMessage(m) { const s=document.getElementById('statusMessage'); if(s) s.textContent = m.length > 100 ? `... ${m.substring(m.length - 100)}` : m; }

    function simulateClick(e) {
        if (!e) { logDebug('Element null for click'); return false; }
        try {
            ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click'].forEach(t =>
                e.dispatchEvent(new MouseEvent(t, { bubbles: true, cancelable: true, composed: true }))
            );
            logDebug('Sim Click (full dispatched event sequence, no programmatic .click()):', e);
            return true;
        } catch (err) {
            log(`Click fail: ${err.message}`, 'error');
            console.error('[RiffTool] Click details:', err, e);
            return false;
        }
    }
    function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }

    // --- UI Functions ---
    function createMainUI() {
        uiElement = document.createElement('div');
        uiElement.id = 'riffControlUI';
        if (UI_INITIAL_RIGHT) {
            const rightPx = parseInt(UI_INITIAL_RIGHT, 10);
            const widthPx = 300;
            lastUiLeft = `${Math.max(0, window.innerWidth - rightPx - widthPx)}px`;
            uiElement.style.left = lastUiLeft;
            uiElement.style.right = 'auto';
        } else {
            lastUiLeft = '20px';
            uiElement.style.left = lastUiLeft;
        }
        lastUiTop = UI_INITIAL_TOP;
        uiElement.style.top = lastUiTop;
        uiElement.style.display = isMinimized ? 'none' : 'block';

        uiElement.innerHTML = `
            <div id="riffControlHeader">
                <h3>Riffusion Multitool v${GM_info.script.version}</h3>
                <button id="minimizeButton" title="Minimize UI">_</button>
            </div>
            <div id="riffControlContent">
                <div id="mainMenuControls" class="section-controls">
                    <button id="goToSelectiveDelete" class="riffMenuButton riffControlButton">Selective Deletion</button>
                    <button id="goToBulkDelete" class="riffMenuButton riffControlButton">Bulk Deletion</button>
                    <button id="goToDownloadQueue" class="riffMenuButton riffControlButton">Download Queue</button>
                </div>
                <div id="selectiveModeControls" class="section-controls">
                    <button class="riffBackButton riffControlButton backToMenuButton">Back to Menu</button>
                    <label class="selectAllContainer"><input type="checkbox" id="deleteSelectAll"> Select All Visible</label>
                    <div id="deleteSongList" class="songListContainer">Loading...</div>
                    <div id="deleteCounter" class="counterDisplay">Deleted: 0 / 0</div>
                    <button id="deleteButton" class="riffControlButton">Delete Selected</button>
                    <button id="reloadDeleteButton" class="riffControlButton">Reload List</button>
                    <div class="filterSettings">
                        <label><input type="checkbox" id="ignoreLikedToggleDelete"> Ignore Liked</label>
                        <input type="text" id="deleteKeywordFilterInput" placeholder="Keywords to ignore (comma-sep)...">
                    </div>
                </div>
                <div id="bulkModeControls" class="section-controls">
                     <button class="riffBackButton riffControlButton backToMenuButton">Back to Menu</button>
                    <button id="deleteAllButton" class="riffControlButton">Delete Entire Library</button>
                    <p>Deletes all songs without scrolling. Retries if needed. Click "Stop Deletion" to halt.</p>
                </div>
                <div id="downloadQueueControls" class="section-controls">
                    <button class="riffBackButton riffControlButton backToMenuButton">Back to Menu</button>
                    <label class="selectAllContainer"><input type="checkbox" id="downloadSelectAll"> Select All</label>
                    <div class="downloadButtonRow">
                        <button id="downloadSelectLiked" class="riffControlButton">Select/Deselect Liked</button>
                        <button id="downloadClearSelection" title="Clear Selection" class="riffControlButton">C</button>
                    </div>
                    <div id="downloadSongList" class="songListContainer">Loading...</div>
                     <div id="downloadCounter" class="counterDisplay">Downloaded: 0 / 0</div>
                    <button id="startDownloadQueueButton" class="riffControlButton">Start Download Queue</button>
                    <button id="reloadDownloadButton" class="riffControlButton">Reload List</button>
                    <div class="filterSettings">
                        <label for="downloadKeywordFilterInput">Filter list by keywords:</label>
                        <input type="text" id="downloadKeywordFilterInput" placeholder="Keywords to show (comma-sep)...">
                        <div class="downloadFormatContainer">
                            <label>Download Formats:</label>
                            <div>
                                <label><input type="checkbox" id="formatMP3" value="MP3" checked> MP3</label>
                                <label><input type="checkbox" id="formatM4A" value="M4A"> M4A</label>
                                <label><input type="checkbox" id="formatWAV" value="WAV"> WAV</label>
                            </div>
                        </div>
                        <div class="downloadDelayContainer">
                             <div>
                                 <label for="downloadIntraFormatDelayInput">Format Delay (s):</label>
                                 <input type="number" id="downloadIntraFormatDelayInput" min="0" step="0.1" value="${DEFAULT_INTRA_FORMAT_DELAY_SECONDS}">
                             </div>
                             <div>
                                 <label for="downloadInterSongDelayInput">Song Delay (s):</label>
                                 <input type="number" id="downloadInterSongDelayInput" min="1" value="${DEFAULT_INTER_SONG_DELAY_SECONDS}">
                             </div>
                        </div>
                    </div>
                </div>
                <button id="debugToggle" class="riffControlButton">${debugMode?'Disable Debug':'Enable Debug'}</button>
                <div id="statusMessage">Ready.</div>
            </div>`;
        document.body.appendChild(uiElement);

        minimizedIconElement = document.createElement('div');
        minimizedIconElement.id = 'riffControlMinimizedIcon';
        minimizedIconElement.textContent = 'RM';
        minimizedIconElement.title = 'Restore Riffusion Multitool';
        minimizedIconElement.style.display = isMinimized ? 'flex' : 'none';
        document.body.appendChild(minimizedIconElement);

        const header = uiElement.querySelector('#riffControlHeader');
        enableDrag(uiElement, header);
        document.getElementById('minimizeButton')?.addEventListener('click', minimizeUI);
        minimizedIconElement?.addEventListener('click', restoreUI);

        document.getElementById('goToSelectiveDelete')?.addEventListener('click', () => navigateToView('selective'));
        document.getElementById('goToBulkDelete')?.addEventListener('click', () => navigateToView('bulk'));
        document.getElementById('goToDownloadQueue')?.addEventListener('click', () => navigateToView('download'));
        uiElement.querySelectorAll('.backToMenuButton').forEach(btn => btn.addEventListener('click', () => navigateToView('menu')));

        document.getElementById('deleteSelectAll')?.addEventListener('change', (e) => toggleSelectAll(e, '#deleteSongList'));
        document.getElementById('deleteButton')?.addEventListener('click', deleteSelectedSongs);
        document.getElementById('reloadDeleteButton')?.addEventListener('click', () => { if (currentView === 'selective') populateDeleteSongList(); });
        const ignoreLikedToggle = document.getElementById('ignoreLikedToggleDelete');
        if (ignoreLikedToggle) { ignoreLikedToggle.checked = ignoreLikedSongsDeleteState; ignoreLikedToggle.addEventListener('change', (e) => { ignoreLikedSongsDeleteState = e.target.checked; log(`Ignore Liked Songs (Delete): ${ignoreLikedSongsDeleteState}`); populateDeleteSongList(); });}
        const deleteKeywordInput = document.getElementById('deleteKeywordFilterInput');
        if (deleteKeywordInput) { deleteKeywordInput.addEventListener('input', debounce(() => { log('Delete keywords changed, refreshing list...'); populateDeleteSongList(); }, KEYWORD_FILTER_DEBOUNCE)); }

        document.getElementById('deleteAllButton')?.addEventListener('click', () => {
            if (currentView === 'bulk') {
                if (isDeleting) {
                    stopBulkDeletionByUser();
                } else {
                    if (isDownloading) { log("Download operation already in progress. Cannot start deletion.", "warn"); return; }
                    if (confirm("ARE YOU SURE? This will attempt to delete ALL songs in your library without scrolling. NO UNDO.")) {
                        deleteAllSongsInLibrary();
                    }
                }
            }
        });

        document.getElementById('downloadSelectAll')?.addEventListener('change', (e) => toggleSelectAll(e, '#downloadSongList'));
        document.getElementById('downloadSelectLiked')?.addEventListener('click', toggleSelectLiked);
        document.getElementById('downloadClearSelection')?.addEventListener('click', clearDownloadSelection);
        document.getElementById('startDownloadQueueButton')?.addEventListener('click', startDownloadQueue);
        document.getElementById('reloadDownloadButton')?.addEventListener('click', () => { if (currentView === 'download') populateDownloadSongList(); });
        const downloadKeywordInput = document.getElementById('downloadKeywordFilterInput');
        if (downloadKeywordInput) { downloadKeywordInput.addEventListener('input', debounce(() => { log('Download filter changed, refreshing list...'); populateDownloadSongList(); }, KEYWORD_FILTER_DEBOUNCE)); }
        const interSongDelayInput = document.getElementById('downloadInterSongDelayInput');
        if (interSongDelayInput) { interSongDelayInput.value = downloadInterSongDelaySeconds; interSongDelayInput.addEventListener('input', (e) => { const val = parseInt(e.target.value, 10); if (!isNaN(val) && val >= 0) { downloadInterSongDelaySeconds = val; log(`Inter-Song delay set to: ${downloadInterSongDelaySeconds}s`); } }); }
        const intraFormatDelayInput = document.getElementById('downloadIntraFormatDelayInput');
        if (intraFormatDelayInput) { intraFormatDelayInput.value = downloadIntraFormatDelaySeconds; intraFormatDelayInput.addEventListener('input', (e) => { const val = parseFloat(e.target.value); if (!isNaN(val) && val >= 0) { downloadIntraFormatDelaySeconds = val; log(`Intra-Format delay set to: ${downloadIntraFormatDelaySeconds}s`); } }); }

        document.getElementById('debugToggle').addEventListener('click', toggleDebug);
        updateUIVisibility();
    }

    function minimizeUI() {
        if (!uiElement || !minimizedIconElement) return;
        if (!isMinimized) {
             lastUiTop = uiElement.style.top || UI_INITIAL_TOP;
             lastUiLeft = uiElement.style.left || lastUiLeft;
        }
        uiElement.style.display = 'none';
        minimizedIconElement.style.display = 'flex';
        isMinimized = true;
        logDebug("UI Minimized");
    }

    function restoreUI() {
        if (!uiElement || !minimizedIconElement) return;
        minimizedIconElement.style.display = 'none';
        uiElement.style.display = 'block';
        uiElement.style.top = lastUiTop;
        uiElement.style.left = lastUiLeft;
        uiElement.style.right = 'auto';
        isMinimized = false;
        logDebug("UI Restored to:", { top: lastUiTop, left: lastUiLeft });
        updateUIVisibility(); // Refresh content if needed
    }

    function navigateToView(view) {
        if (isDeleting || isDownloading) {
            // Allow navigating away from bulk delete even if it's "deleting" (so user can reach stop button if UI glitches)
            // But generally, prevent view changes during operations.
            if (!(currentView === 'bulk' && isDeleting)) {
                 log("Cannot switch views while an operation is in progress.", "warn");
                 return;
            }
        }
        logDebug(`Navigating to view: ${view}`);
        currentView = view;
        updateUIVisibility();
    }

    function updateUIVisibility() {
        if (isMinimized || !uiElement) {
            if (uiElement) uiElement.style.display = 'none'; // Ensure main UI is hidden if minimized
            if (minimizedIconElement) minimizedIconElement.style.display = isMinimized ? 'flex' : 'none';
            return;
        }
        if (minimizedIconElement) minimizedIconElement.style.display = 'none';
        if (uiElement) uiElement.style.display = 'block';


        const sections = {
            menu: document.getElementById('mainMenuControls'),
            selective: document.getElementById('selectiveModeControls'),
            bulk: document.getElementById('bulkModeControls'),
            download: document.getElementById('downloadQueueControls')
        };
        const headerTitle = uiElement.querySelector('#riffControlHeader h3');
        const statusMsg = document.getElementById('statusMessage');
        let title = `Riffusion Multitool v${GM_info.script.version}`;

        Object.values(sections).forEach(section => {
            if (section) section.style.display = 'none';
        });

        if (sections[currentView]) {
            sections[currentView].style.display = 'block';
            switch (currentView) {
                case 'menu': title += " - Menu"; if(!isDeleting && !isDownloading) updateStatusMessage("Select a tool."); break;
                case 'selective': title += " - Selective Deletion"; populateDeleteSongListIfNeeded(); if(!isDeleting && !isDownloading) updateStatusMessage("Select songs to delete."); break;
                case 'bulk':
                    title += " - Bulk Deletion";
                    const deleteAllBtn = document.getElementById('deleteAllButton');
                    if (deleteAllBtn) { // Update button text based on state
                        deleteAllBtn.textContent = isDeleting ? "Stop Deletion" : "Delete Entire Library";
                    }
                    if(!isDeleting && !isDownloading) updateStatusMessage("Warning: Deletes entire library.");
                    break;
                case 'download': title += " - Download Queue"; populateDownloadSongListIfNeeded(); if(!isDeleting && !isDownloading) updateStatusMessage("Select songs to download."); break;
            }
        } else {
            log(`View '${currentView}' not found, showing menu.`, 'warn');
            sections.menu.style.display = 'block';
            currentView = 'menu';
            title += " - Menu"; if(!isDeleting && !isDownloading) updateStatusMessage("Select a tool.");
        }
        if (headerTitle) headerTitle.textContent = title;
        if (statusMsg) statusMsg.style.display = 'block'; // Ensure status is visible
        logDebug(`UI Visibility Updated. Current View: ${currentView}`);
    }

    function toggleDebug() {
        debugMode = !debugMode;
        const btn = document.getElementById('debugToggle');
        if (btn) btn.textContent = debugMode ? 'Disable Debug' : 'Enable Debug';
        log(`Debug mode ${debugMode ? 'enabled' : 'disabled'}.`);
    }

    function enableDrag(element, handle) {
        let isDragging = false, offsetX, offsetY;
        handle.addEventListener('mousedown', (e) => {
            if (e.button !== 0 || e.target.closest('button')) return;
            if (isMinimized) return;
            isDragging = true;
            const rect = element.getBoundingClientRect();
            offsetX = e.clientX - rect.left;
            offsetY = e.clientY - rect.top;
            element.style.cursor = 'grabbing';
            handle.style.cursor = 'grabbing';
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp, { once: true });
            e.preventDefault();
        });
        function onMouseMove(e) {
            if (!isDragging) return;
            let newX = e.clientX - offsetX;
            let newY = e.clientY - offsetY;
            const winWidth = window.innerWidth;
            const winHeight = window.innerHeight;
            const elWidth = element.offsetWidth;
            const elHeight = element.offsetHeight;
            if (newX < 0) newX = 0;
            if (newY < 0) newY = 0;
            if (newX + elWidth > winWidth) newX = winWidth - elWidth;
            if (newY + elHeight > winHeight) newY = winHeight - elHeight;
            element.style.left = `${newX}px`;
            element.style.top = `${newY}px`;
            element.style.right = 'auto';
        }
        function onMouseUp(e) {
            if (e.button !== 0 || !isDragging) return;
            isDragging = false;
            element.style.cursor = 'default';
            handle.style.cursor = 'move';
            document.removeEventListener('mousemove', onMouseMove);
             if (!isMinimized) {
                 lastUiTop = element.style.top;
                 lastUiLeft = element.style.left;
                 logDebug("Stored new position after drag:", { top: lastUiTop, left: lastUiLeft });
             }
        }
    }

    // --- Song List Population & Filtering ---
    function getSongDataFromPage() {
        const songElements = getCurrentSongElements();
        logDebug(`Found ${songElements.length} song elements on page.`);
        const songs = [];
        songElements.forEach((songElement, index) => {
             const titleLink = songElement.querySelector('a[href^="/song/"]');
             let titleElement = titleLink ? titleLink.querySelector('h4.text-primary') : null;
             if (!titleElement) titleElement = songElement.querySelector('[data-sentry-element="RiffTitle"]');
             if (!titleElement && titleLink) titleElement = titleLink.querySelector('div[class*="truncate"], h4');

             const title = titleElement ? titleElement.textContent.trim() : `Untitled Song ${index + 1}`;

             let songId = null;
             if (titleLink) {
                  const match = titleLink.href.match(/\/song\/([a-f0-9-]+)/);
                  if (match && match[1]) songId = match[1];
             }
             if (!songId) {
                 log(`Could not extract song ID for element at index ${index} ('${title}'). Skipping.`, "warn");
                 return;
             }

             const unfavoriteButton = songElement.querySelector('button[aria-label^="Unfavorite"]');
             const solidHeartIcon = songElement.querySelector('button svg[data-prefix="fas"][data-icon="heart"]');

             let isLiked = false;
             if (unfavoriteButton) {
                 isLiked = true;
             } else if (solidHeartIcon && !songElement.querySelector('button[aria-label^="Favorite"]')) {
                 isLiked = true;
             }

             songs.push({
                 id: songId,
                 title: title,
                 titleLower: title.toLowerCase(),
                 isLiked: isLiked,
                 element: songElement
             });
        });
        return songs;
    }

    function populateDeleteSongListIfNeeded() {
        const songListDiv = document.getElementById('deleteSongList');
        if (!songListDiv) return;
        if (isDeleting || isDownloading) { logDebug("Skipping delete list population during active operation."); return; }
        if (songListDiv.innerHTML === '' || songListDiv.innerHTML === 'Loading...' || songListDiv.children.length === 0) {
            populateDeleteSongList();
        }
    }
    function populateDeleteSongList() {
         if (currentView !== 'selective' || isMinimized) return;
         if (isDeleting || isDownloading) { logDebug("Skipping delete list population during active operation."); return; }
         logDebug('Populating DELETE song list with filters...');
         const songListDiv = document.getElementById('deleteSongList');
         const deleteCounter = document.getElementById('deleteCounter');
         if (!songListDiv) return;
         songListDiv.innerHTML = 'Loading...';
         if(deleteCounter) deleteCounter.textContent = 'Deleted: 0 / 0';
         const selectAllCheckbox = document.getElementById('deleteSelectAll');
         if (selectAllCheckbox) selectAllCheckbox.checked = false;
         const ignoreLikedCheckbox = document.getElementById('ignoreLikedToggleDelete');
         if(ignoreLikedCheckbox) ignoreLikedCheckbox.checked = ignoreLikedSongsDeleteState;

         const keywordInput = document.getElementById('deleteKeywordFilterInput');
         const keywordString = keywordInput ? keywordInput.value : '';
         const dynamicIgnoreKeywords = keywordString.split(',').map(k => k.trim().toLowerCase()).filter(k => k !== '');
         logDebug(`Keywords to ignore for delete: [${dynamicIgnoreKeywords.join(', ')}]`);

         setTimeout(() => { // Delay to allow UI to render "Loading..."
             const songs = getSongDataFromPage();
             songListDiv.innerHTML = '';

             if (songs.length === 0) {
                 songListDiv.innerHTML = '<p style="color:#d0d0d0;text-align:center;font-size:12px;">No songs found on page.</p>';
                 if(!isDeleting && !isDownloading) updateStatusMessage("No songs found.");
                 return;
             }

             let ignoredCount = 0;
             let visibleCount = 0;
             songs.forEach(song => {
                 const keywordMatch = dynamicIgnoreKeywords.length > 0 && dynamicIgnoreKeywords.some(keyword => song.titleLower.includes(keyword));
                 const likedMatch = ignoreLikedSongsDeleteState && song.isLiked;
                 const shouldIgnore = keywordMatch || likedMatch;

                 let ignoreReason = '';
                 if (keywordMatch) ignoreReason += 'Keyword';
                 if (likedMatch) ignoreReason += (keywordMatch ? ' & Liked' : 'Liked');

                 const label = document.createElement('label');
                 label.innerHTML = `<input type="checkbox" data-song-id="${song.id}" ${shouldIgnore ? 'disabled' : ''}> ${song.title}`;
                 if (song.isLiked) label.classList.add('liked');

                 if (shouldIgnore) {
                    label.classList.add('ignored');
                    label.title = `Ignoring for delete: ${ignoreReason}`;
                    ignoredCount++;
                 } else {
                    visibleCount++;
                 }
                 songListDiv.appendChild(label);
             });

             logDebug(`Populated DELETE list: ${songs.length} total, ${visibleCount} selectable, ${ignoredCount} ignored.`);
             if(!isDeleting && !isDownloading) updateStatusMessage(`Loaded ${songs.length} songs (${ignoredCount} ignored).`);
         }, 100);
     }

    function populateDownloadSongListIfNeeded() {
        const songListDiv = document.getElementById('downloadSongList');
         if (!songListDiv) return;
         if (isDeleting || isDownloading) { logDebug("Skipping download list population during active operation."); return; }
        if (songListDiv.innerHTML === '' || songListDiv.innerHTML === 'Loading...' || songListDiv.children.length === 0) {
            populateDownloadSongList();
        }
    }
    function populateDownloadSongList() {
        if (currentView !== 'download' || isMinimized) return;
        if (isDeleting || isDownloading) { logDebug("Skipping download list population during active operation."); return; }
        logDebug('Populating DOWNLOAD song list with filters...');
        const songListDiv = document.getElementById('downloadSongList');
        const downloadCounter = document.getElementById('downloadCounter');
        if (!songListDiv) return;
        songListDiv.innerHTML = 'Loading...';
        if(downloadCounter) downloadCounter.textContent = 'Downloaded: 0 / 0';
        const selectAllCheckbox = document.getElementById('downloadSelectAll');
        if (selectAllCheckbox) selectAllCheckbox.checked = false;

        const keywordInput = document.getElementById('downloadKeywordFilterInput');
        const keywordString = keywordInput ? keywordInput.value : '';
        const filterKeywords = keywordString.split(',').map(k => k.trim().toLowerCase()).filter(k => k !== '');
        logDebug(`Keywords to filter for download: [${filterKeywords.join(', ')}]`);

        setTimeout(() => { // Delay to allow UI to render "Loading..."
            const songs = getSongDataFromPage();
            songListDiv.innerHTML = '';

             if (songs.length === 0) {
                 songListDiv.innerHTML = '<p style="color:#d0d0d0;text-align:center;font-size:12px;">No songs found on page.</p>';
                 if(!isDeleting && !isDownloading) updateStatusMessage("No songs found.");
                 updateSelectLikedButtonText();
                 return;
             }

            let displayedCount = 0;
            songs.forEach(song => {
                const keywordMatch = filterKeywords.length === 0 || filterKeywords.some(keyword => song.titleLower.includes(keyword));

                if (keywordMatch) {
                    const label = document.createElement('label');
                    label.innerHTML = `<input type="checkbox" data-song-id="${song.id}" data-is-liked="${song.isLiked}"> ${song.title}`;
                    if (song.isLiked) {
                        label.classList.add('liked');
                    }
                    songListDiv.appendChild(label);
                    displayedCount++;
                } else {
                    logDebug(`Filtering out song for download view ${song.id} ('${song.title}') due to keywords.`);
                }
            });

            logDebug(`Populated DOWNLOAD list: ${songs.length} total, ${displayedCount} displayed after filtering.`);
            if(!isDeleting && !isDownloading) updateStatusMessage(`Showing ${displayedCount} of ${songs.length} songs.`);
            updateSelectLikedButtonText();
        }, 100);
    }

    function toggleSelectAll(event, listSelector) {
        if (isMinimized || isDeleting || isDownloading) return;
        const isChecked = event.target.checked;
        const checkboxes = document.querySelectorAll(`${listSelector} input[type="checkbox"]:not(:disabled)`);
        checkboxes.forEach(cb => cb.checked = isChecked);
        logDebug(`Select All Toggled in ${listSelector}: ${isChecked} (${checkboxes.length} items affected)`);
        if(listSelector === '#downloadSongList') {
            updateSelectLikedButtonText();
        }
    }

    function updateSelectLikedButtonText() {
        if (currentView !== 'download' || isMinimized) return;
        const button = document.getElementById('downloadSelectLiked');
        if (!button) return;

        const checkboxes = document.querySelectorAll('#downloadSongList input[type="checkbox"]:not(:disabled)');
        if (checkboxes.length === 0) {
             button.textContent = 'Select Liked';
             return;
        }
        let shouldSelect = false;
        checkboxes.forEach(cb => {
            if (cb.dataset.isLiked === 'true' && !cb.checked) {
                shouldSelect = true;
            }
        });
        button.textContent = shouldSelect ? 'Select Liked' : 'Deselect Liked';
    }

    function toggleSelectLiked() {
        if (currentView !== 'download' || isMinimized || isDeleting || isDownloading) return;
        const checkboxes = document.querySelectorAll('#downloadSongList input[type="checkbox"]:not(:disabled)');
        if (checkboxes.length === 0) {
            log("No songs available to select.", "warn");
            return;
        }
        let shouldSelect = false;
        for (const cb of checkboxes) {
            if (cb.dataset.isLiked === 'true' && !cb.checked) {
                shouldSelect = true;
                break;
            }
        }

        let changedCount = 0;
        checkboxes.forEach(cb => {
            if (cb.dataset.isLiked === 'true') {
                 if (cb.checked !== shouldSelect) {
                     cb.checked = shouldSelect;
                     changedCount++;
                 }
            }
        });
        log(`Toggled selection for ${changedCount} liked songs. Action: ${shouldSelect ? 'Select' : 'Deselect'}`);
        updateStatusMessage(`${shouldSelect ? 'Selected' : 'Deselected'} ${changedCount} liked songs.`);
        const selectAllCheckbox = document.getElementById('downloadSelectAll');
        if (selectAllCheckbox) {
            const allVisibleCheckboxes = document.querySelectorAll('#downloadSongList input[type="checkbox"]:not(:disabled)');
            const allVisibleChecked = document.querySelectorAll('#downloadSongList input[type="checkbox"]:not(:disabled):checked');
            selectAllCheckbox.checked = allVisibleCheckboxes.length > 0 && allVisibleCheckboxes.length === allVisibleChecked.length;
        }
        updateSelectLikedButtonText();
    }

    function clearDownloadSelection() {
        if (currentView !== 'download' || isMinimized || isDeleting || isDownloading) return;
        const checkboxes = document.querySelectorAll('#downloadSongList input[type="checkbox"]:checked');
        if (checkboxes.length === 0) {
            log("No songs currently selected.", "info");
            return;
        }
        checkboxes.forEach(cb => cb.checked = false);
        const selectAllCheckbox = document.getElementById('downloadSelectAll');
        if (selectAllCheckbox) selectAllCheckbox.checked = false;
        log(`Cleared selection for ${checkboxes.length} songs.`);
        updateStatusMessage("Selection cleared.");
        updateSelectLikedButtonText();
    }

    function updateCounter(type, count, total) {
       if (isMinimized) return;
       let counterElementId = '';
       if (type === 'delete') counterElementId = 'deleteCounter';
       else if (type === 'download') counterElementId = 'downloadCounter';
       else return;

       const counterElement = document.getElementById(counterElementId);
       if (counterElement) {
           const prefix = type === 'delete' ? 'Deleted' : 'Downloaded';
           counterElement.textContent = `${prefix}: ${count} / ${total}`;
       }
       logDebug(`${type} Counter Updated: ${count}/${total}`);
    }

    // --- Deletion Logic ---
    function getCurrentSongElements() {
        let listContainer = document.querySelector('div[data-sentry-component="InfiniteScroll"] > div.grow');
        if (!listContainer || listContainer.children.length === 0) {
            const allRiffRows = document.querySelectorAll('div[data-sentry-component="DraggableRiffRow"]');
            if (allRiffRows.length > 0 && allRiffRows[0].parentElement.childElementCount > 1) {
                 if (Array.from(allRiffRows[0].parentElement.children).every(child => child.getAttribute('data-sentry-component') === 'DraggableRiffRow' || child.tagName === 'HR')) {
                    listContainer = allRiffRows[0].parentElement;
                 }
            }
        }
        if(listContainer) {
             logDebug("Using list container:", listContainer);
             return listContainer.querySelectorAll(':scope > div[data-sentry-component="DraggableRiffRow"]');
        }
        log("Warning: Specific list container not found, using global fallback selector for song elements.", "warn");
        return document.querySelectorAll('div[data-sentry-component="DraggableRiffRow"]');
    }

    async function deleteSelectedSongs() {
        if (isMinimized) { log("Please restore the UI to delete.", "warn"); return; }
        if (currentView !== 'selective') { log("Selective delete only available in Selective View.", "warn"); return; }
        if (isDeleting || isDownloading) { log("Another operation is already in progress.", "warn"); return; }

        const checkboxes = document.querySelectorAll('#deleteSongList input[type="checkbox"]:checked:not(:disabled)');
        const totalToDelete = checkboxes.length;

        if (totalToDelete === 0) {
            updateCounter('delete', 0, 0);
            log('No valid songs selected for deletion.');
            updateStatusMessage('No songs selected or all selected are ignored.');
            return;
        }

        isDeleting = true;
        setAllButtonsDisabled(true);
        const songIdsToDelete = Array.from(checkboxes).map(cb => cb.dataset.songId);
        log(`Starting deletion for ${totalToDelete} selected song IDs: [${songIdsToDelete.join(', ')}]`);
        updateCounter('delete', 0, totalToDelete);
        updateStatusMessage(`Deleting ${totalToDelete} selected...`);

        let deletedCount = 0;
        let criticalErrorOccurred = false;

        for (const songId of songIdsToDelete) {
            logDebug(`Processing Song ID for delete: ${songId}`);
            if (criticalErrorOccurred || !isDeleting) {
                 log(`Stopping deletion loop. CritErr: ${criticalErrorOccurred}, IsDeleting: ${isDeleting}`, "warn");
                 break;
            }
            const songElement = document.querySelector(`div[data-sentry-component="DraggableRiffRow"] a[href="/song/${songId}"]`)?.closest('div[data-sentry-component="DraggableRiffRow"]');
            if (!songElement) {
                log(`Song row for ID ${songId} not found on page (already deleted?). Skipping.`, "warn");
                const checkboxToRemove = document.querySelector(`#deleteSongList input[data-song-id="${songId}"]`);
                checkboxToRemove?.closest('label')?.remove();
                continue;
            }
            const titleData = getSongDataFromPage().find(s => s.id === songId);
            const title = titleData ? titleData.title : `song ID ${songId}`;

            logDebug(`Found element for ${title} (ID: ${songId}). Attempting delete...`);
            const success = await processSingleAction(songElement, 'delete', songId);
            logDebug(`processSingleAction(delete) result for ID ${songId}: ${success}`);

            if (success) {
                deletedCount++;
                updateCounter('delete', deletedCount, totalToDelete);
                updateStatusMessage(`Deleted ${deletedCount}/${totalToDelete}...`);
                logDebug(`Successfully processed deletion for ID ${songId}. Count: ${deletedCount}`);
                 const checkboxToRemove = document.querySelector(`#deleteSongList input[data-song-id="${songId}"]`);
                 checkboxToRemove?.closest('label')?.remove();
            } else {
                log(`Failed to delete ${title} (ID: ${songId}). Stopping selective delete process.`, "error");
                updateStatusMessage(`Error deleting ${title}. Stopped.`);
                criticalErrorOccurred = true;
            }
            logDebug(`Delete loop iteration end for ID: ${songId}. Critical Error: ${criticalErrorOccurred}`);
            await delay(50);
        }

        log(`Selective deletion loop finished. ${deletedCount} of ${totalToDelete} songs attempted.`);
        updateStatusMessage(criticalErrorOccurred ? `Deletion stopped due to error. ${deletedCount} deleted.` : `Selected deletion complete. ${deletedCount} deleted.`);
        isDeleting = false;
        setAllButtonsDisabled(false);
        populateDeleteSongList(); // Refresh the list
    }

    function stopBulkDeletionByUser() {
        if (!isDeleting || currentView !== 'bulk') {
            logDebug("Stop bulk deletion called but not applicable.");
            return;
        }
        log("Bulk deletion stop requested by user.");
        updateStatusMessage("Stopping bulk deletion...");
        stopBulkDeletionSignal = true;
        isDeleting = false; // This will signal the loop in deleteAllSongsInLibrary to terminate
    }

     async function deleteAllSongsInLibrary() {
         if (isMinimized) { log("Please restore the UI to delete.", "warn"); return; }
         if (currentView !== 'bulk') { log("Bulk delete only available in Bulk View.", "warn"); return; }
         // isDeleting check is handled by the caller (deleteAllButton event listener)

         stopBulkDeletionSignal = false; // Reset signal at the start of new operation
         isDeleting = true;
         setAllButtonsDisabled(true);

         const deleteAllBtn = document.getElementById('deleteAllButton');
         if (deleteAllBtn) deleteAllBtn.textContent = "Stop Deletion";

         log("--- STARTING LIBRARY DELETION (Bulk Mode / No-Scroll) ---");
         updateStatusMessage("Starting full library deletion...");

         let totalDeleted = 0;
         let emptyChecks = 0;
         let currentElements;

         while (isDeleting) {
             await delay(500);
             if (!isDeleting) { log("Deletion loop terminating (isDeleting is false).", "warn"); break; }

             currentElements = getCurrentSongElements();
             let currentSize = currentElements.length;
             log(`Checking for songs... Found ${currentSize}.`);

             if (currentSize === 0) {
                 log(`No songs found. Waiting ${EMPTY_RETRY_DELAY / 1000}s (Check ${emptyChecks + 1}/${MAX_EMPTY_CHECKS})...`);
                 updateStatusMessage(`No songs. Re-checking in ${EMPTY_RETRY_DELAY / 1000}s...`);
                 await delay(EMPTY_RETRY_DELAY);
                 if (!isDeleting) { log("Deletion stopped during empty wait.", "warn"); break; }

                 currentElements = getCurrentSongElements();
                 currentSize = currentElements.length;
                 log(`Re-checking after delay... Found ${currentSize} songs.`);

                 if (currentSize > 0) {
                     log("Songs found after wait. Continuing deletion.");
                     updateStatusMessage(`Found ${currentSize} songs after wait. Resuming...`);
                     emptyChecks = 0;
                 } else {
                     emptyChecks++;
                     log(`Still empty (Check ${emptyChecks}/${MAX_EMPTY_CHECKS}).`);
                     updateStatusMessage(`Still empty (Check ${emptyChecks}/${MAX_EMPTY_CHECKS}).`);
                     if (emptyChecks >= MAX_EMPTY_CHECKS) {
                         log("No songs found after multiple retries. Assuming library is empty or cannot load more.");
                         isDeleting = false; // Signal loop to terminate naturally
                         break;
                     }
                     continue;
                 }
             }

             emptyChecks = 0;
             log(`Processing batch of ${currentSize} songs...`);
             let batchDeleted = 0;

             while (currentSize > 0 && isDeleting) {
                 if (!isDeleting) { log("Deletion stopped during batch processing.", "warn"); break; }
                 const firstElement = getCurrentSongElements()[0];
                 if (!firstElement || !firstElement.parentNode) {
                     log(`Top song element disappeared unexpectedly. Re-evaluating list...`, "warn");
                     await delay(100);
                     currentElements = getCurrentSongElements();
                     currentSize = currentElements.length;
                     continue;
                 }

                 const titleLink = firstElement.querySelector('a[href^="/song/"]');
                 let titleElement = titleLink ? titleLink.querySelector('h4.text-primary, [data-sentry-element="RiffTitle"], div[class*="truncate"]') : firstElement.querySelector('[data-sentry-element="RiffTitle"], div[class*="truncate"], h4');
                 const title = titleElement ? titleElement.textContent.trim() : `Top song`;
                 const deletionIdentifier = `Bulk ${totalDeleted + batchDeleted + 1}`;

                 log(`Deleting: ${title} (${deletionIdentifier})...`);
                 updateStatusMessage(`Deleting ${title} (${totalDeleted + batchDeleted + 1} total...)`);

                 const success = await processSingleAction(firstElement, 'delete', deletionIdentifier);

                 if (success) {
                     batchDeleted++;
                     await delay(DELETION_DELAY / 2);
                     currentElements = getCurrentSongElements();
                     currentSize = currentElements.length;
                 } else {
                     log(`Failed to delete ${title}. Stopping bulk delete.`, "error");
                     updateStatusMessage(`Error deleting ${title}. Stopped.`);
                     isDeleting = false; // Signal loop to terminate due to error
                     break;
                 }
                  await delay(50);
             }

             totalDeleted += batchDeleted;
             if (isDeleting) {
                 log(`Batch attempt complete. Deleted ${batchDeleted} this round. Total: ${totalDeleted}. Checking for more...`);
                 updateStatusMessage(`Batch complete. Total: ${totalDeleted}. Checking for more...`);
             }
         }

         // --- Post-loop cleanup and status reporting ---
         if (deleteAllBtn) {
             deleteAllBtn.textContent = "Delete Entire Library";
         }

         let finalLogMessage, finalUiMessage;

         if (stopBulkDeletionSignal) {
             finalLogMessage = `--- BULK DELETION STOPPED BY USER --- Total deleted: ${totalDeleted}`;
             finalUiMessage = `Deletion stopped by user. Total: ${totalDeleted}.`;
         } else if (emptyChecks >= MAX_EMPTY_CHECKS) {
             const finalCheckElements = getCurrentSongElements(); // Re-check for accurate final message
             if (totalDeleted === 0 && finalCheckElements.length === 0) {
                 finalLogMessage = `--- BULK DELETION COMPLETE (Library Appears Empty) --- Total deleted: ${totalDeleted}`;
                 finalUiMessage = "Library appears empty. No songs found or deleted.";
             } else {
                 finalLogMessage = `--- BULK DELETION COMPLETE (No More Songs Found After Retries) --- Total deleted: ${totalDeleted}`;
                 finalUiMessage = `Deletion complete. No more songs found after retries. Total: ${totalDeleted}.`;
             }
         } else { // Loop terminated for other reasons (e.g., error, or all songs deleted before emptyChecks maxed out)
             finalLogMessage = `--- BULK DELETION FINISHED --- Total deleted: ${totalDeleted}`;
             finalUiMessage = `Deletion process finished. Total: ${totalDeleted}.`;
         }

         log(finalLogMessage);
         updateStatusMessage(finalUiMessage);

         isDeleting = false; // Ensure isDeleting is definitively false
         stopBulkDeletionSignal = false; // Reset for the next potential run
         setAllButtonsDisabled(false);
     }

    // --- Download Logic ---
    async function startDownloadQueue() {
        if (isMinimized) { log("Please restore the UI to download.", "warn"); return; }
        if (currentView !== 'download') { log("Download only available in Download View.", "warn"); return; }
        if (isDeleting || isDownloading) { log("Another operation is already in progress.", "warn"); return; }

        const checkboxes = document.querySelectorAll('#downloadSongList input[type="checkbox"]:checked:not(:disabled)');
        const totalSongsToDownload = checkboxes.length;

        if (totalSongsToDownload === 0) {
            updateCounter('download', 0, 0);
            log('No valid songs selected for download.');
            updateStatusMessage('No songs selected for download.');
            return;
        }

        const selectedFormats = [];
        if (document.getElementById('formatMP3')?.checked) selectedFormats.push('MP3');
        if (document.getElementById('formatM4A')?.checked) selectedFormats.push('M4A');
        if (document.getElementById('formatWAV')?.checked) selectedFormats.push('WAV');

        if (selectedFormats.length === 0) {
            log('No download formats selected.', 'error');
            updateStatusMessage('Please select at least one download format.');
            return;
        }

        isDownloading = true;
        setAllButtonsDisabled(true);
        const songIdsToDownload = Array.from(checkboxes).map(cb => cb.dataset.songId);
        const interSongDelayMs = downloadInterSongDelaySeconds * 1000;
        const intraFormatDelayMs = downloadIntraFormatDelaySeconds * 1000;

        log(`Starting download queue for ${totalSongsToDownload} songs. Formats: [${selectedFormats.join(', ')}], Inter-Song Delay: ${downloadInterSongDelaySeconds}s, Intra-Format Delay: ${downloadIntraFormatDelaySeconds}s.`);
        updateCounter('download', 0, totalSongsToDownload);
        updateStatusMessage(`Downloading ${totalSongsToDownload} songs (${selectedFormats.join('/')})...`);

        let songsProcessedCount = 0;
        let criticalErrorOccurred = false;

        for (const songId of songIdsToDownload) {
            logDebug(`Processing Song ID for download: ${songId}`);
            if (criticalErrorOccurred || !isDownloading) {
                log(`Stopping download loop. CritErr: ${criticalErrorOccurred}, IsDownloading: ${isDownloading}`, "warn");
                break;
            }
            const songElement = document.querySelector(`div[data-sentry-component="DraggableRiffRow"] a[href="/song/${songId}"]`)?.closest('div[data-sentry-component="DraggableRiffRow"]');
            if (!songElement) {
                log(`Song row for ID ${songId} not found on page. Skipping download for this song.`, "warn");
                songsProcessedCount++; // Count as processed for UI consistency, though skipped.
                updateCounter('download', songsProcessedCount, totalSongsToDownload);
                continue;
            }
            const titleData = getSongDataFromPage().find(s => s.id === songId);
            const title = titleData ? titleData.title : `song ID ${songId}`;
            let songDownloadAttempted = false;
            let songDownloadSuccessThisSong = false; // Tracks if at least one format succeeded for this song
            let formatIndex = 0;

            for (const format of selectedFormats) {
                if (!isDownloading) {
                     log(`Download stopped externally before processing format ${format} for ${songId}.`, "warn");
                     criticalErrorOccurred = true;
                     break;
                 }

                const currentSongElementCheck = document.querySelector(`div[data-sentry-component="DraggableRiffRow"] a[href="/song/${songId}"]`)?.closest('div[data-sentry-component="DraggableRiffRow"]');
                 if (!currentSongElementCheck) {
                      logWarn(`${title} (ID: ${songId}) element disappeared before downloading format ${format}. Skipping remaining formats for this song.`);
                      criticalErrorOccurred = true; // Treat as critical for this song
                      break; // Stop trying formats for this song
                 }

                 logDebug(`Attempting download for ${title} (ID: ${songId}) - Format: ${format}`);
                 updateStatusMessage(`Downloading ${songsProcessedCount + 1}/${totalSongsToDownload}: ${title} (${format})...`);
                 songDownloadAttempted = true;

                 const success = await processSingleAction(currentSongElementCheck, 'download', `${songId}-${format}`, format);
                 logDebug(`processSingleAction(download) result for ID ${songId}, Format ${format}: ${success}`);

                 if (success) {
                     songDownloadSuccessThisSong = true; // At least one format was successful
                     log(`Successfully initiated download for ${title} (${format}).`);
                     formatIndex++;
                     if (formatIndex < selectedFormats.length && isDownloading && intraFormatDelayMs > 0) {
                         logDebug(`Waiting ${downloadIntraFormatDelaySeconds}s before next format...`);
                         await delay(intraFormatDelayMs);
                     } else if (isDownloading && intraFormatDelayMs <= 0 && formatIndex < selectedFormats.length) {
                         await delay(50); // Minimal delay if intra-format is 0
                     }
                 } else {
                     log(`Failed to download ${title} (ID: ${songId}) - Format: ${format}. Stopping queue.`, "error");
                     updateStatusMessage(`Error downloading ${title} (${format}). Stopped.`);
                     criticalErrorOccurred = true;
                     break; // Stop trying formats for this song AND stop the entire queue
                 }
            } // End format loop

            if (songDownloadAttempted) { // If we tried to download this song
                 if (songDownloadSuccessThisSong) { // And at least one format was initiated
                     songsProcessedCount++;
                     updateCounter('download', songsProcessedCount, totalSongsToDownload);
                 }
            } else if (!songDownloadAttempted && songElement) {
                // This case should ideally not be hit if logic is correct above
                // but if a song was selected but no formats were attempted for it (e.g. due to early !isDownloading)
                // we might still want to increment the counter to reflect it was "passed" in the UI.
                // For now, only increment if a download was truly successful for at least one format.
            }


            if (criticalErrorOccurred || !isDownloading) { // If a critical error occurred or download stopped
                 break; // Exit the main song loop
            }

            // Inter-song delay
            if (songsProcessedCount < totalSongsToDownload && isDownloading && (songIdsToDownload.indexOf(songId) < songIdsToDownload.length -1) ) {
                 log(`Waiting ${downloadInterSongDelaySeconds}s before next song...`);
                 updateStatusMessage(`Waiting ${downloadInterSongDelaySeconds}s before next song...`);
                 await delay(interSongDelayMs);
            }
        } // End song loop

        log(`Download queue finished. Attempted ${totalSongsToDownload} songs. Successfully processed (at least one format for) ${songsProcessedCount} songs.`);
        updateStatusMessage(criticalErrorOccurred ? `Download stopped due to error. ${songsProcessedCount} songs fully/partially downloaded.` : `Download queue complete. ${songsProcessedCount} songs fully/partially downloaded.`);
        isDownloading = false;
        setAllButtonsDisabled(false);
        populateDownloadSongList(); // Refresh list to clear selections or reflect changes
    }


    // --- Generic Action Processor (Handles Delete or Download) ---
    async function processSingleAction(songElement, actionType, identifier, format = null, retryCount = 0) {
        const logPrefix = `(${actionType} - ID/Index: ${identifier}) -`;

        if (!isDownloading && actionType === 'download') {
            log(`${logPrefix} Download process cancelled. Aborting action.`, "warn");
            return false;
        }
        if (!isDeleting && actionType === 'delete') {
            log(`${logPrefix} Deletion process cancelled. Aborting action.`, "warn");
            return false;
        }

        if (!songElement || !songElement.parentNode) {
            log(`${logPrefix} Song element is already gone. Assuming success for ${actionType}.`, "warn");
            return true;
        }

        const menuButton = songElement.querySelector('button[data-sentry-element="MenuTrigger"]');
        if (!menuButton) {
            log(`${logPrefix} 'More options' button (using data-sentry-element="MenuTrigger") not found. Cannot proceed.`, "error");
            logDebug(`${logPrefix} Searched within element:`, songElement);
            return false;
        }

        logDebug(`${logPrefix} Clicking 'More options' button:`, menuButton);
        if (!simulateClick(menuButton)) {
            log(`${logPrefix} Failed to simulate click on 'More options'.`, "error");
            return false;
        }

        await delay(DROPDOWN_DELAY);

        let primaryActionText = actionType === 'delete' ? 'delete' : 'download';
        let primaryActionItem = null;
        let downloadMenuItemId = null;

        let popperWrapper = document.querySelector(`div[data-radix-popper-content-wrapper][style*="transform: translate"]`);
        let potentialItems = [];
        let menuContentElement = null;

        if (popperWrapper) {
             menuContentElement = popperWrapper.querySelector('div[data-radix-menu-content][data-state="open"]');
             if (menuContentElement) {
                 potentialItems = menuContentElement.querySelectorAll(':scope > [role="menuitem"], :scope > [data-radix-collection-item]');
                 logDebug(`${logPrefix} Found ${potentialItems.length} potential menu items in active popper's open menu.`);
             } else { logDebug(`${logPrefix} No open menu content in active popper.`); }
        }
        if (!menuContentElement) {
             logDebug(`${logPrefix} No menu in active popper or no active popper. Searching globally for open menus.`);
             const openMenus = document.querySelectorAll('div[data-radix-menu-content][data-state="open"]');
             if (openMenus.length > 0) {
                 menuContentElement = openMenus[openMenus.length - 1]; // Take the last one, likely the most recent
                 potentialItems = menuContentElement.querySelectorAll(':scope > [role="menuitem"], :scope > [data-radix-collection-item]');
                 logDebug(`${logPrefix} Found ${potentialItems.length} potential menu items in the last globally open menu (ID: ${menuContentElement.id}).`);
             } else {
                 logDebug(`${logPrefix} No globally open menus found.`);
             }
        }

        if (!menuContentElement) {
            log(`${logPrefix} Could not find any open menu content.`, "error");
            if (retryCount < MAX_RETRIES) {
                 logWarn(`${logPrefix} Retrying to open menu and find content (Attempt ${retryCount + 1}/${MAX_RETRIES})`);
                 try { document.body.click(); await delay(150); } catch(e){} // Close any potentially stuck menus
                 return processSingleAction(songElement, actionType, identifier, format, retryCount + 1);
            }
            try { document.body.click(); await delay(50); } catch(e){} // Final attempt to close
            return false;
        }

        primaryActionItem = Array.from(potentialItems).find(el => {
            const isVisible = el.offsetParent !== null;
            if (!isVisible) return false;
            let itemText = '';
            const lineClampDiv = el.querySelector('.line-clamp-2'); // Common pattern for text
            if (lineClampDiv) {
                itemText = lineClampDiv.textContent.trim().toLowerCase();
            } else { // Fallback for different structures
                const mainContentContainer = el.querySelector('.flex.items-center.gap-4'); // another common pattern
                if (mainContentContainer) {
                    let textFromNodes = '';
                    mainContentContainer.childNodes.forEach(node => {
                        if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
                            textFromNodes += node.textContent.trim() + ' ';
                        }
                        else if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('overflow-hidden') && !node.querySelector('svg')) { // Text usually in a div without an icon next to it
                             textFromNodes += node.textContent.trim() + ' ';
                        }
                    });
                    itemText = textFromNodes.trim().toLowerCase();
                } else { // Simplest fallback: direct text content of the menu item
                    itemText = el.textContent.trim().toLowerCase();
                }
            }

            if (itemText === primaryActionText) {
                 if (primaryActionText === 'download') {
                     downloadMenuItemId = el.getAttribute('aria-controls'); // For Radix submenus
                     logDebug(`${logPrefix} Found '${primaryActionText}' item ('${itemText}'), controls submenu ID: ${downloadMenuItemId || 'Not found/applicable'}`);
                 } else {
                     logDebug(`${logPrefix} Found '${primaryActionText}' item ('${itemText}')`);
                 }
                 return true;
            }
            return false;
        });

        if (!primaryActionItem && retryCount < MAX_RETRIES) {
            log(`${logPrefix} '${primaryActionText}' option not found in menu (Attempt ${retryCount + 1}/${MAX_RETRIES}). Retrying click sequence...`, "warn");
            try { document.body.click(); await delay(150); } catch(e){} // Close current menu
            if (!songElement || !songElement.parentNode) { // Check if song element still exists
                log(`${logPrefix} Song element disappeared before retry could occur for '${primaryActionText}'.`, "warn");
                return (actionType === 'delete'); // If deleting, assume it was deleted. If downloading, it's a fail.
            }
             const checkMenuButtonAgain = songElement.querySelector('button[data-sentry-element="MenuTrigger"]');
             if (!checkMenuButtonAgain) { // Check if menu button on song still exists
                  log(`${logPrefix} Song element's menu button disappeared before retry for '${primaryActionText}'.`, "warn");
                  return false;
             }
            return processSingleAction(songElement, actionType, identifier, format, retryCount + 1);
        }

        if (!primaryActionItem) {
            log(`${logPrefix} '${primaryActionText}' option not found after ${MAX_RETRIES} retries. Aborting action. Menu HTML snapshot:`, menuContentElement.innerHTML.substring(0, 700) + '...');
            try { document.body.click(); await delay(50); } catch(e){} // Close menu
            return false;
        }

        logDebug(`${logPrefix} Clicking primary action '${primaryActionText}' (item aria-controls: ${primaryActionItem.getAttribute('aria-controls') || 'N/A'}):`, primaryActionItem);
        if (!simulateClick(primaryActionItem)) {
            log(`${logPrefix} Failed to simulate click on '${primaryActionText}' option.`, "error");
            try { document.body.click(); await delay(50); } catch(e){} // Close menu
            return false;
        }

        if (actionType === 'delete') {
            await delay(DELETION_DELAY); // Wait for delete action to process on server/UI
            logDebug(`--- Finished processing ${logPrefix} (Assumed Success after Delete Click) ---`);
            return true;
        }
        else if (actionType === 'download') {
            if (!downloadMenuItemId) {
                 log(`${logPrefix} Submenu ID for 'Download' was not captured. Aborting download for format ${format}.`, "error");
                 try { document.body.click(); await delay(50); } catch(e){} // Close menu
                 return false;
            }

            let subMenuContent = null;
            let subMenuOpenRetries = 0;

            while (isDownloading && subMenuOpenRetries < MAX_SUB_MENU_OPEN_RETRIES) {
                await delay(DOWNLOAD_MENU_DELAY);

                subMenuContent = document.getElementById(downloadMenuItemId);
                if (subMenuContent && subMenuContent.getAttribute('data-state') === 'open') {
                    logDebug(`${logPrefix} Download sub-menu (ID: ${downloadMenuItemId}) found by ID and is open.`);
                    break;
                } else {
                    logDebug(`${logPrefix} Submenu (ID: ${downloadMenuItemId}) not by ID or not open. Popper search retry ${subMenuOpenRetries + 1}/${MAX_SUB_MENU_OPEN_RETRIES}...`);
                    const allPoppers = Array.from(document.querySelectorAll(`div[data-radix-popper-content-wrapper][style*="transform: translate"]`));
                    let foundInPopper = false;
                    if (allPoppers.length > 0) {
                        for (let i = allPoppers.length - 1; i >= 0; i--) { // Check most recent poppers first
                            const currentPopper = allPoppers[i];
                            const potentialSubMenu = currentPopper.querySelector(`div[data-radix-menu-content][data-state="open"][id="${downloadMenuItemId}"]`);
                            if (potentialSubMenu) {
                                subMenuContent = potentialSubMenu;
                                logDebug(`${logPrefix} Found download sub-menu (ID: ${subMenuContent.id}) in popper and is open.`);
                                foundInPopper = true;
                                break;
                            }
                        }
                    }
                    if (foundInPopper) break;
                }

                subMenuOpenRetries++;
                if (subMenuContent && subMenuContent.getAttribute('data-state') !== 'open') {
                    logWarn(`${logPrefix} Submenu (ID: ${downloadMenuItemId}) found but not 'open' (state: ${subMenuContent.getAttribute('data-state')}). Retry ${subMenuOpenRetries}/${MAX_SUB_MENU_OPEN_RETRIES}.`);
                } else if (!subMenuContent) {
                    logWarn(`${logPrefix} Submenu (ID: ${downloadMenuItemId}) not found. Retry ${subMenuOpenRetries}/${MAX_SUB_MENU_OPEN_RETRIES}.`);
                }
                 // If sub-menu not opening, try re-clicking the "Download" item in main menu (if still visible)
                 if (subMenuOpenRetries > 1 && primaryActionItem && primaryActionItem.offsetParent !== null && primaryActionItem.getAttribute('data-state') === 'closed') { // Check if main menu item is still there and its submenu is closed
                     logWarn(`${logPrefix} Submenu not opening. Re-clicking 'Download' in main menu. Attempt ${subMenuOpenRetries}.`);
                     if(!simulateClick(primaryActionItem)) { logError("Failed to re-click download for submenu."); break;}
                 }
            }

            if (!isDownloading && subMenuOpenRetries < MAX_SUB_MENU_OPEN_RETRIES) {
                log(`${logPrefix} Download cancelled while waiting for sub-menu.`, "warn");
                return false;
            }

            if (!subMenuContent || subMenuContent.getAttribute('data-state') !== 'open') {
                log(`${logPrefix} Download sub-menu (ID: ${downloadMenuItemId}) did not appear or open after ${MAX_SUB_MENU_OPEN_RETRIES} retries. Aborting download for this format.`, "error");
                try { document.body.click(); await delay(50); } catch(e){} // Close any open menus
                return false;
            }

            let formatItem = null;
            const formatTextUpper = format.toUpperCase();
            logDebug(`${logPrefix} Open sub-menu (ID: ${subMenuContent.id}). Searching for format '${formatTextUpper}'.`);
            const potentialFormatItems = subMenuContent.querySelectorAll(':scope > [role="menuitem"], :scope > [data-radix-collection-item]');
            formatItem = Array.from(potentialFormatItems).find(el => {
                const textDiv = el.querySelector('.line-clamp-2');
                const itemText = textDiv ? textDiv.textContent.trim().toUpperCase() : el.textContent.trim().toUpperCase();
                return itemText === formatTextUpper && el.offsetParent !== null && !el.querySelector('svg[data-icon="angle-right"]'); // Ensure it's a final item, not another sub-menu
            });


            if (!formatItem && retryCount < MAX_RETRIES) { // This retry is within processSingleAction, not sub-menu opening retries
                logWarn(`${logPrefix} Format '${formatTextUpper}' not in sub-menu (Attempt ${retryCount +1}/${MAX_RETRIES}). Re-checking sub-menu items...`, "warn");
                await delay(DOWNLOAD_MENU_DELAY / 2); // Give it a moment
                if (subMenuContent && subMenuContent.getAttribute('data-state') === 'open') { // Re-check if sub-menu is still open
                    const potentialFormatItemsAgain = subMenuContent.querySelectorAll(':scope > [role="menuitem"], :scope > [data-radix-collection-item]');
                    formatItem = Array.from(potentialFormatItemsAgain).find(el => {
                        const textDiv = el.querySelector('.line-clamp-2');
                        const itemText = textDiv ? textDiv.textContent.trim().toUpperCase() : el.textContent.trim().toUpperCase();
                        return itemText === formatTextUpper && el.offsetParent !== null && !el.querySelector('svg[data-icon="angle-right"]');
                    });
                    if (formatItem) logDebug(`${logPrefix} Found format '${formatTextUpper}' in sub-menu after delay/re-check.`);
                } else {
                    log(`${logPrefix} Sub-menu closed unexpectedly before format '${formatTextUpper}' could be re-checked. Critical.`, 'error');
                    return false; // Sub-menu gone, critical failure for this format.
                }
            }


            if (!formatItem) {
                 log(`${logPrefix} Format option '${formatTextUpper}' not found in sub-menu after checks. Aborting download for this format.`, "error");
                 logDebug(`${logPrefix} Submenu (ID: ${subMenuContent.id}) content snapshot:`, subMenuContent.innerHTML.substring(0, 700) + '...');
                 // Don't close all menus here, as other formats for the same song might still be processed from this state.
                 // The calling function will handle closing all menus if the entire song fails.
                 return false;
            }

            logDebug(`${logPrefix} Clicking format '${formatTextUpper}' option:`, formatItem);
            if (!simulateClick(formatItem)) {
                log(`${logPrefix} Failed to simulate click on format '${formatTextUpper}' option. Aborting download for this format.`, "error");
                return false;
            }

            await delay(DOWNLOAD_ACTION_DELAY); // Wait for download to initiate
            logDebug(`--- Finished processing ${logPrefix} (Assumed Success after Format Click) ---`);
            return true;
        }

        log(`${logPrefix} Reached unexpected end of function. Action type: ${actionType}`, "error");
        return false;
    }


    // --- Utility ---
    function setAllButtonsDisabled(disabled) {
        if (!uiElement) return;
        if (isMinimized && disabled) {
            logDebug("UI minimized, setAllButtonsDisabled(true) called, skipping most visual updates.");
            return;
        }
         if (isMinimized && !disabled) {
             logDebug("UI restoring/enabled, setAllButtonsDisabled(false) proceeding.");
         }

        const buttons = uiElement.querySelectorAll('#riffControlContent button');
        buttons.forEach(btn => {
            if (btn.id === 'minimizeButton') return;

            if (btn.id === 'deleteAllButton' && currentView === 'bulk' && isDeleting) {
                btn.disabled = false; // "Stop Deletion" button must be enabled
            } else {
                btn.disabled = disabled;
            }
        });

        const inputs = uiElement.querySelectorAll('#riffControlContent input, #riffControlContent select');
        inputs.forEach(input => {
             if (input.id.startsWith('format') || input.id.includes('DelayInput')) {
                  input.disabled = false;
             } else {
                  input.disabled = disabled;
             }
         });

        const labels = uiElement.querySelectorAll('#riffControlContent label');
         labels.forEach(label => {
             const isFormatLabel = label.closest('.downloadFormatContainer') !== null;
             const isDelayLabel = label.closest('.downloadDelayContainer') !== null;
             const forActiveStopButton = label.htmlFor === 'deleteAllButton' && currentView === 'bulk' && isDeleting;

             if (forActiveStopButton || isFormatLabel || isDelayLabel) {
                 label.style.cursor = 'pointer';
                 label.style.opacity = '1';
             } else {
                 label.style.cursor = disabled ? 'not-allowed' : 'pointer';
                 label.style.opacity = disabled ? '0.7' : '1';
             }
         });

        const songListCheckboxes = uiElement.querySelectorAll('.songListContainer input[type="checkbox"]');
        songListCheckboxes.forEach(cb => {
            cb.disabled = disabled;
        });

        if (!disabled) { // If we are enabling controls (operation finished)
            if(currentView === 'selective' && document.getElementById('selectiveModeControls')?.style.display === 'block') {
                populateDeleteSongListIfNeeded();
            }
            if(currentView === 'download' && document.getElementById('downloadQueueControls')?.style.display === 'block') {
                populateDownloadSongListIfNeeded();
            }
        }

        const status = document.getElementById('statusMessage');
        if (status) {
             status.style.pointerEvents = (disabled && !(currentView === 'bulk' && isDeleting)) ? 'none' : 'auto';
        }
        logDebug(`Controls ${disabled ? 'mostly disabled' : 'enabled'} (deleteAllButton may remain active in bulk mode)`);
    }


    // --- Initialization ---
    function waitForPageLoad(callback) {
        if (document.readyState === "complete" || document.readyState === "interactive") {
            setTimeout(callback, 1000); // Delay slightly to ensure page scripts have run
        } else {
            window.addEventListener('load', () => { setTimeout(callback, 1000); }, { once: true });
        }
    }

    function init() {
        // Ensure script only runs on the intended page
        if (window.location.pathname.includes('/library/my-songs')) {
            try {
                log(`Riffusion Multitool Script Loaded (v${GM_info.script.version}).`);
                createMainUI(); // Create the UI elements
                log(`Initialized. Current View: ${currentView}. UI is ${isMinimized ? 'minimized (Top-Right)' : 'visible'}.`);
            } catch (e) {
                console.error("[RiffTool] Initialization failed:", e);
                alert("[RiffTool] Failed to initialize script. See console for errors.");
            }
        } else {
            logDebug("Not on the target /library/my-songs page.");
        }
    }

    waitForPageLoad(init);

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址