Greasy Fork

Plex now playing badge

Display a badge on favicon with a number of users streaming from the server

目前为 2017-09-02 提交的版本。查看 最新版本

// ==UserScript==
// @name        Plex now playing badge
// @namespace   V@no
// @description Display a badge on favicon with a number of users streaming from the server
// @include     http://localhost:32400/web/*
// @include     http://127.0.0.1:32400/web/*
// @include     https://app.plex.tv/desktop
// @include     https://app.plex.tv/desktop/*
// @icon        data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACVklEQVRYhc2Xy08TURTGZ6NAn7RMp1xo587ChVUTY9mCCfFPMFZicGlMNNGli7YoIsYBNDyCDyKmQVGpQMJC0aVuTKCkoFCbEhQoCNrUkBDWn4uWR8s0tHVu603O+vzuPed857scpRTFDO6/Bxi4VIWAhyjGpJvglENkC1B3UsSEWxkg4CHwX6liC0Apxc2ztowAU02VcNXZ2QIcPUIxmfYKU16CYLMVM3cEfG6yQpIYN2Grqzrl1tO3rfh6V8CcbMG3Dh5yI2ELIEkUn24QBLwE082J5KE2HuEHFYh0mRHpNMN5LPeGzGkMz9XaEbxViS+tAuaSyee7zfj+sByLT4x457awBaCUYvgawew9C8L3d5Mv9RkRfWbAqk+PC2eq2QI4HSJmZR6RTjMWek1YTCb/OaDD+qAW4UeGnBoyLyWUGwnme0z48bgc0f5E8l8vtYj5NYiPlKHrssAWQJIogjKPpT4jVnx6rA9qERvS4M9oKTbGShAfLUXNiey0Ie9d0FBvw/LT5O1faREfLsPGWAk23x7G1vtD+NhhYAtAKcW4h8facx1+v9bgA8flFf8EUHNcRNSnR2yo2AD+XYBsjyoA414eay90KS9QMICGehui/YZ9PbB9OI5TDFUAJIki2FaRMgUFBZAvEiz0mlJ04KASqAbgdIgItfM7SricVMKCAYxcJwi1798FmQBULYGr1o6ZFqviNmQOsG1IMvkB5iXYsWQZHBFTgHRTmu4JJ1oEtmOoZMv3uuLzp23sAA76mLy5mnDDzKQ4269ZUZbR3ijKOlYD4C/uwVlNS+Cv+wAAAABJRU5ErkJggg==
// @author      V@no
// @version     2.0
// @grant       none
// ==/UserScript==

var prefs = {
	position: 2, //0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left
	offsetX: 0, //move badge away from x egde (use negative numbers for auto scale based on text size)
	offsetY: 0, //move badge away from y edge (use negative numbers for auto scale based on text size)
	textSize: 0, //text size, 0 = auto, 1 = 5px, 2 = 10px, so on
	textMargin: -1, //margin around text, use negative number for auto scale based on text size
	textColor: "#000000", // text color
	backgroundColor: "#FFFFFF", //background color
	borderColor: "#B90000", //border color
	borderWidth: -1, //border width, -1 = auto based on text size
	borderRadius: 0, //border corners radius 
	sizeIcon: 0, //image size in pixels, 0 = original
};

var links = document.getElementsByTagName("link"),
		link = null,
		head = document.getElementsByTagName('head')[0],
		prev = null,
		img = new Image(),
		canvas = document.createElement('canvas'),
		ctx = canvas.getContext('2d'),
		size = 16,
		multi;

for(let i = 0; i < links.length; i++)
{
	if (links[i].getAttribute("rel") == "shortcut icon")
	{
		link = links[i];
		break;
	}
}
if (!link)
{
	link = document.createElement('link');
	head.appendChild(link);
/*
//for debuging purposes
link = document.createElement('img');
document.body.appendChild(link);

let span = document.createElement("span");
var auto = false,add = 0;
span.className = "activity-badge badge badge-transparent";
span.innerText = "0";
span.style.display = "none";
document.body.appendChild(span);
link.addEventListener("click", function(e){if (e.button)return;add = 1;}, false);
link.addEventListener("dblclick", function(e){auto = !auto;}, false);
*/
	link.type = 'image/x-icon';
	link.rel = 'shortcut icon';
	link.href = "/web/favicon.ico";
}
img.setAttribute('crossOrigin','anonymous');
img.src = link.href;
img.onload = function()
{
	if (prefs.sizeIcon)
		img.width = img.height = prefs.sizeIcon;

	img.loaded = true;
	size = img.height;
	multi = prefs.textSize ? prefs.textSize : size / 16;
	canvas.width = canvas.height = size;
};

function drawText(text)
{
	if (!img.loaded)
		return;

	ctx.save();
	ctx.drawImage(img, 0, 0, size, size);

	if (text)
	{
		let textArray = text.toString().toUpperCase().split(''),
				textHeight = 0,
				textWidth = textArray.reduce(function (prev, cur)
				{
					let px = getPixelMap(cur);
					if (px.length * multi > textHeight)
						textHeight = px.length * multi;

					return prev + px[0].length * multi + 1;
				}, -1),
				borderWidth = prefs.borderWidth == -1 ? multi : prefs.borderWidth,
				textMargin = prefs.textMargin < 0 ? -prefs.textMargin * multi : prefs.textMargin,
				width = textWidth + textMargin * 2 + borderWidth,
				height = textHeight + textMargin * 2 + borderWidth,
				xy = -borderWidth / 2,
				offsetX = prefs.offsetX < 0 ? -prefs.offsetX * multi : prefs.offsetX,
				offsetY = prefs.offsetY < 0 ? -prefs.offsetY * multi : prefs.offsetY,
				x, y;

		switch (prefs.position)
		{
			case 0:
				x = borderWidth + offsetX;
				y = borderWidth + offsetY;
				break;
			case 1:
				x = size - width - offsetX;
				y = borderWidth + offsetY;
				break;
			default:
			case 2:
				x = size - width - offsetX;
				y = size - height - offsetY;
				break;
			case 3:
				x = borderWidth + offsetX;
				y = size - height - offsetX;
				break;
		}
		ctx.translate(x, y);

		// Draw Box
		ctx.fillStyle = prefs.backgroundColor;//backgborderRadius
		ctx.strokeStyle = prefs.borderColor;//border

		ctx.lineWidth = borderWidth;
		ctx.borderRadiusRect(xy, xy, width, height, prefs.borderRadius * multi).fill();
		ctx.borderRadiusRect(xy, xy, width, height, prefs.borderRadius * multi).stroke();

		// Draw Text
		ctx.fillStyle = prefs.textColor;
		ctx.translate(textMargin, textMargin);
		for(let i = 0; i < textArray.length; i++)
		{
			let px = getPixelMap(textArray[i]),
					_y = 0;

			for (let y = 0; y < px.length; y++)
			{
				let _x = 0;
				for (let x = 0; x < px[y].length; x++)
				{
					if (px[y] && px[y][x])
					{
						for(let mx = 0; mx < multi; mx++)
						{
							for(let my = 0; my < multi; my++)
							{
								ctx.fillRect(_x + mx, _y + my, 1, 1);
							}
						}
					}
					_x += multi;
				}
				_y += multi;
			}
			ctx.translate(px[0].length * multi + 1, 0);
		}
	}
	let data = canvas.toDataURL("image/x-icon");
	ctx.restore();
	ctx.clearRect(0, 0, size, size);
	return data;
}
//borrowed from https://chrome.google.com/webstore/detail/favicon-badges/fjnaohmeicdkcipkhddeaibfhmbobbfm/related?hl=en-US
/**
 * Gets a character's pixel map
 */
function getPixelMap(sym)
{
	let px = PIXELMAPS[sym];
	if (!px)
		px = PIXELMAPS['0'];
	return px;
}
var PIXELMAPS = {
	'0': [
		[1,1,1],
		[1,0,1],
		[1,0,1],
		[1,0,1],
		[1,1,1]
	],
	'1': [
		[0,1,0],
		[1,1,0],
		[0,1,0],
		[0,1,0],
		[1,1,1]
	],
	'2': [
		[1,1,1],
		[0,0,1],
		[1,1,1],
		[1,0,0],
		[1,1,1]
	],
	'3': [
		[1,1,1],
		[0,0,1],
		[0,1,1],
		[0,0,1],
		[1,1,1]
	],
	'4': [
		[1,0,1],
		[1,0,1],
		[1,0,1],
		[1,1,1],
		[0,0,1]
	],
	'5': [
		[1,1,1],
		[1,0,0],
		[1,1,1],
		[0,0,1],
		[1,1,1]
	],
	'6': [
		[1,1,1],
		[1,0,0],
		[1,1,1],
		[1,0,1],
		[1,1,1]
	],
	'7': [
		[1,1,1],
		[0,0,1],
		[0,0,1],
		[0,1,0],
		[0,1,0]
	],
	'8': [
		[1,1,1],
		[1,0,1],
		[1,1,1],
		[1,0,1],
		[1,1,1]
	],
	'9': [
		[1,1,1],
		[1,0,1],
		[1,1,1],
		[0,0,1],
		[1,1,1]
	],
};

//https://stackoverflow.com/a/7838871/2930038
CanvasRenderingContext2D.prototype.borderRadiusRect = function (x, y, w, h, r)
{
	if (w < 2 * r) r = w / 2;
	if (h < 2 * r) r = h / 2;
	this.beginPath();
	this.moveTo(x+r, y);
	this.arcTo(x+w,  y,   x+w, y+h, r);
	this.arcTo(x+w,  y+h, x,   y+h, r);
	this.arcTo(x,    y+h, x,   y,   r);
	this.arcTo(x,    y,   x+w, y,   r);
	this.closePath();
	return this;
};

(function loop()
{
	let data,
			_badge = document.getElementsByClassName("activity-badge badge badge-transparent"),
			badge = _badge.length ? _badge[0].innerText : "";

//try{if(add)_badge[0].innerText++; if(!auto) add = 0}catch(e){};

	let text = parseInt(badge);

	if (isNaN(text))
		text = 0;

	if (prev != text && (data = drawText(text)))
	{

//link.src = data;
		link.href = data;
		prev = text;
	}
	setTimeout(loop, 1000);
})();