Skip to content

Instantly share code, notes, and snippets.

@Explosion-Scratch
Last active July 25, 2024 21:09
Show Gist options
  • Save Explosion-Scratch/bb7e6e9db855cd51bb860e132ee3cb05 to your computer and use it in GitHub Desktop.
Save Explosion-Scratch/bb7e6e9db855cd51bb860e132ee3cb05 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Get accent color
// @namespace mailto:[email protected]
// @version 2024-07-25
// @description try to take over the world!
// @author You
// @match https://open.spotify.com/*
// @require https://cdnjs.cloudflare.com/ajax/libs/vibrant.js/1.0.0/Vibrant.min.js
// @icon https://www.google.com/s2/favicons?sz=64&domain=spotify.com
// @grant none
// ==/UserScript==
let IMAGE_QUERY_SELECTORS = {
"spotify.com": "img[data-testid*=cover-art-image]",
};
// Can be one of:
// Vibrant
// Muted
// DarkVibrant
// DarkMuted
// LightVibrant
// LightMuted
const PALETTE_FUNC = "DarkVibrant";
(function () {
"use strict";
console.log("ACCENT COLOR RUNNING");
let site = Object.entries(IMAGE_QUERY_SELECTORS).find(i => location.host.includes(i[0]))
const QS = `img:hover${site ? ", " + site : ""}`
const getImage = () => document.querySelector(QS);
setInterval(() => color(getImage()), 500);
let bgEl;
function createEl() {
if (bgEl) {
return bgEl;
}
let e = document.createElement("div");
e.id = "current_album_accent";
Object.assign(e.style, {
position: "fixed",
top: 0,
right: 0,
width: "5px",
height: "5px",
display: "block",
});
document.body.appendChild(e);
bgEl = e;
return e;
}
const COLORS = {};
async function color(el) {
if (!el) {
return;
}
if (COLORS[el.src]) {
return COLORS[el.src];
}
let url = await fetch(el.src)
.then((r) => r.blob())
.then((b) => URL.createObjectURL(b));
let img = new Image();
img.src = url;
await new Promise((r) => (img.onload = r));
console.log("IMG loaded");
let out = new Vibrant(img).swatches();
window.COLOR = out;
console.log(out);
out = (out[PALETTE_FUNC] || out[Object.entries(out).find(i => i[1])[0]]).getHex();
createEl().style.backgroundColor = out;
URL.revokeObjectURL(url);
console.log("COLOR:", out);
COLORS[el.src] = out;
return out;
}
function getAverageRGB(imgEl) {
var blockSize = 5, // only visit every 5 pixels
defaultRGB = { r: 0, g: 0, b: 0 }, // for non-supporting envs
canvas = document.createElement("canvas"),
context = canvas.getContext && canvas.getContext("2d"),
data,
width,
height,
i = -4,
length,
rgb = { r: 0, g: 0, b: 0 },
count = 0;
if (!context) {
return defaultRGB;
}
height = canvas.height =
imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
width = canvas.width =
imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;
context.drawImage(imgEl, 0, 0);
try {
data = context.getImageData(0, 0, width, height);
} catch (e) {
/* security error, img on diff domain */
return defaultRGB;
}
length = data.data.length;
while ((i += blockSize * 4) < length) {
++count;
rgb.r += data.data[i];
rgb.g += data.data[i + 1];
rgb.b += data.data[i + 2];
}
// ~~ used to floor values
rgb.r = ~~(rgb.r / count);
rgb.g = ~~(rgb.g / count);
rgb.b = ~~(rgb.b / count);
return rgb;
}
})();
async function watch(fn, onchange = () => {}, int = 100) {
let r = await fn();
let i = setInterval(async () => {
let new_r = await fn();
if (new_r !== r) {
onchange(new_r, r);
}
r = new_r;
}, int);
return () => {
clearInterval(i);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment