// ==UserScript==
// @name 新盟网页助手
// @name:en XinMeng Web Assistant
// @namespace http://tampermonkey.net/
// @version 1.3.0
// @description 新盟网页助手 - 视频播放速度控制、视频下载、解除复制限制等功能
// @description:en XinMeng Web Assistant - Video speed control, video download, and copy restriction removal
// @author fengliunian
// @license MIT
// @match *://*/*
// @grant GM_xmlhttpRequest
// @grant GM_notification
// @grant GM_download
// @connect *
// @homepage https://github.com/fengliunian/xinmeng-Web-Assistant
// @supportURL https://github.com/fengliunian/xinmeng-Web-Assistant/issues
// ==/UserScript==
(function() {
'use strict';
// 定义默认快捷键
const DEFAULT_HOTKEYS = {
inputText: { key: 'm', ctrl: true, shift: false, alt: true },
};
// 清除之前的设置并使用新的默认值
localStorage.removeItem('videoSpeedHotkeys');
let hotkeys = DEFAULT_HOTKEYS;
// 创建一个全局变量来存储按钮引用
let inputTextButton = null;
// 添加全局快捷键监听
document.addEventListener('keydown', function(e) {
// 如果焦点在设置窗口的输入框,不触发快捷键
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
const currentHotkey = hotkeys.inputText;
if (e.ctrlKey === currentHotkey.ctrl &&
e.shiftKey === currentHotkey.shift &&
e.altKey === currentHotkey.alt &&
e.key.toLowerCase() === currentHotkey.key) {
e.preventDefault();
// 使用全局变量引用按钮
if (inputTextButton) {
inputTextButton.click();
console.log('Triggered input text button');
} else {
console.log('Button not found');
}
}
});
// 在创建输入文字按钮的地修改代码
const disablePasteButton = document.createElement('button');
disablePasteButton.id = 'input-text-button';
disablePasteButton.textContent = '输入文字';
disablePasteButton.style.cssText = `
flex: 1;
padding: 6px;
border: none;
border-radius: 4px;
background: #9C27B0;
color: white;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
`;
// 保存按钮引用到全局变量
inputTextButton = disablePasteButton;
// 创建速度显示器
const speedDisplay = document.createElement('div');
speedDisplay.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.7);
color: white;
padding: 10px;
border-radius: 5px;
z-index: 9999;
display: none;
font-family: Arial, sans-serif;
`;
document.body.appendChild(speedDisplay);
// 显示速度的函数
function showSpeed(speed) {
speedDisplay.textContent = `速度: ${speed.toFixed(2)}x`;
speedDisplay.style.display = 'block';
setTimeout(() => {
speedDisplay.style.display = 'none';
}, 1000);
}
// 更改视频速度的函数
function changeVideoSpeed(delta) {
const videos = document.getElementsByTagName('video');
for (const video of videos) {
let newSpeed = video.playbackRate + delta;
// 限制速度范围在0.1到16之间
newSpeed = Math.max(0.1, Math.min(16, newSpeed));
video.playbackRate = newSpeed;
showSpeed(newSpeed);
}
}
// 监听键盘事
document.addEventListener('keydown', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
switch (e.key) {
case '[': // 减速
changeVideoSpeed(-0.25);
break;
case ']': // 加速
changeVideoSpeed(0.25);
break;
case '\\': // 重置速度
const videos = document.getElementsByTagName('video');
for (const video of videos) {
video.playbackRate = 1.0;
showSpeed(1.0);
}
break;
}
});
// 监听新加载的视频
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeName === 'VIDEO') {
// 新视频添加速度控制功能
node.addEventListener('ratechange', function() {
showSpeed(this.playbackRate);
});
}
});
});
});
// 启动观察器
observer.observe(document.body, {
childList: true,
subtree: true
});
// 修改面板创建和检查的逻辑
const PANEL_ID = 'video-speed-controller-panel';
// 将面板创建逻辑包装在一个函数中
function createControlPanel() {
// 移除所有已存在的控制面板(确保只有一个)
function removeExistingPanels() {
const existingPanels = document.querySelectorAll(`#${PANEL_ID}`);
existingPanels.forEach(panel => panel.remove());
}
// 在创建新面板前先移除已存在的面板
removeExistingPanels();
// 修改控制面板的初始状态和样式
const controlPanel = document.createElement('div');
controlPanel.id = PANEL_ID;
controlPanel.style.cssText = `
position: fixed;
top: 50%;
right: 20px;
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 10px;
border-radius: 8px;
z-index: 2147483647;
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
gap: 6px;
transform: translateY(-50%);
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
min-width: 120px;
user-select: none;
-webkit-user-select: none;
transition: all 0.3s ease;
`;
// 创建一个迷你版的控制按钮
const miniButton = document.createElement('button');
miniButton.textContent = '⚙️';
miniButton.style.cssText = `
position: fixed;
top: 50%;
right: 20px;
width: 40px;
height: 40px;
background: rgba(0, 0, 0, 0.8);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
z-index: 2147483647;
transform: translateY(-50%);
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
`;
// 默认隐藏控制面板,显示迷你按钮
controlPanel.style.display = 'none';
document.body.appendChild(miniButton);
// 添加迷你按钮的悬停效果
miniButton.addEventListener('mouseover', () => {
miniButton.style.background = 'rgba(0, 0, 0, 0.9)';
miniButton.style.transform = 'translateY(-50%) scale(1.1)';
});
miniButton.addEventListener('mouseout', () => {
miniButton.style.background = 'rgba(0, 0, 0, 0.8)';
miniButton.style.transform = 'translateY(-50%) scale(1)';
});
// 点击迷你按钮显示/隐藏控制面板
let isPanelVisible = false;
miniButton.addEventListener('click', () => {
isPanelVisible = !isPanelVisible;
controlPanel.style.display = isPanelVisible ? 'flex' : 'none';
miniButton.style.display = isPanelVisible ? 'none' : 'flex';
});
// 修改按钮样式,使其更紧凑
const buttonStyle = `
padding: 4px 8px;
font-size: 12px;
`;
// 修改所有按钮的尺寸
controlPanel.querySelectorAll('button').forEach(button => {
button.style.cssText += buttonStyle;
});
// 添加关闭按钮
const closeButton = document.createElement('button');
closeButton.innerHTML = '✕';
closeButton.style.cssText = `
position: absolute;
top: 5px;
right: 5px;
background: transparent;
border: none;
color: rgba(255,255,255,0.6);
cursor: pointer;
font-size: 14px;
padding: 2px 6px;
border-radius: 4px;
`;
closeButton.addEventListener('click', () => {
controlPanel.style.display = 'none';
miniButton.style.display = 'flex';
isPanelVisible = false;
});
controlPanel.appendChild(closeButton);
// 将面板添加到页面
document.body.appendChild(controlPanel);
// 创建速度控制组件
const speedControl = document.createElement('div');
speedControl.style.cssText = `
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
`;
// 减速按钮
const decreaseButton = document.createElement('button');
decreaseButton.textContent = '-';
decreaseButton.style.cssText = `
width: 30px;
height: 30px;
border: none;
border-radius: 4px;
background: #4CAF50;
color: white;
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.3s;
`;
// 速度显示
const speedDisplay = document.createElement('div');
speedDisplay.style.cssText = `
font-size: 16px;
font-weight: bold;
min-width: 60px;
text-align: center;
`;
speedDisplay.textContent = '1.0x';
// 加速按钮
const increaseButton = document.createElement('button');
increaseButton.textContent = '+';
increaseButton.style.cssText = decreaseButton.style.cssText;
// 添加事件监听
let currentSpeed = 1.0;
const speedStep = 0.25;
const minSpeed = 0.25;
const maxSpeed = 16;
decreaseButton.addEventListener('click', () => {
currentSpeed = Math.max(minSpeed, currentSpeed - speedStep);
updateSpeed(currentSpeed);
});
increaseButton.addEventListener('click', () => {
currentSpeed = Math.min(maxSpeed, currentSpeed + speedStep);
updateSpeed(currentSpeed);
});
// 重置按钮
const resetButton = document.createElement('button');
resetButton.textContent = '重置';
resetButton.style.cssText = `
width: 100%;
padding: 6px;
border: none;
border-radius: 4px;
background: #2196F3;
color: white;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
`;
resetButton.addEventListener('click', () => {
currentSpeed = 1.0;
updateSpeed(currentSpeed);
});
// 更新速度的函数
function updateSpeed(speed) {
speedDisplay.textContent = `${speed.toFixed(1)}x`;
const videos = document.getElementsByTagName('video');
for (const video of videos) {
video.playbackRate = speed;
}
}
//
speedControl.appendChild(decreaseButton);
speedControl.appendChild(speedDisplay);
speedControl.appendChild(increaseButton);
controlPanel.appendChild(speedControl);
controlPanel.appendChild(resetButton);
// 加分隔线
const separator = document.createElement('div');
separator.style.cssText = `
height: 1px;
background: rgba(255,255,255,0.2);
margin: 4px 0;
`;
controlPanel.appendChild(separator);
// 下载按钮
const downloadButton = document.createElement('button');
downloadButton.textContent = '下载视频';
downloadButton.style.cssText = `
width: 100%;
padding: 6px;
border: none;
border-radius: 4px;
background: #FF5722;
color: white;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
`;
controlPanel.appendChild(downloadButton);
// 在创建下载按钮后添加点击事件
downloadButton.addEventListener('click', () => {
const videos = document.getElementsByTagName('video');
if (videos.length === 0) {
alert('未找到可下载的视频!');
return;
}
// 创建选择菜单
const selectMenu = document.createElement('div');
selectMenu.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
z-index: 999999;
color: black;
`;
const title = document.createElement('h3');
title.textContent = '请选择下载方式:';
title.style.marginBottom = '15px';
selectMenu.appendChild(title);
// 创建按容器
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.gap = '10px';
buttonContainer.style.justifyContent = 'center';
// 直接下载按钮
const directDownloadBtn = document.createElement('button');
directDownloadBtn.textContent = '直接下载';
directDownloadBtn.style.cssText = `
padding: 8px 15px;
border: none;
border-radius: 4px;
background: #4CAF50;
color: white;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
`;
directDownloadBtn.addEventListener('mouseover', () => directDownloadBtn.style.background = '#45a049');
directDownloadBtn.addEventListener('mouseout', () => directDownloadBtn.style.background = '#4CAF50');
directDownloadBtn.addEventListener('click', () => {
document.body.removeChild(selectMenu);
if (videos.length > 1) {
showVideoSelection(videos);
} else {
downloadVideo(videos[0]);
}
});
// 录制下载按钮
const recordDownloadBtn = document.createElement('button');
recordDownloadBtn.textContent = '录制下载';
recordDownloadBtn.style.cssText = `
padding: 8px 15px;
border: none;
border-radius: 4px;
background: #FF5722;
color: white;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
`;
recordDownloadBtn.addEventListener('mouseover', () => recordDownloadBtn.style.background = '#F4511E');
recordDownloadBtn.addEventListener('mouseout', () => recordDownloadBtn.style.background = '#FF5722');
recordDownloadBtn.addEventListener('click', () => {
document.body.removeChild(selectMenu);
if (videos.length > 1) {
showVideoSelectionForRecording(videos);
} else {
startRecording(videos[0]);
}
});
buttonContainer.appendChild(directDownloadBtn);
buttonContainer.appendChild(recordDownloadBtn);
selectMenu.appendChild(buttonContainer);
document.body.appendChild(selectMenu);
// 添加拖动功能
makeElementDraggable(selectMenu);
});
// 修改下载视频的函数
function downloadVideo(video) {
// 创建下载提示
const downloadTip = createDownloadTip();
downloadTip.textContent = '正在准备下载...';
try {
// 获取视频源的多种方式
let videoUrl = '';
// 方法1: 直接获取src
videoUrl = video.currentSrc || video.src;
// 方法2: 尝试获取source标签
if (!videoUrl) {
const sources = video.getElementsByTagName('source');
if (sources.length > 0) {
videoUrl = sources[0].src;
}
}
// 方法3: 尝试从video标签的dataset中获取
if (!videoUrl && video.dataset.src) {
videoUrl = video.dataset.src;
}
// 如果找不到视频源或是blob URL,提示使用录制方式
if (!videoUrl || videoUrl.startsWith('blob:')) {
downloadTip.textContent = '无法直接下载视频,请使用录制方式!';
downloadTip.style.background = 'rgba(255, 0, 0, 0.8)';
setTimeout(() => document.body.removeChild(downloadTip), 3000);
return;
}
// 使用 GM_download 进行下载
GM_download({
url: videoUrl,
name: `video_${new Date().getTime()}.mp4`,
headers: {
'Referer': window.location.href,
'Origin': window.location.origin,
'User-Agent': navigator.userAgent
},
onload: function() {
downloadTip.textContent = '下载已开始!';
setTimeout(() => document.body.removeChild(downloadTip), 2000);
},
onerror: function(error) {
console.error('Download failed:', error);
// 如果GM_download失败,尝试使用fetch方法
fallbackDownload(videoUrl, downloadTip);
}
});
} catch (error) {
console.error('Download error:', error);
// 如果主下载方法失败,尝试备用下载方法
fallbackDownload(videoUrl, downloadTip);
}
}
// 添加备用下载方法
function fallbackDownload(videoUrl, downloadTip) {
fetch(videoUrl, {
method: 'GET',
headers: {
'Referer': window.location.href,
'Origin': window.location.origin,
'Range': 'bytes=0-'
},
mode: 'cors',
credentials: 'include'
})
.then(response => response.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `video_${new Date().getTime()}.mp4`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
downloadTip.textContent = '下载已开始!';
setTimeout(() => document.body.removeChild(downloadTip), 2000);
})
.catch(error => {
console.error('Fallback download failed:', error);
downloadTip.textContent = '下载失败,请尝试使用录制方式';
downloadTip.style.background = 'rgba(255, 0, 0, 0.8)';
setTimeout(() => document.body.removeChild(downloadTip), 3000);
});
}
// 创建下载提示的辅助函数
function createDownloadTip() {
const downloadTip = document.createElement('div');
downloadTip.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 999999;
transition: all 0.3s ease;
`;
document.body.appendChild(downloadTip);
return downloadTip;
}
// 添加拖动功能
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 0;
let yOffset = 0;
controlPanel.addEventListener('mousedown', dragStart);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', dragEnd);
function dragStart(e) {
if (e.target === controlPanel) {
isDragging = true;
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
}
}
function drag(e) {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, controlPanel);
}
}
function setTranslate(xPos, yPos, el) {
el.style.transform = `translate(${xPos}px, ${yPos}px)`;
}
function dragEnd() {
isDragging = false;
}
// 添加一个拖动提示
controlPanel.title = '按住空白处拖动位置';
// 在添加下载按钮后,添加另一个分隔线解除粘贴限制按钮
const separator2 = document.createElement('div');
separator2.style.cssText = `
height: 1px;
background: rgba(255,255,255,0.2);
margin: 4px 0;
`;
controlPanel.appendChild(separator2);
// 创建一个容器来放置两个按钮
const pasteControlContainer = document.createElement('div');
pasteControlContainer.style.cssText = `
display: flex;
gap: 5px;
width: 100%;
`;
// 创建解除限制按钮
const pasteButton = document.createElement('button');
pasteButton.textContent = '限制已解除';
pasteButton.style.cssText = `
width: 100%;
padding: 6px;
border: none;
border-radius: 4px;
background: #4CAF50;
color: white;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
`;
// 添加点击事件
pasteButton.addEventListener('click', () => {
removeCopyLimits();
showMessage('已解除输入限制!', 'success');
});
// 解除复制粘贴限制的核心函数
function removeCopyLimits() {
try {
// 移除基本限
document.body.removeAttribute("onselectstart");
document.documentElement.style.userSelect = "unset";
// 移除编辑器限制
if (window.UE && window.UE.instants && typeof window.UE.instants === "object") {
for (const [key, instance] of Object.entries(window.UE.instants)) {
try {
if (instance.options) {
instance.options.disablePasteImage = false;
}
if (instance.removeListener) {
instance.removeListener("beforepaste", window.editorPaste);
}
} catch (error) {
console.error("[Input Helper] Failed to remove copy limits from editor instance", key, error);
}
}
}
// 移除调试器限制
const constructorHook = Function.prototype.constructor;
Function.prototype.constructor = (s) => {
if (s === "debugger") {
return () => {};
}
return constructorHook(s);
};
// 添加全样式
const style = document.createElement('style');
style.textContent = `
* {
user-select: text !important;
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
}
`;
document.head.appendChild(style);
console.info("[Input Helper] Successfully removed copy limits.");
} catch (error) {
console.error("[Input Helper] Failed to remove copy limits:", error);
}
}
// 在页面加载完成后自动执行一次
setTimeout(removeCopyLimits, 1000);
// 将按钮添加到控制面板
pasteControlContainer.appendChild(pasteButton);
// 将容器添加到控制面板
controlPanel.appendChild(pasteControlContainer);
// 添加分隔线
const separator3 = document.createElement('div');
separator3.style.cssText = `
height: 1px;
background: rgba(255,255,255,0.2);
margin: 4px 0;
`;
controlPanel.appendChild(separator3);
// 换原来的快捷键设置钮为更新检查按钮
const updateButton = document.createElement('button');
updateButton.textContent = '检查更新';
updateButton.style.cssText = `
width: 100%;
padding: 6px;
border: none;
border-radius: 4px;
background: #2196F3;
color: white;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
`;
// 添加更新图标
const updateIcon = document.createElement('span');
updateIcon.innerHTML = '↻';
updateIcon.style.cssText = `
display: inline-block;
transition: transform 0.3s ease;
`;
updateButton.prepend(updateIcon);
// 添加悬停效果
updateButton.addEventListener('mouseover', () => {
updateButton.style.background = '#1976D2';
updateIcon.style.transform = 'rotate(180deg)';
});
updateButton.addEventListener('mouseout', () => {
updateButton.style.background = '#2196F3';
updateIcon.style.transform = 'rotate(0)';
});
// 添加点击事件
updateButton.addEventListener('click', () => {
// 添加旋转动画
updateIcon.style.animation = 'checking 1s linear infinite';
// 跳转到更新页面
window.open('http://113.44.209.113:9002/', '_blank');
// 移除动画
setTimeout(() => {
updateIcon.style.animation = 'none';
}, 1000);
});
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes checking {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
// 将更新按钮添加到控制面板
controlPanel.appendChild(updateButton);
// 更新按钮提示文本
disablePasteButton.title = `快捷键:${formatHotkey(hotkeys.inputText)}`;
// 添加辅助函数到作用域
function formatHotkey(hotkey) {
const parts = [];
if (hotkey.ctrl) parts.push('Ctrl');
if (hotkey.shift) parts.push('Shift');
if (hotkey.alt) parts.push('Alt');
parts.push(hotkey.key.toUpperCase());
return parts.join(' + ');
}
function showTip(message) {
const tip = document.createElement('div');
tip.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 999999;
`;
tip.textContent = message;
document.body.appendChild(tip);
setTimeout(() => {
document.body.removeChild(tip);
}, 2000);
}
function createSettingsWindow() {
const settingsWindow = document.createElement('div');
settingsWindow.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
z-index: 999999;
min-width: 300px;
color: black;
`;
const title = document.createElement('h3');
title.textContent = '快捷键设置';
title.style.marginBottom = '15px';
settingsWindow.appendChild(title);
// 创建快捷键输入区域
const hotkeyInput = document.createElement('input');
hotkeyInput.type = 'text';
hotkeyInput.readOnly = true;
hotkeyInput.value = formatHotkey(hotkeys.inputText);
hotkeyInput.style.cssText = `
width: 100%;
padding: 8px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 4px;
`;
const label = document.createElement('div');
label.textContent = '输入文字快捷键:';
label.style.color = '#333';
settingsWindow.appendChild(label);
settingsWindow.appendChild(hotkeyInput);
// 添加提示文本
const tip = document.createElement('div');
tip.textContent = '点击输入框并按下新的快捷键组合';
tip.style.cssText = `
font-size: 12px;
color: #666;
margin: 5px 0 15px 0;
`;
settingsWindow.appendChild(tip);
// 添加按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.cssText = `
display: flex;
gap: 10px;
margin-top: 20px;
`;
// 保存按钮
const saveButton = document.createElement('button');
saveButton.textContent = '保存';
saveButton.style.cssText = `
flex: 1;
padding: 8px;
border: none;
border-radius: 4px;
background: #4CAF50;
color: white;
cursor: pointer;
`;
// 取消按钮
const cancelButton = document.createElement('button');
cancelButton.textContent = '取消';
cancelButton.style.cssText = `
flex: 1;
padding: 8px;
border: none;
border-radius: 4px;
background: #f44336;
color: white;
cursor: pointer;
`;
// 添加快捷键监听
hotkeyInput.addEventListener('keydown', (e) => {
e.preventDefault();
const newHotkey = {
key: e.key.toLowerCase(),
ctrl: e.ctrlKey,
shift: e.shiftKey,
alt: e.altKey
};
hotkeys.inputText = newHotkey;
hotkeyInput.value = formatHotkey(newHotkey);
});
// 添加按钮事件
saveButton.addEventListener('click', () => {
localStorage.setItem('videoSpeedHotkeys', JSON.stringify(hotkeys));
disablePasteButton.title = `快捷键:${formatHotkey(hotkeys.inputText)}`;
document.body.removeChild(settingsWindow);
showTip('快捷键设置已保存!');
});
cancelButton.addEventListener('click', () => {
document.body.removeChild(settingsWindow);
});
buttonContainer.appendChild(saveButton);
buttonContainer.appendChild(cancelButton);
settingsWindow.appendChild(buttonContainer);
// 添加拖动功能
makeElementDraggable(settingsWindow);
document.body.appendChild(settingsWindow);
}
// 在添加更新按钮后,添加版本显示
const versionInfo = document.createElement('div');
versionInfo.style.cssText = `
text-align: center;
color: rgba(255, 255, 255, 0.6);
font-size: 12px;
margin-top: 8px;
padding: 4px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.1);
`;
versionInfo.textContent = 'v1.0.0'; // 使用脚本的当前版本
// 将版本信息添加到控制面板
controlPanel.appendChild(versionInfo);
// 为版本信息添加悬停效果
versionInfo.addEventListener('mouseover', () => {
versionInfo.style.background = 'rgba(255, 255, 255, 0.15)';
});
versionInfo.addEventListener('mouseout', () => {
versionInfo.style.background = 'rgba(255, 255, 255, 0.1)';
});
function startRecording(video) {
try {
const stream = video.captureStream();
const mediaRecorder = new MediaRecorder(stream, {
mimeType: 'video/webm;codecs=h264,opus', // 使用H264编码
videoBitsPerSecond: 2500000, // 2.5Mbps视频比特率
audioBitsPerSecond: 128000 // 128kbps音频比特率
});
const chunks = [];
// 创建录制控制界面
const recordControl = document.createElement('div');
recordControl.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
z-index: 999999;
color: black;
text-align: center;
`;
// 添加进度条
const progress = document.createElement('div');
progress.style.cssText = `
width: 300px;
height: 20px;
background: #f0f0f0;
border-radius: 10px;
margin: 10px 0;
overflow: hidden;
`;
const progressBar = document.createElement('div');
progressBar.style.cssText = `
width: 0%;
height: 100%;
background: #4CAF50;
transition: width 1s linear;
`;
progress.appendChild(progressBar);
// 添加时显示
const timeDisplay = document.createElement('div');
timeDisplay.style.marginBottom = '10px';
// 添加控制按钮
const controlButtons = document.createElement('div');
controlButtons.style.marginTop = '10px';
const stopButton = document.createElement('button');
stopButton.textContent = '停止录制';
stopButton.style.cssText = `
padding: 5px 15px;
margin: 0 5px;
border: none;
border-radius: 3px;
background: #f44336;
color: white;
cursor: pointer;
`;
recordControl.appendChild(timeDisplay);
recordControl.appendChild(progress);
recordControl.appendChild(controlButtons);
controlButtons.appendChild(stopButton);
document.body.appendChild(recordControl);
// 开始录制
let startTime = Date.now();
let duration = 0;
mediaRecorder.start(1000); // 每秒保存一次数据
// 更新进度的函数
const updateProgress = () => {
if (mediaRecorder.state === 'recording') {
duration = Math.floor((Date.now() - startTime) / 1000);
timeDisplay.textContent = `录制 ${duration} 秒`;
progressBar.style.width = `${Math.min((duration / 300) * 100, 100)}%`;
if (duration >= 300) {
mediaRecorder.stop();
return;
}
requestAnimationFrame(updateProgress);
}
};
updateProgress();
// 修改处理录制数据的部分
mediaRecorder.ondataavailable = (e) => {
if (e.data && e.data.size > 0) {
chunks.push(e.data);
}
};
// 修改停止制的处理
mediaRecorder.onstop = () => {
if (chunks.length > 0) {
const blob = new Blob(chunks, {
type: 'video/webm;codecs=h264,opus'
});
// 添加metadata以支持进度条
const metadata = {
duration: duration,
lastModified: new Date().getTime()
};
const finalBlob = new Blob([blob], {
type: 'video/webm;codecs=h264,opus',
...metadata
});
const url = window.URL.createObjectURL(finalBlob);
const a = document.createElement('a');
a.href = url;
a.download = `recorded_video_${new Date().getTime()}.webm`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
// 显示下载提示
const downloadTip = document.createElement('div');
downloadTip.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 999999;
`;
downloadTip.textContent = '录制完成,开始下载!';
document.body.appendChild(downloadTip);
setTimeout(() => {
document.body.removeChild(downloadTip);
}, 2000);
}
// 移除录制控制界面
document.body.removeChild(recordControl);
};
// 停止按钮��件
stopButton.addEventListener('click', () => {
if (mediaRecorder.state === 'recording') {
mediaRecorder.stop();
}
});
// 添加拖动功能
makeElementDraggable(recordControl);
recordControl.title = '按住可拖动';
return mediaRecorder;
} catch (err) {
// 如果H264编码不支持,回退到VP8
try {
const mediaRecorder = new MediaRecorder(stream, {
mimeType: 'video/webm;codecs=vp8,opus',
videoBitsPerSecond: 2500000,
audioBitsPerSecond: 128000
});
// ... 继续录制流程 ...
} catch (fallbackErr) {
console.error('录制失败:', fallbackErr);
alert('录制失败,该视频可能无法录制');
return null;
}
}
}
// 添加视频选择函数
function showVideoSelection(videos) {
const selectMenu = document.createElement('div');
selectMenu.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
z-index: 999999;
color: black;
`;
const title = document.createElement('h3');
title.textContent = '选择要下载的视频:';
title.style.marginBottom = '10px';
selectMenu.appendChild(title);
Array.from(videos).forEach((video, index) => {
const button = document.createElement('button');
button.textContent = `视频 ${index + 1}`;
button.style.cssText = buttonStyle;
button.style.margin = '5px';
button.style.background = '#4CAF50';
button.addEventListener('click', () => {
downloadVideo(video);
document.body.removeChild(selectMenu);
});
selectMenu.appendChild(button);
});
document.body.appendChild(selectMenu);
}
// 添加录制视频选择函数
function showVideoSelectionForRecording(videos) {
const selectMenu = document.createElement('div');
selectMenu.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
z-index: 999999;
color: black;
`;
const title = document.createElement('h3');
title.textContent = '选择录制的视频:';
title.style.marginBottom = '10px';
selectMenu.appendChild(title);
Array.from(videos).forEach((video, index) => {
const button = document.createElement('button');
button.textContent = `视频 ${index + 1}`;
button.style.cssText = buttonStyle;
button.style.margin = '5px';
button.style.background = '#FF5722';
button.addEventListener('click', () => {
startRecording(video);
document.body.removeChild(selectMenu);
});
selectMenu.appendChild(button);
});
document.body.appendChild(selectMenu);
}
// 修改录制控制面板,添加拖动功能
function makeElementDraggable(element) {
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 0;
let yOffset = 0;
element.style.cursor = 'move';
element.addEventListener('mousedown', dragStart);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', dragEnd);
function dragStart(e) {
if (e.target === element || e.target.parentNode === element) {
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
isDragging = true;
}
}
function drag(e) {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, element);
}
}
function setTranslate(xPos, yPos, el) {
el.style.transform = `translate(${xPos}px, ${yPos}px)`;
}
function dragEnd() {
isDragging = false;
}
}
// 添��一个变量来存储最后聚焦的输入框
let lastFocusedElement = null;
// 添加全局焦点监听
document.addEventListener('focusin', (e) => {
if (e.target.tagName === 'INPUT' ||
e.target.tagName === 'TEXTAREA' ||
e.target.getAttribute('contenteditable') === 'true') {
lastFocusedElement = e.target;
}
});
}
// 在页面加载完成后建面板
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createControlPanel);
} else {
createControlPanel();
}
// 添加一个定时检查,确保面板存在
setInterval(() => {
if (!document.getElementById(PANEL_ID)) {
createControlPanel();
}
}, 1000);
})();