Greasy Fork

YouTube - No more Letterboxing

Middleclick a YT video to crop black borders and fill the viewport (without loosing content)

// ==UserScript==
// @name        YouTube - No more Letterboxing
// @description Middleclick a YT video to crop black borders and fill the viewport (without loosing content)
// @namespace   ceremony.no.more.letterboxing
// @include     /https?://(.*\.)?youtube\..+/.*/
// @require     https://unpkg.com/[email protected]/underscore.js
// @require     https://unpkg.com/[email protected]/detect-element-resize.js
// @version     0.0.2
// @grant       none
// ==/UserScript==

let contrast = 10, pixelsteps = 64, handled = [], active = [];
setInterval(function () {
	Array.prototype.forEach.call(document.querySelectorAll(".html5-video-player video"), handleVideo)
}, 1000);
Array.prototype.forEach.call(document.querySelectorAll(".html5-video-player video"), handleVideo)

function handleVideo(video) {
	if (handled.indexOf(video) != -1)
		return;
	handled.push(video);
	let resizeEvent = _.throttle(function () {
			centerVideo(video, video.offset);
			scaleVideo(video, video.offset);
		}, 16, {
			leading: false
		});
	video.addEventListener('mousedown', function (e) {
		if (e.buttons !== 4)
			return;
		e.preventDefault();
		if (active.indexOf(video) == -1) {
			active.push(video);
			video.offset = getBlackBars(video);
			let hook = video;
			for (let i = 0; i < 4; i++) {
				addResizeListener(hook, resizeEvent);
				hook = hook.parentElement;
			}
		} else {
			active.splice(active.indexOf(video), 1);
			video.style.transform = "";
			let hook = video;
			for (let i = 0; i < 4; i++) {
				removeResizeListener(hook, resizeEvent);
				hook = hook.parentElement;
			}
		}
	})
}

function centerVideo(video, offset) {
	let style = video.style.transform.replace(/ ?translate\([^)]+\) ?/i, ""),
	centerx = (offset.right - offset.left) / video.videoWidth * 50,
	centery = (offset.bottom - offset.top) / video.videoHeight * 50;
	style += " translate(" + (Math.abs(centerx) > 0.01 ? centerx : 0) + "%," + (Math.abs(centery) > 0.01 ? centery : 0) + "%)";
	video.style.transform = style;
}

function scaleVideo(video, offset) {
	let style = video.style.transform.replace(/ ?scale\([^)]+\) ?/i, ""),
	container = video.parentElement.parentElement,
	zoom = Math.min(container.clientWidth / (video.videoWidth - offset.left - offset.right), container.clientHeight / (video.videoHeight - offset.top - offset.bottom)) / video.clientWidth * video.videoWidth;
	if (zoom > 1)
		style = "scale(" + zoom + ") " + style;
	video.style.transform = style;
}

function getBlackBars(video) {
	let w = video.videoWidth,
			h = video.videoHeight,
			canvas = document.createElement('canvas'),
			ctx = canvas.getContext('2d'),a
			offset = {
				top: 0,
				bottom: 0,
				left: 0,
				right: 0
			};
	canvas.width = w;
	canvas.height = h;
	ctx.drawImage(video, 0, 0, w, h);
	
	let imagedata = ctx.getImageData(0, 0, w, h).data;
  
	for (let i = 0; i < h / 2; i++) {
		let pixels = Array(Math.floor(pixelsteps*1.5)).fill(w * i).map((x, y) => 4 * Math.floor(x + w / 5 + w / 5 * 3 / (pixelsteps*1.5 + 1) * (y + 1)));
    if (pixelsAnalyzer(pixels, imagedata))
			break;
		offset.top++
	}
	
	for (let i = h; i > h / 2; i--) {
		let pixels = Array(Math.floor(pixelsteps*1.5)).fill(w * i).map((x, y) => 4 * Math.floor(x - w / 5 - w / 5 * 3 / (pixelsteps*1.5 + 1) * (y + 1)));
		if (pixelsAnalyzer(pixels, imagedata))
			break;
		offset.bottom++;
	}
	
	for (let i = 0; i < w / 2; i++) {
		let pixels = Array(pixelsteps).fill(w).map((x, y) => 4 * (x * Math.floor(h / 5 + h / 5 * 3 / (pixelsteps + 1) * (y + 1)) + i));
		if (pixelsAnalyzer(pixels, imagedata))
			break;
		offset.left++;
	}
	
	for (let i = w - 1; i > w / 2; i--) {
		let pixels = Array(pixelsteps).fill(w).map((x, y) => 4 * (x * Math.floor(h / 5 + h / 5 * 3 / (pixelsteps + 1) * (y + 1)) + i));
		if (pixelsAnalyzer(pixels, imagedata))
			break;
		offset.right++;
	}
	return offset;
}

function pixelsAnalyzer(pixels, imagedata) {
	return !pixels.every(px => Math.max.apply(null, [0, 1, 2].map(i => imagedata[px + i])) < contrast) ;
}