您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds buttons and keyboard shortcuts to rotate and flip the video.
- // ==UserScript==
- // @name V LIVE Video Rotation
- // @description Adds buttons and keyboard shortcuts to rotate and flip the video.
- // @version 1.10
- // @author aqmx
- // @namespace aqmx
- // @license MIT
- // @match https://www.vlive.tv/*
- // @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;
- }
- #playerBoxArea > .u_rmcplayer:fullscreen div[id*=".videoArea"],
- #playerBoxArea > .vwplayer_vlivelive:fullscreen .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 brightnessPercent = 100;
- let brightness = {
- up: 5,
- down: -5,
- reset: 0,
- }*/
- 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('[class^="video_content"] [class^="video_detail"] [class^="post_detail_reaction_info"]');
- 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 btnBrightness = [
- ['up', '+'],
- ['down', '−'],
- ['reset', '⟳'],
- ];*/
- let div = document.createElement('div');
- div.id = controlsID;
- for (let entry of btnDirections) {
- let btn = document.createElement('button');
- btn.dataset.direction = entry[0];
- btn.textContent = entry[1];
- div.appendChild(btn);
- }
- /*for (let entry of btnBrightness) {
- let btn = document.createElement('button');
- btn.dataset.brightness = entry[0];
- btn.textContent = entry[1];
- div.appendChild(btn);
- }*/
- btnArea.style.justifyContent = 'initial';
- btnArea.appendChild(div);
- }
- function rotateVideo(direction) {
- let video = document.querySelector('.u_rmcplayer_video video') || document.querySelector('.vwplayer_vlivelive .videoBox 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');
- }
- }
- }
- }
- /*function changeBrightness(value) {
- let video = document.querySelector('.u_rmcplayer_video video') || document.querySelector('.vwplayer_vlivelive .videoBox video');
- if (!video) return;
- if (brightness[value]) {
- brightnessPercent += brightness[value];
- video.style.filter = 'brightness('+brightnessPercent+'%)';
- }
- else {
- brightnessPercent = 100;
- video.style.filter = null;
- }
- }*/
- 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);
- }
- /*else if (e.target.dataset.brightness) {
- changeBrightness(e.target.dataset.brightness);
- }*/
- }, 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);