// ==UserScript==
// @name Media Player Enhancer
// @description Enhances the HTML5 video player with keyboard shortcuts and other features, and provides feedback on key presses.
// @match *://*/*
// @icon https://raw.githubusercontent.com/WhyWhatHow/powertoys4browser/master/icons/media_enhancer.ico
// @grant none
// @run-at window-load
// @namespace https://whywhathow.github.io/
// @homepage https://github.com/WhyWhatHow/powertoys4browser
// @supportURL https://github.com/WhyWhatHow/powertoys4browser/issues
// @version 1.9
// @author whywhathow
// @license MIT
// ==/UserScript==
// 快捷键消息提示框 元素ID.
const MSG_BOX_ID = 'reference';
// 避免 body.offsetHeight = 0 的情况.
let body_size;
// 初始化
function init() {
// 初始化消息提示框
initShortCutsBox();
// 初始化 body_size
initBodySize();
}
// 初始化 body_size
function initBodySize() {
body_size = {
width: document.body.offsetWidth,
height: document.body.offsetHeight
};
console.log("----------body_size----------")
console.log(body_size)
}
var container_default_size; // 即videoPlayer的 width, height
// 初始化快捷键信息提示框
function initShortCutsBox() {
const reference = document.createElement('div');
reference.id = MSG_BOX_ID;
reference.style.top = '35%';
reference.style.left = '15%';
reference.style.width = 'auto';
reference.style.height = 'auto';
reference.style.fontSize = 'large';
reference.style.margin = '10px';
reference.style.transform = 'translate(-50%, -50%)';
reference.style.background = 'rgba(33,33,33,.9)'
reference.style.color = '#fff';
reference.style.padding = '10px';
reference.style.zIndex = '9999';
reference.style.position = 'fixed';
reference.style.display = 'none';
reference.innerHTML = `
<h3>Video Player Shortcuts</h3>
<ul style="list-style-type: none; padding-left: 0;">
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">←</code> <span style="color: #999; margin-left: 10px;">Rewind 5 seconds</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">→</code> <span style="color: #999; margin-left: 10px;">Fast forward 5 seconds</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">↑</code> <span style="color: #999; margin-left: 10px;">Increase volume</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">↓</code> <span style="color: #999; margin-left: 10px;">Decrease volume</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">F</code> <span style="color: #999; margin-left: 10px;">Toggle fullscreen</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">M</code> <span style="color: #999; margin-left: 10px;">Toggle mute</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">[</code> <span style="color: #999; margin-left: 10px;">Slow down playback</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">]</code> <span style="color: #999; margin-left: 10px;">Speed up playback</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">R</code> <span style="color: #999; margin-left: 10px;">Reset player settings</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">Q</code> <span style="color: #999; margin-left: 10px;">Show shortcuts reference</span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">P</code> <span style="color: #999; margin-left: 10px;">Play or Pause Video </span></li>
<li style="display: flex; align-items: center;"><code style="font-size: 1.2em; font-weight: bold;">Esc</code> <span style="color: #999; margin-left: 10px;">Exit fullscreen</span></li>
</ul>
<button style="position: absolute; top: 5px; right: 5px; background: none; border: none; font-size: 20px; color: #fff; cursor: pointer;" onclick="this.parentNode.style.display = 'none'">×</button>
`;
document.body.appendChild(reference);
}
// 显示快捷键提示框
function showReference() {
let ele = document.getElementById(MSG_BOX_ID);
if (ele.style.display === 'none') {
ele.style.display = 'block';
} else {
ele.style.display = 'none';
}
}
/**
* 生成video标签的父标签
* @param videoElement video player tag
* @param feedbackElement some Information
* @returns videoContainer
*/
function createVideoParentElement(videoElement, feedbackElement) {
var videoContainer;
///////
videoContainer = document.createElement('div');
videoContainer.id = 'fun-video-container';
// videoContainer 样式设置
videoContainer.style.position = 'relative'; // 设置父节点 div 的定位方式
// videoContainer.style.position ='inherit';
videoContainer.style.width = videoElement.offsetWidth + 'px'; // 设置父节点 div 的宽度
videoContainer.style.height = videoElement.offsetHeight + 'px'; // 设置父节点 div 的高度
container_default_size = {
width: videoElement.offsetWidth,
height: videoElement.offsetHeight
};
// console.log("----------------------container_default_size--------------------")
// console.log(container_default_size)
// console.log(videoContainer)
if (videoElement.parentElement === document.body) {
document.body.appendChild(videoContainer);
} else {
// hint: 这个函数已经将videoContainer 加入到document 中, 不需要二次加入
videoElement.parentNode.insertBefore(videoContainer, videoElement); // 将父节点 div 插入到 video 的前面
}
// console.log(videoContainer)
videoContainer.appendChild(videoElement);
videoContainer.appendChild(feedbackElement); //
videoContainer.appendChild(document.getElementById(MSG_BOX_ID)); // 添加 快捷键消息提示框.
// console.log(videoElement.parentElement)
return videoContainer;
}
// 获取<video> 主元素
function initVideoPlayerDefault() {
// 获取HTML5视频播放器元素
let videoPlayer = document.querySelector('video');
// 如果没有找到视频播放器则退出
if (!videoPlayer) return;
console.log('----------------videoPlayer--------------')
console.log(videoPlayer)
videoPlayer.setAttribute('controls', true);
videoPlayer.constrolsList = 'nofullscreen';
videoPlayer.style.cssText = 'width:100%;height:100%;display:inline-block';
return videoPlayer;
}
//全屏观看
function enterFullScreen(videoContainer, videoPlayer, showFeedback) {
console.log("---------------enterFullScreen----------------------")
console.log(container_default_size)
videoContainer.style.width = body_size.width + 'px';
videoContainer.style.height = body_size.height + 'px';
addVideo(videoContainer,videoPlayer);
videoContainer.requestFullscreen();
console.log(videoContainer)
console.log("====================================================")
videoPlayer.play();
showFeedback('Fullscreen');
}
// 退出全屏
function exitFullScreen(videoContainer) {
if (document.fullscreenElement === videoContainer) {
console.log("----------------exit-fullScreen----------------------")
console.log(container_default_size)
videoContainer.style.width = container_default_size.width + 'px';
videoContainer.style.height = container_default_size.height + 'px';
console.log(videoContainer)
console.log("------------------------------------------------------")
document.exitFullscreen();
}
}
/**
* 将 video player 作为 videoContainer 的第一个子元素存在
* @param videoContainer
* @param videoPlayer
*/
function addVideo(videoContainer, videoPlayer) {
if (videoContainer && videoPlayer) {
videoContainer.insertBefore(videoPlayer, videoContainer.firstChild);
} else {
console.log(" video-container | video don't exist ")
}
}
/**
* 判断 videoPlayer.display === none , 如果是, 设置为inline-block
* @param videoPlayer
*/
function checkVideoPlayerDisplay(videoPlayer) {
if (videoPlayer.style.display === 'none') {
videoPlayer.style.display = 'inline-block';
}
return videoPlayer;
}
/**
* 静音切换
* @param videoPlayer
* @param showFeedback
*/
function toggleMute(videoPlayer, showFeedback) {
if (videoPlayer.muted) {
videoPlayer.muted = false;
showFeedback('Mute Off')
} else {
videoPlayer.muted = true;
showFeedback('Mute On')
}
}
/**
* 视频全屏, 不是 videoContainer 下的全屏 切换到 videoContainer,
* @param videoContainer
* @param videoPlayer
* @param showFeedback
*/
function toggleFullScreen(videoContainer, videoPlayer, showFeedback) {
addVideo(videoContainer, videoPlayer);
videoPlayer = checkVideoPlayerDisplay(videoPlayer);
if (document.fullscreenElement === videoContainer) {
console.log("*********************----------------------exit------------------------")
exitFullScreen(videoContainer)
} else {
console.log('--*************---------------F----not full screen --------------------')
if (document.fullscreenElement) {
document.exitFullscreen();
}
enterFullScreen(videoContainer, videoPlayer, showFeedback);
}
}
/// main function////
function main() {
'use strict';
// 初始化, 创建shortcutsBox & initBodySize
console.log("-----------------video Enhancer---------")
init()
// 创建 video标签 deepCopy
// var videoPlayer = initVideoPlayer(); //copy
let videoPlayer = initVideoPlayerDefault();
if (!videoPlayer) return;
// 创建一个用于显示反馈提示的<div>元素
let feedback = document.createElement('div');
feedback.style.cssText = 'position:fixed !important; z-index:2147483647 !important; isolation:isolate !important;top:10%;right:5%;transform:translate(-50%,-50%);background:#333;color:#fff;padding:10px;border-radius:5px;z-index:2147483647;font-size:16px;visibility:hidden;'
// document.body.appendChild(feedback);
// 为视频播放器创建父元素
let videoContainer = createVideoParentElement(videoPlayer, feedback);
// 全屏 变化 操作
document.addEventListener('fullscreenchange', function () {
// toggleFullScreen(videoContainer, videoPlayer, feedback)
addVideo(videoContainer, videoPlayer)
var fullscreenElement = document.fullscreenElement;
if (!fullscreenElement) { // 退出全屏
videoContainer.style.width = container_default_size.width + 'px';
videoContainer.style.height = container_default_size.height + 'px';
} else if (videoContainer === fullscreenElement) {
videoContainer.style.width = body_size.width + 'px';
videoContainer.style.height = body_size.height + 'px';
console.log("------------video-container fullscreen ----------------")
} else { // 进入全屏
console.log("-------------other div-------------------------------------")
videoContainer.style.position = 'static';
// enterFullScreen(videoContainer,videoPlayer,feedback)
}
});
handleShortCuts();
/////////////////////////////////// end //////////////////////////
function handleShortCuts() {
// 设置视频播放器的快捷键
document.addEventListener('keydown', function (event) {
// Add keyboard event listeners to the video element
const key = event.key;
console.log("----------------key: " + key + "-----------------")
switch (key) {
case 'Space':
// // console.log("----------------space---------------")
if (videoPlayer.paused) { // 如果视频已经暂停,则播放视频
videoPlayer.play();
} else { // 如果视频正在播放,则暂停视频
videoPlayer.pause();
}
break;
case 'ArrowLeft':
videoPlayer.currentTime -= 5;
showFeedback('← 5s');
break;
case 'ArrowRight':
videoPlayer.currentTime += 5;
showFeedback('→ 5s');
break;
case 'ArrowUp':
if (videoPlayer.volume < 1) {
videoPlayer.volume += 0.1;
showFeedback('↑ Volume');
}
break;
case 'ArrowDown':
if (videoPlayer.volume > 0) {
videoPlayer.volume -= 0.1;
showFeedback('↓ Volume');
}
break;
case 'f':
toggleFullScreen(videoContainer, videoPlayer, showFeedback);
break;
case 'm':
toggleMute(videoPlayer, showFeedback);
break;
case '[':
videoPlayer.playbackRate = Math.max(0.1, videoPlayer.playbackRate - 0.1);
showFeedback(`- ${videoPlayer.playbackRate.toFixed(1)}x`);
break;
case ']':
videoPlayer.playbackRate += 0.1;
showFeedback(`+ ${videoPlayer.playbackRate.toFixed(1)}x`);
break;
case 'r': // r 键,重置播放器设置
videoPlayer.volume = 1;
videoPlayer.playbackRate = 1;
showFeedback('Reset');
break;
case 'q': // 显示快捷键信息
showReference();
break;
case 'p':
if (videoPlayer.paused) { // 如果视频已经暂停,则播放视频
videoPlayer.play();
} else { // 如果视频正在播放,则暂停视频
videoPlayer.pause();
}
break;
default:
break;
}
});
}
// 显示按键反馈
function showFeedback(text) {
feedback.textContent = text;
feedback.style.visibility = 'visible';
setTimeout(function () {
feedback.style.visibility = 'hidden';
}, 1000);
}
}
window.addEventListener('load', main);