Greasy Fork 还支持 简体中文。

Add and Remove Songs from Library on YouTube Music

Adds or removes all songs in the current playlist to/from your library with auto-scroll, counters, a tooltip, and a female TTS voice notification when completed.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Add and Remove Songs from Library on YouTube Music
// @version      1.7
// @license MIT
// @description  Adds or removes all songs in the current playlist to/from your library with auto-scroll, counters, a tooltip, and a female TTS voice notification when completed.
// @author       Casket Pizza
// @match        https://music.youtube.com/*
// @grant        none
// @namespace https://greasyfork.org/users/1374050
// ==/UserScript==

(function() {
    'use strict';

    let songCounter = 0;
    let counterElement;

    // Scroll to bottom of the page to load all songs
    async function scrollToBottom() {
        return new Promise((resolve) => {
            let lastScrollHeight = 0;
            const interval = setInterval(() => {
                window.scrollTo(0, document.body.scrollHeight); // Scroll to bottom
                let newScrollHeight = document.body.scrollHeight;

                if (newScrollHeight !== lastScrollHeight) {
                    lastScrollHeight = newScrollHeight; // Update last height
                } else {
                    clearInterval(interval); // Stop scrolling when no more new songs load
                    resolve();
                }
            }, 1000); // Check every 1 second
        });
    }

    // Function to add "+" and "-" buttons next to the search bar
    function addButtons() {
        const searchBar = document.querySelector('ytmusic-search-box');
        if (!searchBar) return; // Ensure search bar exists

        const existingPlusButton = document.getElementById('addAllToLibraryButton');
        const existingMinusButton = document.getElementById('removeAllFromLibraryButton');
        if (existingPlusButton || existingMinusButton) return; // Avoid adding multiple buttons

        // Create "+" button to add all songs
        const plusButton = document.createElement('button');
        plusButton.id = 'addAllToLibraryButton';
        plusButton.innerHTML = '+';
        plusButton.style.fontSize = '20px';
        plusButton.style.marginLeft = '10px';
        plusButton.style.cursor = 'pointer';
        plusButton.style.background = 'none';
        plusButton.style.border = 'none';
        plusButton.style.color = 'white';
        plusButton.title = 'Click to add all songs to library'; // Tooltip for Add

        // Create "-" button to remove all songs
        const minusButton = document.createElement('button');
        minusButton.id = 'removeAllFromLibraryButton';
        minusButton.innerHTML = '-';
        minusButton.style.fontSize = '20px';
        minusButton.style.marginLeft = '10px';
        minusButton.style.cursor = 'pointer';
        minusButton.style.background = 'none';
        minusButton.style.border = 'none';
        minusButton.style.color = 'white';
        minusButton.title = 'Click to remove all songs from library'; // Tooltip for Remove

        // Insert the buttons next to the search bar
        searchBar.parentNode.insertBefore(plusButton, searchBar.nextSibling);
        searchBar.parentNode.insertBefore(minusButton, plusButton.nextSibling);

        // Add counter display in bottom-right corner
        counterElement = document.createElement('div');
        counterElement.id = 'songCounter';
        counterElement.style.position = 'fixed';
        counterElement.style.bottom = '80px'; // Move it up to avoid overlap with the media player
        counterElement.style.right = '20px';
        counterElement.style.fontSize = '16px';
        counterElement.style.color = 'white';
        counterElement.style.background = '#333';
        counterElement.style.padding = '10px';
        counterElement.style.borderRadius = '5px';
        counterElement.style.display = 'none'; // Hidden initially
        document.body.appendChild(counterElement);


        // Add event listener for "+" button to add all songs to the library
        plusButton.addEventListener('click', async function() {
            await scrollToBottom(); // Scroll to the bottom to load all songs
            songCounter = 0; // Reset counter
            counterElement.innerHTML = `Songs added: ${songCounter}`;
            counterElement.style.display = 'block'; // Show counter

            var song = document.body.querySelectorAll(".dropdown-trigger.style-scope.ytmusic-menu-renderer");

            for (var i = 0; i < song.length; i++) {
                song[i].click();
                var dropdown = document.body.querySelector("ytmusic-menu-popup-renderer[slot='dropdown-content']");

                if (dropdown != undefined) {
                    var addSong = dropdown.querySelector("tp-yt-paper-listbox#items")
                        .querySelector("ytmusic-toggle-menu-service-item-renderer.style-scope.ytmusic-menu-popup-renderer");

                    if (addSong != null) {
                        var actualAddSong = addSong.querySelector('yt-formatted-string.text.style-scope.ytmusic-toggle-menu-service-item-renderer');

                        if (actualAddSong != null && actualAddSong.innerHTML == 'Save to library') {
                            addSong.click();
                            songCounter++; // Increase counter
                            counterElement.innerHTML = `Songs added: ${songCounter}`;
                            console.log(`Song ${songCounter} saved to library`);
                            await new Promise(r => setTimeout(r, 200)); // Wait for the action to complete
                        }
                    }
                }

                await new Promise(r => setTimeout(r, 100)); // Avoid overloading the system
            }

            // Play TTS notification when the process is complete
            playTTS("Songs added.");
        });

        // Add event listener for "-" button to remove all songs from the library
        minusButton.addEventListener('click', async function() {
            await scrollToBottom(); // Scroll to the bottom to load all songs
            songCounter = 0; // Reset counter
            counterElement.innerHTML = `Songs removed: ${songCounter}`;
            counterElement.style.display = 'block'; // Show counter

            var song = document.body.querySelectorAll(".dropdown-trigger.style-scope.ytmusic-menu-renderer");

            for (var i = 0; i < song.length; i++) {
                song[i].click();
                var dropdown = document.body.querySelector("ytmusic-menu-popup-renderer[slot='dropdown-content']");

                if (dropdown != undefined) {
                    var removeSong = dropdown.querySelector("tp-yt-paper-listbox#items")
                        .querySelector("ytmusic-toggle-menu-service-item-renderer.style-scope.ytmusic-menu-popup-renderer");

                    if (removeSong != null) {
                        var actualRemoveSong = removeSong.querySelector('yt-formatted-string.text.style-scope.ytmusic-toggle-menu-service-item-renderer');

                        if (actualRemoveSong != null && actualRemoveSong.innerHTML == 'Remove from library') {
                            removeSong.click();
                            songCounter++; // Increase counter
                            counterElement.innerHTML = `Songs removed: ${songCounter}`;
                            console.log(`Song ${songCounter} removed from library`);
                            await new Promise(r => setTimeout(r, 200)); // Wait for the action to complete
                        }
                    }
                }

                await new Promise(r => setTimeout(r, 100)); // Avoid overloading the system
            }

            // Play TTS notification when the process is complete
            playTTS("Songs removed.");
        });
    }

    // Function to use TTS to say custom message with a female voice
    function playTTS(message) {
        const utterance = new SpeechSynthesisUtterance(message);
        utterance.lang = 'en-US';
        utterance.pitch = 1;
        utterance.rate = 1;

        // Select a female voice if available
        const voices = speechSynthesis.getVoices();
        const femaleVoice = voices.find(voice => voice.name.includes('Female') || voice.gender === 'female' || voice.name.includes('Samantha')); // Adjust for common female names

        if (femaleVoice) {
            utterance.voice = femaleVoice;
        }

        speechSynthesis.speak(utterance);
    }

    // Run the addButtons function when the page is loaded
    window.addEventListener('load', function() {
        // Wait for voices to be loaded and then add the buttons
        speechSynthesis.onvoiceschanged = addButtons;
    });

    // Reset counter and hide it after each button click
    window.addEventListener('beforeunload', function() {
        songCounter = 0;
        if (counterElement) {
            counterElement.style.display = 'none'; // Hide the counter on navigation/reload
        }
    });
})();