SoundCloud: Additional "Add to playlist" button

Adds an additional "Add to Playlist" buttons for easier and quicker access.

От 16.04.2025. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         SoundCloud: Additional "Add to playlist" button
// @description  Adds an additional "Add to Playlist" buttons for easier and quicker access.
// @version      0.8
// @author       iammordaty
// @namespace    https://github.com/iammordaty
// @match        https://soundcloud.com/*
// @license      MIT
// @grant        none
// @icon         https://a-v2.sndcdn.com/assets/images/sc-icons/favicon-2cadd14bdb.ico
// ==/UserScript==

// This must be unique on the page
const ANIMATION_NAME = 'IAMMORDATY-SC-AATPB-ON-NODE-INSTERTED';

// https://davidwalsh.name/detect-node-insertion
document.head.insertAdjacentHTML('beforeend', `
    <style>
    @keyframes ${ANIMATION_NAME} {
        from { opacity: 0.99; }
        to { opacity: 1; }
    }

    .soundList__item, .listenEngagement__footer, .trackList__item, .historicalPlays__item {
        animation-duration: 0.001s;
        animation-name: ${ANIMATION_NAME};
    }
    </style>
`);

const BUTTON_CLASS_NAMES = [
    'sc-button-medium',
    'sc-button-secondary',
    'sc-button-small',
    'sc-button-icon',
    'sc-button',
    'sc-button-responsive',
];

const getButtonClassList = refNodeClassList => {
    const classList = BUTTON_CLASS_NAMES.filter(value => refNodeClassList.includes(value));

    return [ ...classList, 'sc-button-add-to-playlist', 'sc-button-addtoset' ];
}

const createButton = (container, refNode) => {
    const button = document.createElement('button');

    button.setAttribute('role', 'button');

    const classList = getButtonClassList([ ...refNode.classList ]);
    button.classList.add(...classList);

    // button.innerHTML = 'Add to Playlist';
    button.setAttribute('title', 'Add this track to Playlist');

    const innerDiv = document.createElement('div');

    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 16 16');
    svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    svg.setAttribute('aria-hidden', 'true');

    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M3.25 7V4.75H1v-1.5h2.25V1h1.5v2.25H7v1.5H4.75V7h-1.5zM9 4.75h6v-1.5H9v1.5zM15 9.875H1v-1.5h14v1.5zM1 15h14v-1.5H1V15z');
    path.setAttribute('fill', 'currentColor');

    svg.appendChild(path);
    innerDiv.appendChild(svg);

    button.appendChild(innerDiv);

    button.addEventListener('click', () => {
        // Ignoring case sensitiveness in querySelectorAll: https://stackoverflow.com/a/38399344
        container.querySelector('button[title="more" i]').click();

        document.querySelector('button[title="add to playlist" i]').click();
    }, false);

    return button;
}


const insertButton = (button, refButton) => refButton.parentNode.insertBefore(button, refButton);

const onNodeInsert = ({ animationName, target: container }) => {
    if (animationName !== ANIMATION_NAME) {
        return;
    }

    const refButton = container.querySelector('.sc-button-more');

    if (!refButton) {
        return;
    }

    const button = createButton(container, refButton);

    insertButton(button, refButton);
}

document.addEventListener('animationstart', onNodeInsert, false);