// ==UserScript==
// @name Video Control with Reload
// @namespace http://tampermonkey.net/
// @version 0.9
// @description 为指定视频添加可移动的进度条、音量控制器和重新加载按钮
// @match https://app.kosmi.io/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let videoElement = null;
let controller = null;
let isDragging = false; // 是否正在拖动控制器
let initialX = 0, initialY = 0; // 初始点击位置
let lastX = 0, lastY = 0; // 上一次位置
// 创建控制器
function createController() {
controller = document.createElement('div');
controller.id = 'video-controller';
controller.style.cssText = `
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 10px;
border-radius: 5px;
z-index: 9999;
cursor: move;
user-select: none;
width: 300px;
transition: width 0.3s, height 0.3s;
display: none; /* 初始隐藏 */
`;
controller.innerHTML = `
<div id="progress-container" style="width: 100%; height: 10px; background-color: #444; position: relative; cursor: pointer;">
<div id="progress-indicator" style="width: 0%; height: 100%; background-color: #fff; position: absolute;"></div>
</div>
<div id="time-display" style="text-align: center; margin-top: 5px;">0:00 / 0:00</div>
<div id="volume-container" style="display: flex; align-items: center; margin-top: 10px;">
<span id="volume-icon" style="margin-right: 10px;">🔊</span>
<input type="range" id="volume-slider" min="0" max="1" step="0.1" value="1" style="flex-grow: 1;">
</div>
<button id="reload-button" style="width: 100%; margin-top: 10px; padding: 5px; background-color: #4CAF50; border: none; color: white; cursor: pointer;">重新加载视频控制</button>
`;
document.body.appendChild(controller);
return controller;
}
// 创建显示/隐藏按钮
function createToggleButton() {
const button = document.createElement('button');
button.textContent = '🎥'; // 修改按钮图标
button.style.cssText = `
position: fixed;
left: 10px;
top: 50%;
transform: translateY(-50%);
padding: 5px;
font-size: 20px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
border-radius: 50%;
cursor: move;
z-index: 9999;
`;
document.body.appendChild(button);
// 使按钮可拖动
let pos1 = 0, pos2 = 0;
button.addEventListener('touchstart', touchStart, false);
function touchStart(e) {
isDragging = true;
initialX = e.touches[0].clientX;
initialY = e.touches[0].clientY;
lastX = initialX;
lastY = initialY;
document.addEventListener('touchmove', touchMove, false);
document.addEventListener('touchend', touchEnd, false);
}
function touchMove(e) {
if (!isDragging) return;
e.preventDefault();
let currentX = e.touches[0].clientX;
let currentY = e.touches[0].clientY;
pos1 = lastX - currentX;
pos2 = lastY - currentY;
lastX = currentX;
lastY = currentY;
button.style.top = (button.offsetTop - pos2) + "px";
button.style.left = (button.offsetLeft - pos1) + "px";
}
function touchEnd() {
isDragging = false;
document.removeEventListener('touchmove', touchMove, false);
document.removeEventListener('touchend', touchEnd, false);
}
button.addEventListener('click', function() {
if (controller.style.display === 'none') {
controller.style.display = 'block';
} else {
controller.style.display = 'none';
}
});
}
// 使控制器可拖动
function makeDraggable(element) {
let pos1 = 0, pos2 = 0;
element.addEventListener('mousedown', dragMouseDown, false);
function dragMouseDown(e) {
if (e.target.id === 'progress-container' || e.target.id === 'progress-indicator' || e.target.id === 'volume-slider' || e.target.id === 'reload-button') return;
e.preventDefault();
isDragging = true;
initialX = e.clientX;
initialY = e.clientY;
lastX = initialX;
lastY = initialY;
document.addEventListener('mouseup', closeDragElement, false);
document.addEventListener('mousemove', throttle(elementDrag, 16), false); // 节流控制拖动频率
}
function elementDrag(e) {
if (!isDragging) return;
e.preventDefault();
pos1 = initialX - e.clientX;
pos2 = initialY - e.clientY;
initialX = e.clientX;
initialY = e.clientY;
requestAnimationFrame(() => {
element.style.top = (element.offsetTop - pos2) + "px";
element.style.left = (element.offsetLeft - pos1) + "px";
element.style.bottom = 'auto';
});
}
function closeDragElement() {
document.removeEventListener('mouseup', closeDragElement, false);
document.removeEventListener('mousemove', throttle(elementDrag, 16), false);
isDragging = false;
}
}
// 节流函数,用于限制函数调用频率
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function() {
const context = this;
const args = arguments;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function() {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
// 格式化时间
function formatTime(seconds) {
const minutes = Math.floor(seconds / 60);
seconds = Math.floor(seconds % 60);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
// 主函数
function main() {
// 先移除已存在的控制器元素和按钮
const existingController = document.getElementById('video-controller');
const existingButton = document.getElementById('toggle-button');
if (existingController) {
existingController.remove();
}
if (existingButton) {
existingButton.remove();
}
videoElement = document.querySelector('video[src^="blob:https://app.kosmi.io/"]');
if (!videoElement) {
console.log('未找到指定视频');
return;
}
controller = createController();
makeDraggable(controller);
createToggleButton();
const progressContainer = document.getElementById('progress-container');
const progressIndicator = document.getElementById('progress-indicator');
const timeDisplay = document.getElementById('time-display');
const volumeSlider = document.getElementById('volume-slider');
const volumeIcon = document.getElementById('volume-icon');
const reloadButton = document.getElementById('reload-button');
// 更新进度
function updateProgress() {
const progress = (videoElement.currentTime / videoElement.duration) * 100;
progressIndicator.style.width = `${progress}%`;
const current = formatTime(videoElement.currentTime);
const total = formatTime(videoElement.duration);
timeDisplay.textContent = `${current} / ${total}`;
}
// 点击进度条更新视频位置
progressContainer.addEventListener('click', function(e) {
if (isDragging) return;
const rect = progressContainer.getBoundingClientRect();
const pos = (e.clientX - rect.left) / rect.width;
videoElement.currentTime = pos * videoElement.duration;
});
// 更新音量
volumeSlider.addEventListener('input', function() {
videoElement.volume = this.value;
updateVolumeIcon(this.value);
});
// 更新音量图标
function updateVolumeIcon(volume) {
if (volume > 0.5) {
volumeIcon.textContent = '🔊';
} else if (volume > 0) {
volumeIcon.textContent = '🔉';
} else {
volumeIcon.textContent = '🔇';
}
}
// 初始化音量
volumeSlider.value = videoElement.volume;
updateVolumeIcon(videoElement.volume);
// 重新加载按钮事件
reloadButton.addEventListener('click', function() {
// 移除旧的事件监听器
videoElement.removeEventListener('timeupdate', updateProgress);
videoElement.removeEventListener('loadedmetadata', updateProgress);
// 重新执行主函数
main();
// 重新绑定事件监听器
videoElement.addEventListener('timeupdate', updateProgress);
videoElement.addEventListener('loadedmetadata', updateProgress);
});
videoElement.addEventListener('timeupdate', updateProgress);
videoElement.addEventListener('loadedmetadata', updateProgress);
}
// 等待视频加载完成
function waitForVideo() {
const video = document.querySelector('video[src^="blob:https://app.kosmi.io/"]');
if (video) {
main();
} else {
setTimeout(waitForVideo, 1000);
}
}
waitForVideo();
})();