您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Displays the BPM of a song on YouTube using the Spotify API
当前为
// ==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或关注我们的公众号极客氢云获取最新地址