Deezer PiP - deezer.com

Adds PiP features to Deezer

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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,
    });
};