Displays the BPM of a song on YouTube using the Spotify API
当前为
// ==UserScript==
// @name YouTube BPM Display
// @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
// @homepageURL https://greasyfork.org/es/scripts/511311
// @namespace http://violentmonkey.net/
// ==/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();
});
});
})();