YouTube BPM Display

Displays the BPM of a song on YouTube using the Spotify API

目前为 2024-10-03 提交的版本。查看 最新版本

// ==UserScript==
// @name         YouTube BPM Display
// @namespace    http://violentmonkey.net/
// @version      1.0 
// @description  Displays the BPM of a song on YouTube using the Spotify API
// @author       Sergi0
// @match        https://www.youtube.com/*
// @grant        GM_xmlhttpRequest
// @connect      api.spotify.com
// @icon         https://www.freeiconspng.com/uploads/youtube-icon-app-logo-png-9.png
// @license MIT

// ==/UserScript==

(function() {
    'use strict';

    // 1. Spotify API credentials
    const CLIENT_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
    const CLIENT_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
    let accessToken = '';
    let lastTitle = '';
    let lastUrl = '';

    // 2. Function to get the access token from Spotify
    function getAccessToken(callback) {
        const authString = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`);
        GM_xmlhttpRequest({
            method: 'POST',
            url: 'https://accounts.spotify.com/api/token',
            headers: {
                'Authorization': `Basic ${authString}`,
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            data: 'grant_type=client_credentials',
            onload: function(response) {
                if (response.status === 200) {
                    accessToken = JSON.parse(response.responseText).access_token;
                    callback();
                }
            }
        });
    }

    // 3. Function to search for the track on Spotify
    function searchTrack(trackName, callback) {
        const query = encodeURIComponent(trackName);
        const apiUrl = `https://api.spotify.com/v1/search?q=${query}&type=track&limit=1`;

        GM_xmlhttpRequest({
            method: 'GET',
            url: apiUrl,
            headers: {
                'Authorization': `Bearer ${accessToken}`
            },
            onload: function(response) {
                if (response.status === 200) {
                    const data = JSON.parse(response.responseText);
                    if (data.tracks.items.length > 0) {
                        const track = data.tracks.items[0];
                        callback(track.id, track.artists[0].name, track.name);
                    } else {
                        callback(null, null, null);
                    }
                } else {
                    callback(null, null, null);
                }
            }
        });
    }

    // 4. Function to get audio features (including BPM)
    function getTrackFeatures(trackId, callback) {
        const apiUrl = `https://api.spotify.com/v1/audio-features/${trackId}`;

        GM_xmlhttpRequest({
            method: 'GET',
            url: apiUrl,
            headers: {
                'Authorization': `Bearer ${accessToken}`
            },
            onload: function(response) {
                if (response.status === 200) {
                    const data = JSON.parse(response.responseText);
                    callback(data.tempo);
                } else {
                    callback(null);
                }
            }
        });
    }

    // 5. Function to display BPM and song information
    function displayBPM(bpm, artist, title) {
        let existingSongInfoElement = document.getElementById('song_info');
        if (existingSongInfoElement) {
            existingSongInfoElement.remove();
        }

        let songInfoElement = document.createElement('div');
        songInfoElement.id = 'song_info';
        songInfoElement.style.position = 'absolute';
        songInfoElement.style.top = '10px';
        songInfoElement.style.right = '10px';
        songInfoElement.style.backgroundColor = '#ff0000';
        songInfoElement.style.color = 'white';
        songInfoElement.style.padding = '15px';
        songInfoElement.style.zIndex = '9999';
        songInfoElement.style.borderRadius = '5px';
        songInfoElement.style.fontSize = '60px';
        songInfoElement.style.fontWeight = 'bold';
        songInfoElement.style.textAlign = 'center';

        let bpmElement = document.createElement('div');
        bpmElement.id = 'bpm';
        bpmElement.innerText = `BPM: ${bpm ? bpm.toFixed(2) : 'Unavailable'}`;

        let artistSongElement = document.createElement('div');
        artistSongElement.id = 'artist_song';
        artistSongElement.style.fontSize = '24px';
        artistSongElement.innerText = `${artist ? artist : 'Artist unavailable'} / ${title ? title : 'Title unavailable'}`;

        songInfoElement.appendChild(bpmElement);
        songInfoElement.appendChild(artistSongElement);

        const playerElement = document.querySelector('#player');
        if (playerElement) {
            playerElement.appendChild(songInfoElement);
        }
    }

    // 6. Main function
    function main() {
        const videoTitleElement = document.querySelector('#title h1');
        if (videoTitleElement) {
            const titleText = videoTitleElement.innerText.trim();

            if (titleText !== lastTitle) {
                lastTitle = titleText;

                searchTrack(titleText, function(trackId, artist, title) {
                    if (trackId) {
                        getTrackFeatures(trackId, function(bpm) {
                            displayBPM(bpm, artist, title);
                        });
                    } else {
                        displayBPM(null, null, null);
                    }
                });
            }
        }
    }

    // 7. Function to wait for the title indefinitely
    function waitForTitle() {
        const interval = setInterval(() => {
            const videoTitleElement = document.querySelector('#title h1');
            if (videoTitleElement) {
                clearInterval(interval);
                main();
                setInterval(main, 2000); // Check every 2 seconds
            }
        }, 1000); // Check every second
    }

    // 8. Observe URL changes
    function observeUrlChanges() {
        const urlObserver = new MutationObserver(() => {
            if (location.href !== lastUrl) {
                lastUrl = location.href;
                waitForTitle();
            }
        });

        urlObserver.observe(document.body, { childList: true, subtree: true });
    }

    // 9. Load the script
    window.addEventListener('load', () => {
        getAccessToken(function() {
            observeUrlChanges();
            waitForTitle();
        });
    });

})();

QingJ © 2025

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