// ==UserScript== // @name 屏幕录像机 // @namespace https://scriptcat.org/zh-CN/users/13895 // @version 0.1 // @description 在任何网页上添加屏幕录制功能 // @author you // @match *://*/* // @grant none // ==/UserScript== (function() { 'use strict'; // 创建控制界面元素 const createControlPanel = () => { const panel = document.createElement('div'); panel.style.position = 'fixed'; panel.style.bottom = '20px'; panel.style.right = '20px'; panel.style.zIndex = '999999'; panel.style.backgroundColor = 'white'; panel.style.padding = '1rem'; panel.style.borderRadius = '8px'; panel.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)'; panel.style.display = 'flex'; panel.style.flexDirection = 'column'; panel.style.gap = '0.8rem'; const title = document.createElement('h3'); title.textContent = '屏幕录制'; title.style.margin = '0 0 0.5rem 0'; title.style.fontSize = '1rem'; title.style.textAlign = 'center'; const micToggleContainer = document.createElement('div'); micToggleContainer.style.display = 'flex'; micToggleContainer.style.alignItems = 'center'; micToggleContainer.style.gap = '0.5rem'; const micLabel = document.createElement('label'); micLabel.textContent = '启用麦克风'; micLabel.style.fontSize = '0.9rem'; const micToggle = document.createElement('input'); micToggle.type = 'checkbox'; micToggle.id = 'tm-mic-toggle'; micToggleContainer.appendChild(micLabel); micToggleContainer.appendChild(micToggle); const startBtn = document.createElement('button'); startBtn.id = 'tm-start-btn'; startBtn.textContent = '开始录制'; startBtn.style.padding = '0.5rem'; startBtn.style.backgroundColor = '#28a745'; startBtn.style.color = 'white'; startBtn.style.border = 'none'; startBtn.style.borderRadius = '4px'; startBtn.style.cursor = 'pointer'; const stopBtn = document.createElement('button'); stopBtn.id = 'tm-stop-btn'; stopBtn.textContent = '停止录制'; stopBtn.style.padding = '0.5rem'; stopBtn.style.backgroundColor = '#dc3545'; stopBtn.style.color = 'white'; stopBtn.style.border = 'none'; stopBtn.style.borderRadius = '4px'; stopBtn.style.cursor = 'pointer'; stopBtn.style.display = 'none'; panel.appendChild(title); panel.appendChild(micToggleContainer); panel.appendChild(startBtn); panel.appendChild(stopBtn); document.body.appendChild(panel); return { micToggle, startBtn, stopBtn }; }; // 获取DOM元素 const { micToggle, startBtn, stopBtn } = createControlPanel(); // 状态变量 let mediaRecorder = null; let recordedChunks = []; let mediaStream = null; // 事件监听器 startBtn.addEventListener('click', startRecording); stopBtn.addEventListener('click', stopRecording); /** * 开始屏幕和音频录制 */ async function startRecording() { try { // 获取屏幕捕获流 const displayVideoConstraints = { cursor: 'always' }; const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: displayVideoConstraints, audio: false, }); let audioStream = null; // 如果麦克风开关开启,获取音频流 if (micToggle.checked) { audioStream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, sampleRate: 44100, }, }); } // 合并视频和音频轨道 const videoTrack = displayStream.getVideoTracks()[0]; const audioTrack = audioStream ? audioStream.getAudioTracks()[0] : null; mediaStream = new MediaStream(); mediaStream.addTrack(videoTrack); if (audioTrack) { mediaStream.addTrack(audioTrack); } // 当用户停止分享时停止录制 videoTrack.onended = () => { stopRecording(); }; // 配置MediaRecorder recordedChunks = []; // 尝试使用MP4格式 let mimeType = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; // 检查浏览器支持 if (!MediaRecorder.isTypeSupported(mimeType)) { alert(`您的浏览器不支持MP4格式,将使用WebM格式。`); mimeType = 'video/webm'; } mediaRecorder = new MediaRecorder(mediaStream, { mimeType }); // 处理录制数据 mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { recordedChunks.push(event.data); } }; // 录制停止时下载 mediaRecorder.onstop = downloadRecording; // 开始录制并更新UI mediaRecorder.start(); updateUIVisibility(true); } catch (error) { console.error('开始录制时出错:', error); alert('无法开始录制,请确保您授予了屏幕和麦克风权限。'); } } /** * 停止录制并释放资源 */ function stopRecording() { if (mediaRecorder && mediaRecorder.state === 'recording') { mediaRecorder.stop(); } // 停止所有轨道以释放资源 mediaStream?.getTracks().forEach(track => track.stop()); // 更新UI updateUIVisibility(false); } /** * 下载录制的视频 */ function downloadRecording() { if (recordedChunks.length === 0) { console.warn('没有录制数据,下载已中止。'); return; } // 确定文件格式 const mimeType = mediaRecorder?.mimeType || 'video/mp4'; const fileExtension = mimeType.includes('mp4') ? 'mp4' : 'webm'; // 创建Blob并下载 const recordingBlob = new Blob(recordedChunks, { type: mimeType }); const url = URL.createObjectURL(recordingBlob); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; a.download = `screen-recording-${new Date().toISOString()}.${fileExtension}`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); // 重置 recordedChunks = []; } /** * 更新UI可见性 */ function updateUIVisibility(isRecording) { startBtn.style.display = isRecording ? 'none' : 'block'; stopBtn.style.display = isRecording ? 'block' : 'none'; micToggle.disabled = isRecording; } })();