// ==UserScript==
// @name Weverse Video Rotation
// @description Adds buttons and keyboard shortcuts to rotate and flip the video on Weverse.io
// @version 1
// @author Slooshi
// @namespace Slooshi
// @match https://weverse.io/*
// @license MIT
// @grant GM_addStyle
// ==/UserScript==
GM_addStyle(`
#video-rotation-controls {
margin-left: 15px;
}
#video-rotation-controls button {
font-size: 20px;
background: #f5f5f5;
border: 1px solid #d9d9d9;
color: #414141;
line-height: 22px;
height: 24px;
width: 24px;
outline: 0;
}
#video-rotation-controls button:not(:last-child) {
margin-right: 3px;
}
.vwplayer_vlivelive .videoBox {
height: calc(100% - 4px);
width: calc(100% - 4px);
}
`);
let directions = {
left: 'r270',
up: 'r0',
right: 'r90',
down: 'r180',
flipH: 'flipH',
flipV: 'flipV',
}
let rotations = {
r90: 'rotate(90deg)',
r180: 'rotate(180deg)',
r270: 'rotate(270deg)',
};
let scaling = {
flipH: 'scaleX(-1)',
flipV: 'scaleY(-1)',
portrait: 'scale(1.77777)',
landscape: 'scale(0.5625)',
};
let attempts = 0;
let controlsID = 'video-rotation-controls';
let shortcutsEnabled = true;
let shortcuts = {
'Numpad8': directions.up,
'Numpad2': directions.down,
'Numpad4': directions.left,
'Numpad6': directions.right,
'Numpad9': directions.flipH,
'Numpad3': directions.flipV,
}
let stylesheet = null;
let timer = null;
(() => {
stylesheet = document.createElement('style');
document.head.appendChild(stylesheet);
let getAllSubsets = arr => arr.reduce((subsets, value) => subsets.concat(subsets.map(set => [...set, value])), [[]]).slice(1);
let scalings = getAllSubsets(Object.keys(scaling));
for (let rule in rotations) {
stylesheet.sheet.insertRule(`video.${rule} { transform: ${rotations[rule]}; }`, stylesheet.sheet.cssRules.length);
for (let scalingSet of scalings) {
stylesheet.sheet.insertRule(`video.${rule}.${scalingSet.reduce((s, v) => s+'.'+v)} { transform: ${rotations[rule] + scalingSet.reduce((s, v) => s+' '+scaling[v], '')}; }`, stylesheet.sheet.cssRules.length);
}
}
let flips = getAllSubsets([directions.flipH, directions.flipV]);
for (let flipSet of flips) {
stylesheet.sheet.insertRule(`video.${flipSet.reduce((s, v) => s+'.'+v)} { transform:${flipSet.reduce((s, v) => s+' '+scaling[v], '')}; }`, stylesheet.sheet.cssRules.length);
}
addButtons();
// Re-add buttons if page changes
window.history.pushState = function() {
History.prototype.pushState.apply(history, arguments);
attempts = 0;
setTimeout(addButtons, 2000);
}
})();
function addButtons() {
clearTimeout(timer);
let btnArea = document.querySelector('.TitleView_title__SSnHb');
if (!btnArea) {
if (++attempts < 10) {
timer = setTimeout(addButtons, 500);
}
return;
}
else if (document.getElementById(controlsID)) {
return;
}
let btnDirections = [
[directions.left, '🠘'],
[directions.up, '🠙'],
[directions.right, '🠚'],
[directions.down, '🠛'],
[directions.flipV, '🡙'],
[directions.flipH, '🡘'],
];
let div = document.createElement('div');
div.id = controlsID;
div.style.marginLeft = '15px';
div.style.display = 'inline-block';
for (let entry of btnDirections) {
let btn = document.createElement('button');
btn.dataset.direction = entry[0];
btn.textContent = entry[1];
btn.style.fontSize = '20px';
btn.style.background = '#f5f5f5';
btn.style.border = '1px solid #d9d9d9';
btn.style.color = '#414141';
btn.style.lineHeight = '22px';
btn.style.height = '24px';
btn.style.width = '24px';
btn.style.outline = '0';
btn.style.marginRight = '3px';
div.appendChild(btn);
}
btnArea.parentNode.insertBefore(div, btnArea.nextSibling);
}
function rotateVideo(direction) {
let video = document.querySelector('video.webplayer-internal-video');
if (!video) return;
let flip = [directions.flipH, directions.flipV].includes(direction);
if (flip) {
video.classList.toggle(direction);
}
else {
video.classList.remove(directions.left, directions.right, directions.down, 'portrait', 'landscape');
if (direction != directions.up) {
video.classList.add(direction);
if ([directions.left, directions.right].includes(direction)) {
video.classList.add(video.videoHeight > video.videoWidth ? 'portrait' : 'landscape');
}
}
}
}
window.addEventListener('click', function(e) {
if (e.target.parentNode.id != controlsID || e.target.tagName.toLowerCase() != 'button') {
return;
}
else if (e.target.dataset.direction) {
rotateVideo(e.target.dataset.direction);
}
}, true);
// Shortcuts
window.addEventListener('keydown', function(e) {
let stopPropagation = false;
if (shortcuts[e.code] && shortcutsEnabled) {
rotateVideo(shortcuts[e.code]);
stopPropagation = true;
}
else if (e.code == 'Pause') {
shortcutsEnabled = !shortcutsEnabled;
stopPropagation = true;
}
if (stopPropagation) {
e.preventDefault();
e.stopPropagation();
}
}, true);