您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为指定视频添加可移动的进度条、音量控制器、重新加载按钮和悬浮窗功能
当前为
// ==UserScript== // @name Video Control with Reload and Floating Window // @namespace http://tampermonkey.net/ // @version 1.3 // @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; let buttonCreated = false; let floatingWindow = null; 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> <button id="float-video-button" style="width: 100%; margin-top: 10px; padding: 5px; background-color: #2196F3; border: none; color: white; cursor: pointer;">创建悬浮视频窗口</button> <div style="display: flex; justify-content: space-between; margin-top: 10px;"> <button class="size-button" data-action="increase-width">宽度+</button> <button class="size-button" data-action="decrease-width">宽度-</button> <button class="size-button" data-action="increase-height">高度+</button> <button class="size-button" data-action="decrease-height">高度-</button> </div> `; document.body.appendChild(controller); return controller; } function createToggleButton() { if (!buttonCreated) { 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.onmousedown = function(e) { e.preventDefault(); isDragging = true; initialX = e.clientX; initialY = e.clientY; lastX = initialX; lastY = initialY; document.onmouseup = closeDragElement; document.onmousemove = throttle(buttonDrag, 16); }; function buttonDrag(e) { if (!isDragging) return; e.preventDefault(); pos1 = lastX - e.clientX; pos2 = lastY - e.clientY; lastX = e.clientX; lastY = e.clientY; button.style.top = (button.offsetTop - pos2) + "px"; button.style.left = (button.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; isDragging = false; } button.addEventListener('click', function() { if (controller.style.display === 'none') { controller.style.display = 'block'; } else { controller.style.display = 'none'; } }); let isClicking = false; button.addEventListener('touchstart', function(e) { e.preventDefault(); if (e.touches.length === 1) { isClicking = true; setTimeout(() => { if (isClicking) { if (controller.style.display === 'none' || controller.style.display === '') { controller.style.display = 'block'; } else { controller.style.display = 'none'; } } }, 200); } else { isClicking = false; } const touch = e.touches[0]; initialX = touch.clientX - button.offsetLeft; initialY = touch.clientY - button.offsetTop; isDragging = true; }); button.addEventListener('touchmove', function(e) { e.preventDefault(); if (!isDragging) return; isClicking = false; const touch = e.touches[0]; const newX = touch.clientX - initialX; const newY = touch.clientY - initialY; button.style.left = newX + 'px'; button.style.top = newY + 'px'; }); button.addEventListener('touchend', function() { isDragging = false; }); } buttonCreated = true; } function makeDraggable(element) { let pos1 = 0, pos2 = 0; element.onmousedown = dragMouseDown; function dragMouseDown(e) { if (e.target.id === 'progress-container' || e.target.id === 'progress-indicator' || e.target.id === 'volume-slider' || e.target.tagName === 'BUTTON') return; e.preventDefault(); isDragging = true; initialX = e.clientX; initialY = e.clientY; lastX = initialX; lastY = initialY; document.onmouseup = closeDragElement; document.onmousemove = throttle(elementDrag, 16); } 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.onmouseup = null; document.onmousemove = null; 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 createFloatingWindow() { if (floatingWindow) { floatingWindow.remove(); } floatingWindow = document.createElement('div'); floatingWindow.style.cssText = ` position: fixed; top: 50px; left: 50px; width: 320px; height: 240px; background-color: #000; border: 2px solid #fff; z-index: 10000; resize: both; overflow: hidden; `; const closeButton = document.createElement('button'); closeButton.textContent = 'X'; closeButton.style.cssText = ` position: absolute; top: 5px; right: 5px; background-color: red; color: white; border: none; cursor: pointer; z-index: 10001; `; closeButton.onclick = () => floatingWindow.remove(); floatingWindow.appendChild(closeButton); document.body.appendChild(floatingWindow); videoElement = document.querySelector('video[src^="blob:"]'); if (videoElement) { // 保存视频的原始尺寸 const originalWidth = videoElement.offsetWidth; const originalHeight = videoElement.offsetHeight; // 调整视频大小以适应悬浮窗 videoElement.style.width = '100%'; videoElement.style.height = '100%'; // 将视频添加到悬浮窗中 floatingWindow.appendChild(videoElement); // 添加悬浮窗到body document.body.appendChild(floatingWindow); makeDraggable(floatingWindow); } } 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'); const floatVideoButton = document.getElementById('float-video-button'); const sizeButtons = document.querySelectorAll('.size-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); }); floatVideoButton.addEventListener('click', createFloatingWindow); sizeButtons.forEach(button => { button.addEventListener('click', function() { if (!floatingWindow) return; const action = this.dataset.action; const step = 20; switch (action) { case 'increase-width': floatingWindow.style.width = (floatingWindow.offsetWidth + step) + 'px'; break; case 'decrease-width': floatingWindow.style.width = Math.max(160, floatingWindow.offsetWidth - step) + 'px'; break; case 'increase-height': floatingWindow.style.height = (floatingWindow.offsetHeight + step) + 'px'; break; case 'decrease-height': floatingWindow.style.height = Math.max(120, floatingWindow.offsetHeight - step) + 'px'; break; } }); }); 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(); })();