当前位置: 首页 > news >正文

实战:从零构建一个支持屏幕录制与片段合并的视频管理系统 (Node.js + FFmpeg)

1. 背景 (Background)

作为开发者,我们每天都会浏览大量的技术教程(YouTube, Bilibili)。收藏夹往往乱作一团,而且很多时候,我们只需要长视频中的某几个关键片段

单纯的书签管理已经无法满足需求,我想要一个工具:

  1. 美观管理:像 Netflix 一样展示我的视频收藏。
  2. 随手记录:看到重点时,能直接录制屏幕片段。
  3. 自动归档:录制的片段自动关联到当前视频条目下,不丢失。
  4. 整理输出:能把零散的片段合并成一个完整的笔记视频。

于是,VideoHub Pro诞生了。
C:\myApp\myvideo-manager

2. 目标 (Goal)

我们需要构建一个B/S 架构的全栈应用,无需繁重的数据库安装,开箱即用。

  • 前端:原生 HTML/CSS/JS(极致轻量),实现卡片式展示、剪贴板截图粘贴、Web 录屏 API 调用。
  • 后端:Node.js (Express),负责数据存储(JSON)、文件上传(Multer)以及视频处理(FFmpeg)。
  • 核心痛点解决:解决 Web 端录屏的持久化存储,以及多段视频的后端合并下载。

3. 方法 (Method)

  • 技术栈

    • Server: Node.js + Express
    • Media Processing:fluent-ffmpeg(调用系统安装的 FFmpeg)
    • Uploads:multer
    • Frontend: HTML5, CSS3 (Grid + Glassmorphism), Native JavaScript (ES6+)
    • Database: 本地data.json文件 (模拟 NoSQL)
  • 数据模型设计
    一个视频对象(Video)包含基本信息和一个片段(Clips)数组:

    {"id":"uuid-...","title":"Node.js 教程","url":"https://...","thumbnail":"base64...","clips":[{"filename":"17000.webm","createdAt":"...","size":1024}]}

4. 过程与核心代码解析 (Process)

4.1 前端交互与视觉设计

我们采用了深色模式 (Dark Mode)毛玻璃 (Glassmorphism)风格,利用 CSS Grid 实现响应式的卡片布局。

关键点在于粘贴上传缩略图。为了提升体验,我们监听了粘贴事件:

// 监听粘贴事件,自动读取剪贴板中的图片流并转为 Base64document.getElementById('paste-area').addEventListener('paste',e=>{constitems=e.clipboardData.items;for(letitemofitems){if(item.type.startsWith('image')){constblob=item.getAsFile();constreader=newFileReader();reader.onload=ev=>{// 直接展示并赋值给隐藏域document.getElementById('v-thumb').value=ev.target.result;// ...预览逻辑};reader.readAsDataURL(blob);}}});

4.2 核心难点一:Web 录屏与自动上传

这是本项目的核心功能。传统的 Web 录屏往往只保存在浏览器内存中,刷新即丢失。我们的改进方案是:录制停止 -> 自动上传 -> 服务器持久化

前端实现 (index.html):
使用navigator.mediaDevices.getDisplayMedia获取屏幕流,通过MediaRecorder录制。

asyncfunctionstartRec(){// 1. 唤起浏览器原生屏幕分享弹窗conststream=awaitnavigator.mediaDevices.getDisplayMedia({video:true,audio:true});// 2. 创建录制器,指定 MIME 类型constmediaRecorder=newMediaRecorder(stream,{mimeType:'video/webm; codecs=vp9'});letchunks=[];// 临时存储二进制块mediaRecorder.ondataavailable=e=>{if(e.data.size>0)chunks.push(e.data);};// 3. 监听停止事件mediaRecorder.onstop=async()=>{// 将 chunks 转为 Blob 对象constblob=newBlob(chunks,{type:'video/webm'});// 4. 构建 FormData,准备上传constfd=newFormData();fd.append('clip',blob,`rec_${Date.now()}.webm`);fd.append('videoId',currentVideoId);// 关键:带上当前视频的 ID// 5. 立即上传服务器awaitfetch(`${API}/upload-clip`,{method:'POST',body:fd});// 停止流,释放资源stream.getTracks().forEach(t=>t.stop());refreshClipsPanel();// 刷新 UI};mediaRecorder.start();}

后端处理 (server.js):
后端接收文件,保存到uploads目录,并更新data.json中的对应记录。

app.post('/api/upload-clip',upload.single('clip'),(req,res)=>{const{videoId}=req.body;// ...读取 JSON 数据constidx=videos.findIndex(v=>v.id===videoId);if(idx!==-1){// 构建片段元数据对象constclipInfo={filename:req.file.filename,// Multer 生成的物理文件名originalName:req.file.originalname,size:req.file.size,createdAt:newDate()};// 关联数据:将片段信息 push 到该视频的 clips 数组中videos[idx].clips.push(clipInfo);writeDB(videos);// 写入磁盘res.json({success:true});}});

分析:这种“ID关联法”使得录屏文件不再是孤立的,而是永远从属于某个具体的视频条目。


4.3 核心难点二:多片段合并 (FFmpeg)

当用户录制了片段 A、片段 B、片段 C 后,可能只想把 A 和 C 合并下载。

策略变化
早期版本是前端把 Blob 传给后端合并,这很浪费流量。现在的策略是:文件已经在服务器上了,前端只需要传“文件名列表”,后端在本地进行合并

前端逻辑
用户勾选复选框,获取文件名数组:

constcbs=document.querySelectorAll('.clip-cb:checked');constfilenames=Array.from(cbs).map(cb=>cb.value);// ['file1.webm', 'file3.webm']awaitfetch(`${API}/merge-files`,{body:JSON.stringify({filenames})// 只发文件名,极快});

后端逻辑 (server.js):
使用fluent-ffmpeg链式调用。

app.post('/api/merge-files',(req,res)=>{const{filenames}=req.body;constoutputFilename=`merged-${Date.now()}.mp4`;constoutputPath=path.join(UPLOAD_DIR,outputFilename);constcommand=ffmpeg();// 1. 遍历添加输入源 (Input)filenames.forEach(name=>{// 拼接服务器的绝对路径command.input(path.join(UPLOAD_DIR,name));});// 2. 执行合并 (Merge)command.on('error',(err)=>res.status(500).send('Merge failed')).on('end',()=>{// 3. 返回合并后的文件 URLres.json({url:`/uploads/${outputFilename}`});}).mergeToFile(outputPath,UPLOAD_DIR);// 输出});

分析:这个设计将繁重的视频处理工作全部交给了服务器(Node.js 调用底层 FFmpeg),前端只需要发送轻量级的指令,体验非常流畅。


5. 结果展示 (Result)

最终,我们得到了一个功能闭环的系统:

  1. 卡片墙:首页展示所有视频,缩略图清晰,支持分类过滤。
  2. 播放详情页
    • 左侧:智能解析 URL(YouTube/Bilibili 自动嵌入 iframe),下方有“录制”按钮。
    • 右侧:显示所有关联的录屏片段历史。
  3. 工作流
    • 点击“录制” -> 选择区域 -> 录制结束 ->自动出现在右侧列表
    • 在右侧列表中勾选第1条和第3条 -> 点击“合并下载” -> 浏览器弹出合并后的 MP4 下载。
http://www.jsqmd.com/news/263305/

相关文章:

  • Submitted to Journal 是什么:Expert Systems With Applications(ESWA)中 有作者没有确认
  • Python 3.14(2025最新版)的核心语法特性分析 - 详解
  • KUKA机器人KR C4 控制柜蓄电池的维护指南
  • 30、二分类和多分类的区别 - 教程
  • 人生必备的9个第一性原理
  • Apollo 9.0.0 自动驾驶系统整体架构分析 - 实践
  • 火锅爱好者集合!2026年这些火锅品牌值得一试,火锅/牛肉火锅/成都火锅/老火锅/社区火锅/附近火锅,火锅品牌哪个好 - 品牌推荐师
  • 2026 年高铁广告公司综合实力排行榜单及行业分析报告:2026年高铁广告公司如何选?高铁广告公司推荐及选购指南 - Top品牌推荐
  • 攻克RAG系统最后一公里 图文混排PDF解析的挑战与实战方案
  • TailWindCss 核心功能总结 - 教程
  • 向师傅学习的黄金和斐波总结二
  • 爬虫学习
  • Java泛型---桥方法
  • C14-2025.1.18
  • C14-2025.1.18
  • AI论文写作效率飙升200%:从零到一9款工具实操指南,彻底告别拖延 - 麟书学长
  • 别忽视这个16×16的小图标:Icon背后的产品逻辑,以及一款让开发者省心的制作工具
  • 2026年国内专业的智能货架定制厂家推荐,钢制货架/悬臂货架/重载货架/不锈钢货架/模具架/货架,智能货架批发厂家选哪家 - 品牌推荐师
  • 小程序计算机毕设之基于springboot位置服务的城市路线分享系统小程序(完整前后端代码+说明文档+LW,调试定制等)
  • 小程序毕设项目:基于springboot位置服务的城市路线分享系统小程序(源码+文档,讲解、调试运行,定制等)
  • 计算机小程序毕设实战-基于springboot+微信小程序的校园导航与信息服务系统基于微信小程序的校园信息交流平台【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 【课程设计/毕业设计】基于小程序的位置服务的城市路线分享系统的设计与实现基于springboot位置服务的城市路线分享系统小程序【附源码、数据库、万字文档】
  • 基于AI智能名片链动2+1模式S2B2C商城小程序的流量运营策略研究
  • 学霸同款2026 AI论文网站TOP8:本科生毕业论文必备测评
  • 分期乐购物额度闲置?安全回收指南+可可收实操拆解 - 可可收
  • 全球口碑最好的防脱洗发水排名前十,洗发水哪个牌子效果好?青少年安全首选第一名 - 博客万
  • 大学院-筆記試験練習:数据库(データベース問題訓練) と 软件工程(ソフトウェア)(12)
  • 微信小程序毕设项目推荐-基于微信小程序的驾考在线学习与测试系统基于springboot+微信小程序的驾考在线学习与测试系统【附源码+文档,调试定制服务】
  • 【毕业设计】基于springboot位置服务的城市路线分享系统小程序(源码+文档+远程调试,全bao定制等)
  • Java String 字符串终极详解(全特性+全API+全示例)