Deezer PiP - deezer.com

Adds PiP features to Deezer

// ==UserScript==
// @name        Deezer PiP - deezer.com
// @namespace   https://github.com/Thibb1
// @match       https://www.deezer.com/*
// @grant       none
// @version     1.0.1
// @author      Thibb1
// @description Adds PiP features to Deezer
// @license     GPL
// ==/UserScript==

let timer;
function debounce(func) {
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => { func.apply(this, args); }, 1000);
    };
}
const CLICK_TIMEOUT = 200;
const NEXT = '.player-controls [data-testid="StepForwardIcon"]';
const PREVIOUS = '.player-controls [data-testid="StepBackwardIcon"]';
const PAUSE = '.player-controls [data-testid="PauseIcon"]'
const PLAY = '.player-controls [data-testid="PlayIcon"]'
const QUEUELIST = '.player-full.player-lyrics-full';
const QUEUE = '.player-options > ul > li:nth-child(2) > button';
const FAVORITES = ".sidebar-nav-list > li:nth-child(5) > a";
const SHUFFLE = '[data-testid="user-shuffle-button"]';
const MIX = '[data-testid="NoteWaveIcon"]';
const REMOVE_FAV = '.player-bottom [data-testid="HeartFillIcon"]'
const ADD_FAV = '.player-bottom [data-testid="HeartIcon"]'

const STYLE_TEXT = `
    body {
        background: #212121;
        color: #fff;
        font-weight: 700;
    }
    button {
        flex: 1;
        height: 45px;
        font-size: 16px;
        box-shadow: 0px 0px 20px -20px;
        border-radius: 5px;
        cursor: pointer;
        transition: all 0.2s ease-in-out 0ms;
        user-select: none;
    }
    button:hove {
        box-shadow: 0px 0px 20px -18px;
    }
    button:active {
        transform: scale(0.95);
    }
    .shuffle {
        background: #ca2a36;
        color: #fff;
    }
    .main > div {
        flex: 1;
    }
    .main {
        flex-direction: column;
        width: 90%;
    }
    body, p, div {
        display: flex;
        gap: 8px;
        justify-content: center;
    }
`;


function getTitle() {
    return [...document.querySelectorAll('.track-link')].map((e) => e.textContent).join(' - ');
}

function createButton(text, onClick) {
    const button = document.createElement('button');
    button.textContent = text;
    button.addEventListener('click', onClick);
    return button;
}

async function onClick() {
    const pipWindow = await documentPictureInPicture.requestWindow({ width: 284, height: 284 });
    const main = pipWindow.document.createElement('div');
    const audioController = document.createElement("div");
    const nextSong = createButton('Next', () => {
        document.querySelector(NEXT).parentElement.click();
    });
    const previousSong = createButton('Previous', () => {
        document.querySelector(PREVIOUS).parentElement.click();
    });
    const playPause = createButton('', () => {
        const play = document.querySelector(PLAY);
        const pause = document.querySelector(PAUSE);
        if (play) {
            play.parentElement.click();
            playPause.textContent = 'Pause';
        } else {
            pause.parentElement.click();
            playPause.textContent = 'Play';
        }
    });
    const playMix = createButton('Launch Track mix', () => {
        const queueOpened = document.querySelector(QUEUELIST);
        if (!queueOpened) {
            document.querySelector(QUEUE).click();
        }
        setTimeout(() => {
            document.querySelector(`${QUEUELIST} [aria-selected=true] [aria-haspopup]`).click();
            document.querySelector(MIX).closest('button').click();
            if (!queueOpened) {
                document.querySelector(QUEUE).click();
            }
        }, CLICK_TIMEOUT);
    });
    const shuffle = createButton('Shuffle my music', () => {
        document.querySelector(FAVORITES).click();
        setTimeout(() => {
            document.querySelector(SHUFFLE).click();
        }, CLICK_TIMEOUT);
    });
    const toggleFav = createButton('', () => {
        const fav = document.querySelector(ADD_FAV);
        if (fav) {
            fav.parentElement.click();
            toggleFav.textContent = 'Remove from favorites';
        } else {
            document.querySelector(REMOVE_FAV).parentElement.click();
            toggleFav.textContent = 'Add to favorites';
        }
    });
    const songTitle = pipWindow.document.createElement('p');
    const style = pipWindow.document.createElement('style');
    main.classList.add('main');
    shuffle.classList.add('shuffle');
    const getText = () => {
        songTitle.textContent = getTitle();
        playPause.textContent = document.querySelector(PLAY) ? 'Play' : 'Pause';
        toggleFav.textContent = document.querySelector(REMOVE_FAV) ? 'Remove from favorites' : 'Add to favorites';
    }
    getText();
    audioController.appendChild(previousSong);
    audioController.appendChild(playPause);
    audioController.appendChild(nextSong);
    main.appendChild(audioController);
    main.appendChild(toggleFav);
    main.appendChild(playMix);
    main.appendChild(shuffle);
    main.appendChild(songTitle);
    pipWindow.document.body.appendChild(main);
    style.innerHTML = STYLE_TEXT;
    pipWindow.document.head.appendChild(style);
    const observer = new MutationObserver(() => getText());
    observer.observe(document.body, {
        childList: true,
        subtree: true,
    });
}

const observer = new MutationObserver((mutations) => {
    debounce(() => {
        observer.disconnect();
        const pipButton = document.createElement('button');
        pipButton.textContent = 'PiP';
        pipButton.addEventListener('click', onClick);
        pipButton.classList.add('svg-icon-group-btn');
        document.querySelector('.player-options > ul > li:nth-child(1) > ul').appendChild(pipButton);
    })();
});

window.onload = () => {
    observer.observe(document.body, {
        childList: true,
        subtree: true,
    });
};

QingJ © 2025

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