您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
视频录屏
// ==UserScript== // @name VideoRecorder // @namespace Recorder // @version 0.0.3 // @description 视频录屏 // @author zenfanxing // @license MIT // @match *://pan.baidu.com/disk/home* // @match *://yun.baidu.com/disk/home* // @match *://pan.baidu.com/disk/main* // @match *://yun.baidu.com/disk/main* // @match *://pan.baidu.com/s* // @match *://yun.baidu.com/s* // @match *://*.youku.com/* // @match *://*.iqiyi.com/* // @match *://*.iq.com/* // @match *://*.le.com/* // @match *://v.qq.com/* // @match *://m.v.qq.com/* // @match *://*.tudou.com/* // @match *://*.mgtv.com/* // @match *://tv.sohu.com/* // @match *://film.sohu.com/* // @match *://*.1905.com/* // @match *://*.bilibili.com/* // @match *://*.pptv.com/* // @match *://item.taobao.com/* // @match *://s.taobao.com/* // @match *://chaoshi.detail.tmall.com/* // @match *://detail.tmall.com/* // @match *://detail.tmall.hk/* // @match *://item.jd.com/* // @match *://*.yiyaojd.com/* // @match *://npcitem.jd.hk/* // @match *://*.liangxinyao.com/* // @match *://music.163.com/* // @match *://y.qq.com/* // @match *://*.kugou.com/* // @match *://*.kuwo.cn/* // @match *://*.ximalaya.com/* // @match *://*.zhihu.com/* // @match *://*.douyin.com/* // @match *://*.kuaishou.com/* // @match *://*.ixigua.com/* // @match *://*.youtube.com/* // ==/UserScript== (function () { // const videoType = 'video/mp4' const videoType = 'video/webm' const html = ` <style> *:not(#recorder-videos)>video:hover { outline: 4px solid greenyellow; } video.recorder-selected { outline: 4px solid orange; } .recorder { display: none; } .recorder.show { display: block; } .recorder * { box-sizing: border-box; } .recorder-btn { position: fixed; right: 60px; bottom: 60px; width: 60px; height: 60px; padding: 4px; padding-bottom: 56px; opacity: 0.4; transform: all 0.3s; background-color: #00B2FF; border-radius: 30px; overflow: hidden; color: white; z-index: 999; } .recorder-btn button { display: none; width: 52px; height: 52px; background-color: #0091FF; border-radius: 50%; margin-bottom: 10px; border: none; cursor: pointer; color: inherit; font-size: 12px; } .recorder-btn #recorder-btn-list { display: block; } .recorder-btn button.show { display: block; } .recorder-btn:hover { height: auto; opacity: 1; } .recorder-modal { display: none; } .recorder-modal.show { display: block; } .recorder-mask { position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.3); z-index: 9999; } .recorder-videos { position: fixed; right: 0; top: 0; background-color: #fff; width: 300px; height: 100%; padding: 10px; z-index: 9999; overflow-y: auto; } .recorder-videos::before { content: 'CTRL + T 快捷键快速设置录屏结束点'; margin-bottom: 10px; font-size: 12px; } .recorder-videos video { width: 280px; } .recorder-videos a { display: block; color: white; background-color: #00B2FF; padding: 6px; margin-bottom: 10px; text-align: center; text-decoration: none; } .recorder-btn-handle { display: flex; position: absolute; left: 0; bottom: 0; width: 60px; height: 60px; justify-content: center; align-items: center; font-size: 40px; background-color: #00B2FF; cursor: pointer; } #recorder-time { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 50%; background-color: red; display: none; justify-content: center; align-items: center; text-align: center; font-size: 12px; } #recorder-time.show { display: flex; } </style> <div class="recorder" id="recorder"> <div class="recorder-btn"> <button id="recorder-btn-list">列表</button> <button id="recorder-btn-pause">暂停</button> <button id="recorder-btn-resume">恢复</button> <button id="recorder-btn-stop">停止</button> <button id="recorder-btn-start" class="show">开始</button> <div class="recorder-btn-handle"> <svg width="1em" height="1em" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M9.25 10.607v5.321h1.5v-5.321h5.321v-1.5H10.75V3.786h-1.5v5.32H3.93v1.5h5.32Z" fill="currentColor"></path> </svg> <div id="recorder-time"></div> </div> </div> <div id="recorder-modal" class="recorder-modal"> <div id="recorder-mask" class="recorder-mask"></div> <div id="recorder-videos" class="recorder-videos"></div> </div> </div> ` // 创建html const createHTML = () => { const con = document.createElement('div'); con.innerHTML = html; document.body.appendChild(con) } // 时间格式化 const timeFormat = (t, hasHours = true) => { let h, m if (hasHours) { h = parseInt(t / 3600000) + '' m = parseInt((t % 3600000) / 60000) + '' } else { m = parseInt(t / 60000) + '' } const s = parseInt(t % 60000 / 1000) + '' return hasHours ? `${h}:${m.padStart(2, '0')}:${s.padStart(2, '0')}` : `${m}:${s.padStart(2, '0')}` } // 状态 const STATUS = { PAUSE: 'pause', RECORDING: 'recording', STOP: 'stop' } class Recorder { constructor() { this._urls = []; // 录屏视频链接 this._status = STATUS.STOP; // 初始状态 this._time = 0; // 录制时常 this._realtime = 0; // 实时显示时长 this._targetTime = 0; // 自动停止时间点 this.init(); // 初始话html // dom this._recorderEle = document.getElementById('recorder'); this._modalEle = document.getElementById('recorder-modal'); this._maskEle = document.getElementById('recorder-mask'); this._videos = document.getElementById('recorder-videos'); this._startEle = document.getElementById('recorder-btn-start'); this._pauseEle = document.getElementById('recorder-btn-pause'); this._resumeEle = document.getElementById('recorder-btn-resume'); this._stopEle = document.getElementById('recorder-btn-stop'); this._listEle = document.getElementById('recorder-btn-list'); this._timeEle = document.getElementById('recorder-time'); this._video = document.querySelector('video'); // 自动选择页面第一个视频节点 if (this._video) { this._video.classList.add("recorder-selected"); this._recorderEle.classList.add('show'); this._video.preload = 'auto'; } this.listen(); } init() { createHTML(html); } // 监听元素点击事件 listen() { this._startEle.onclick = () => { this.start(); } this._pauseEle.onclick = () => { this.pause(); } this._resumeEle.onclick = () => { this.resume(); } this._stopEle.onclick = () => { this.stop(); } this._listEle.onclick = () => { this._modalEle.classList.add('show'); } this._maskEle.onclick = () => { this._modalEle.classList.remove('show'); } if (this._video) { this.videoAddListener(); } // 快捷键 window.addEventListener('keydown', (e) => { // ctrl + T if (this._video && e.ctrlKey && e.keyCode === 84) { if (this._status === STATUS.STOP && this._video.currentTime && this._targetTime !== this._video.currentTime && confirm( `设置视频的${timeFormat(this._video.currentTime * 1000)}下次录制的结束时间点`)) { this._targetTime = this._video.currentTime; } else if (this._targetTime && alert('清除录制结束点')) { this._targetTime = 0; } } }) // 切换视频节点 document.body.addEventListener('click', (e) => { if (e.target.nodeName === 'VIDEO' && e.target.parentNode.id !== 'recorder-videos' && this._status === STATUS.STOP && this._video !== e.target) { this._recorderEle.classList.add('show'); this._video && this._video.classList.remove('recorder-selected'); this._video && this.videoRemoveListener(); this._video = e.target; this._video.preload = 'auto'; this.videoAddListener(); this._video.classList.add("recorder-selected"); } }) } // 视频节点事件监听 videoAddListener() { if (!this._video) return; this._videoHandlePlay = () => { if (this._status === STATUS.PAUSE) { this.setStatus(STATUS.RECORDING); } } this._videoHandlePause = () => { if (this._status === STATUS.RECORDING) { this.setStatus(STATUS.PAUSE); } } this._videoHandleEnded = () => { if (this._status !== STATUS.STOP) { this.setStatus(STATUS.STOP); } } this._videoHandleTimeUpdate = () => { if (this._targetTime && this._status === STATUS.RECORDING && this._video.currentTime >= this ._targetTime) { this.stop(); } if (this._status === STATUS.RECORDING && this._video.buffered && this._video.buffered.length && (this._video.buffered.end(this._video.buffered.length - 1) - this._video.currentTime < 3)) { this._bufferTimer && clearInterval(this._bufferTimer); this._bufferTimer = null; this.pause(); this._video.pause(); console.log('buffered 小于 3s, 暂停录制等待缓存', this._video.buffered.end(this._video.buffered.length - 1) - this._video.currentTime) this._bufferTimer = setInterval(() => { if (this._video.buffered.end(this._video.buffered.length - 1) - this._video.currentTime > 6) { this._bufferTimer && clearInterval(this._bufferTimer); this._bufferTimer = null; this.resume(); this._video.play(); console.log('buffered 大于 6s, 恢复录制') } }, 500) } } this._video.addEventListener('play', this._videoHandlePlay); this._video.addEventListener('pause', this._videoHandlePause); this._video.addEventListener('waiting', this._videoHandlePause); this._video.addEventListener('playing', this._videoHandlePlay); this._video.addEventListener('ended', this._videoHandleEnded); this._video.addEventListener('timeupdate', this._videoHandleTimeUpdate); } // 视频节点事件移除 videoRemoveListener() { if (!this._video) return; this._video.removeEventListener('play', this._videoHandlePlay); this._video.removeEventListener('pause', this._videoHandlePause); this._video.removeEventListener('waiting', this._videoHandlePause); this._video.removeEventListener('playing', this._videoHandlePlay); this._video.removeEventListener('ended', this._videoHandleEnded); this._video.addEventListener('timeupdate', this._videoHandleTimeUpdate); } // 更新状态 setStatus(status) { const now = Date.now(); switch (status) { case STATUS.RECORDING: // 视频可播放下一帧才开始录制 if (this._status === STATUS.PAUSE) { this._recorder.state !== 'inactive' && this._recorder.resume(); // 恢复录制 } else { this._recorder.start(); // 开始录制 } this._video.play(); this.timing(); // 更新ui this._timeEle.classList.add('show'); this._startEle.classList.remove('show'); this._resumeEle.classList.remove('show'); this._pauseEle.classList.add('show'); this._stopEle.classList.add('show'); this._bufferTimer && clearInterval(this._bufferTimer); this._bufferTimer = null; break; case STATUS.PAUSE: this.timingEnd(); this._recorder.state !== 'inactive' && this._recorder.pause(); // 暂停录制 // 更新ui this._pauseEle.classList.remove('show'); this._resumeEle.classList.add('show'); break; case STATUS.STOP: this.timingEnd(); this._bufferTimer && clearInterval(this._bufferTimer); this._bufferTimer = null; this._time = 0; this._targetTime = 0; this._recorder.state !== 'inactive' && this._recorder.stop(); // 停止录制 this._video.pause(); // 暂停视频 // 更新ui this._timeEle.classList.remove('show'); this._startEle.classList.add('show'); this._pauseEle.classList.remove('show'); this._resumeEle.classList.remove('show'); this._stopEle.classList.remove('show'); break; } // 更新状态 this._status = status; } // 录制前初始化 startInit() { if (this._targetTime && this._video.currentTime >= this._targetTime) { this._targetTime = 0; } this._time = 0; this._realtime = 0; this._recorder = null; this._currentStartTime = null; clearInterval(this._timer); this._timer = null; clearInterval(this._bufferTimer) this._bufferTimer = null; } // 开始录制 start() { if (!this._video || !this._video.isConnected) { alert('请点击重新选中播放器'); return; } if (this._video.ended) { alert('视频已经播完'); return; } if (this._video.readyState !== 4) { alert('视频还未准备好'); return } if (this._status === STATUS.STOP) { this.startInit(); // 初始化 this._recorder = new MediaRecorder(this._video.captureStream(), { mimeType: 'video/webm;codecs=h264' // 视频编码格式 }); const blobs = []; // 处理剪辑的数据 this._recorder.ondataavailable = (event) => { if (event.data.size > 0) blobs.push(event.data); }; this._recorder.onstop = () => { this._urls.push(URL.createObjectURL(new Blob(blobs, { type: videoType }))); // 更新列表 this.renderList(); } this.setStatus(STATUS.RECORDING); } } // 停止 stop() { if (this._recorder && this._status !== STATUS.STOP) { this.setStatus(STATUS.STOP); } } // 暂停 pause() { if (this._recorder && this._status === STATUS.RECORDING) { this.setStatus(STATUS.PAUSE); } } // 恢复 resume() { if (this._recorder && this._status === STATUS.PAUSE) { this.setStatus(STATUS.RECORDING); } } // 开始计时 timing() { this._currentStartTime = Date.now(); const run = () => { this._realtime = this._time + (Date.now() - this._currentStartTime) this._timeEle.innerText = `${timeFormat(this._realtime, false)}${this._targetTime ? `\n(${timeFormat(this._targetTime * 1000)})`: ''}`; } run(); this._timer = setInterval(run, 500); } // 结束计时 timingEnd() { if (this._currentStartTime) { clearInterval(this._timer); this._time += (Date.now() - this._currentStartTime); this._currentStartTime = null; this._realtime = 0; } } // 更新录制列表 renderList() { const htmlList = this._urls.map((url) => `<video src=${url} controls></video><a download href="${url}">下载</a>`); this._videos.innerHTML = htmlList.join(''); } } window.__videoRecorder = new Recorder() })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址