您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a volume bar to Bandcamp, styled with the album page's accent color, auto-inverts icons on dark backgrounds.
// ==UserScript== // @name Bandcamp Volume Bar 2.0 // @version 3.0 // @description Adds a volume bar to Bandcamp, styled with the album page's accent color, auto-inverts icons on dark backgrounds. // @author @nj4442 // @match *://*.bandcamp.com/* // @exclude *://bandcamp.com/ // @license MIT // @grant GM_addStyle // @require http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.js // @namespace https://greasyfork.org/en/users/1490367-nj4442 // ==/UserScript== (function () { 'use strict'; /* ------------------ COLOR HELPERS ------------------ */ function rgbToHex(r, g, b) { return "#" + ((1 << 24) + (r << 16) + (g << 8) + b) .toString(16) .slice(1); } function isDarkColor(hex) { if (hex.length === 4) { hex = "#" + [...hex.slice(1)].map(c => c + c).join(""); } const r = parseInt(hex.substr(1, 2), 16); const g = parseInt(hex.substr(3, 2), 16); const b = parseInt(hex.substr(5, 2), 16); const brightness = (r * 299 + g * 587 + b * 114) / 1000; return brightness < 128; } function getElementBgColor(el) { if (!el) return '#ffffff'; const bg = getComputedStyle(el).backgroundColor; if (bg.startsWith('rgb')) { const rgb = bg.match(/\d+/g); return rgbToHex(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2])); } return bg; } /* ------------------ ACCENT COLOR ------------------ */ function getAccentColor() { const accentElem = document.querySelector('.primaryText, .compound-button'); if (accentElem) { const color = getComputedStyle(accentElem).color || ''; if (color.startsWith('rgb')) { const rgb = color.match(/\d+/g); if (rgb && rgb.length >= 3) { return rgbToHex(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2])); } } return color; } return '#f2a6ea'; // fallback } /* ------------------ INITIAL SETUP ------------------ */ const accentColor = getAccentColor(); let percentage = 75; let muted = false; const speakerUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/2/21/Speaker_Icon.svg/250px-Speaker_Icon.svg.png"; const muteUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Mute_Icon.svg/250px-Mute_Icon.svg.png"; const pgBdBgColor = getElementBgColor(document.querySelector('#pgBd')); const darkBg = isDarkColor(pgBdBgColor); /* ------------------ DOM INSERTION ------------------ */ $("audio").attr("id", "audioSource"); const $control = $("<div class='volumeControl'></div>").insertAfter(".inline_player"); $control.append("<div class='speaker'></div>"); $control.append("<div class='volume'><span class='volumeInner'></span></div>"); /* ------------------ STYLING ------------------ */ let css = ` .volumeControl { margin-bottom: 10px; display: flex; align-items: center; gap: 10px; } .speaker { width: 30px; height: 30px; background: url('${speakerUrl}') center/contain no-repeat; cursor: pointer; ${darkBg ? 'filter: invert(1);' : ''} } .volume { position: relative; flex: 1; height: 10px; background-color: rgba(0, 0, 0, 0.1); border-radius: 5px; overflow: hidden; cursor: pointer; } .volumeInner { position: absolute; top: 0; left: 0; height: 100%; width: ${percentage}%; background-color: ${accentColor}; border-radius: 5px; } `; GM_addStyle(css); /* ------------------ VOLUME LOGIC ------------------ */ $("#audioSource")[0].volume = percentage / 100; function changeVolume(e) { const offset = $(".volume").offset().left; const width = $(".volume").width(); let pos = (e.pageX - offset) / width; pos = Math.max(0, Math.min(1, pos)); percentage = Math.floor(pos * 100); $(".volumeInner").css("width", `${percentage}%`); $("#audioSource")[0].volume = pos; if (muted) toggleMute(); // unmute if volume changed } function toggleMute() { muted = !muted; if (muted) { $(".speaker").css("background-image", `url('${muteUrl}')`); $("#audioSource")[0].volume = 0; $(".volumeInner").css("width", "0%"); } else { $(".speaker").css("background-image", `url('${speakerUrl}')`); $("#audioSource")[0].volume = percentage / 100; $(".volumeInner").css("width", `${percentage}%`); } } /* ------------------ EVENT HANDLERS ------------------ */ $(".volume").mousedown(e => { changeVolume(e); $("body").on("mousemove.volume", changeVolume); $("body").css("user-select", "none"); }); $(document).mouseup(() => { $("body").off("mousemove.volume"); $("body").css("user-select", "auto"); }); $(".speaker").click(toggleMute); })();