实战:从零构建一个支持屏幕录制与片段合并的视频管理系统 (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.mzph.cn/news/1178752.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Submitted to Journal 是什么:Expert Systems With Applications(ESWA)中 有作者没有确认

Submitted to Journal 是什么:Expert Systems With Applications(ESWA)中 有作者没有确认或者在技术审查,一般还是每一个人确认下 目录 Submitted to Journal 是什么:Expert Systems With Applications(ESWA)中 有作者没有确认或者在技术审查,一般还是每一个人确认下 �…

Python 3.14(2025最新版)的核心语法特性分析 - 详解

Python 3.14(2025最新版)的核心语法特性分析 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&qu…

人生必备的9个第一性原理

我们应该有的「第一性原理」 目录我们应该有的「第一性原理」🌞 第一组:社交、旅行、阅读1. 社交的第一性原理是品质,而非数量2. 旅行的第一性原理是体验,而非打卡3. 阅读的第一性原理是思考,而非浏览⏰ 第二组&#x…

Apollo 9.0.0 自动驾驶系统整体架构分析 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

uboot—2.编译

1. 交叉编译工具链 https://blog.csdn.net/Qiuhongim/article/details/124137192 2. 编译3. 烧写

火锅爱好者集合!2026年这些火锅品牌值得一试,火锅/牛肉火锅/成都火锅/老火锅/社区火锅/附近火锅,火锅品牌哪个好 - 品牌推荐师

近年来,火锅市场持续升温,消费者对食材品质、用餐体验及文化认同的需求日益提升。从社区老店到连锁品牌,火锅行业正经历从“口味竞争”到“综合体验”的升级。本文基于公开数据、市场调研及行业口碑,筛选出5家具有…

2026 年高铁广告公司综合实力排行榜单及行业分析报告:2026年高铁广告公司如何选?高铁广告公司推荐及选购指南 - Top品牌推荐

一、高铁广告行业概述 1.1 市场规模 中国高铁广告市场正处于高速发展阶段,根据多家权威机构预测:2025 年市场规模预计突破300 亿元,年复合增长率保持在15% 以上前瞻产业研究院测算显示,高铁广告市场规模将在 2025 …

Cisco全套教学视频汇总,CCNA,CCNP多套教程版,学思科必备,推荐收藏!

【手机复制整段内容,打开「夸克APP」即可获取。 动作发嘎齾戆嬲多好 /~51393A5jIj~:/】

攻克RAG系统最后一公里 图文混排PDF解析的挑战与实战方案

在企业数字化转型的浪潮中,PDF格式早已成为高价值信息的主要载体。研究数据显示,企业80%的核心资产如技术白皮书、财务报表、行业报告等都被锁定在PDF文件中,但传统RAG(检索增强生成)方案受限于解析能力,仅…

TailWindCss 核心功能总结 - 教程

TailWindCss 核心功能总结 - 教程2026-01-18 16:35 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !impor…

向师傅学习的黄金和斐波总结二

目录一、概述二、本次策略和对应学习三、黄金和斐波那些事?四、感悟一、概述 本次是向师傅学习黄金和斐波第二波,虽然说是第二波,其实离第一波深度学习已经过去40天了。 2026元旦开年,新年新气象,一扫去年的所有&…

爬虫学习

爬虫 定义爬取目标(URL) 在浏览器网络部分能让我们看见真正需要的url#定义urlurl = f"https://www.mashangpa.com/api/problem-detail/1/data/?page={i}"但大多数情况我们需要携带请求头 #声明请求头----带…

STM32F0实战:基于HAL库开发【1.3】

2.2.2 STM32系统板 STM32F072VBT6微控制器采用LQFP100封装,引脚间距仅为0.5mm,这样的封装很难用手工的方法搭建系统板,所以建议使用成品的系统板或者全功能开发板来完成本书的代码测试任务。 STM32F072VBT6系统板的外观如图2-75所示,系统板电路原理可以参考本书附录A,全…

KUKA机器人KR C4 控制柜蓄电池的维护指南

KUKA机器人KR C4 控制柜蓄电池的维护指南 一、核心功能:断电瞬间的 “数据守护者” KUKA KR C4控制柜内置蓄电池,绝非普通备用电源,其核心使命是保障系统断电后的安全受控关机。当外部供电突然中断时,蓄电池立即通过电源管理板(PMB)X305接口为控制系统供电,支撑系统完成…

Java泛型---桥方法

桥方法 桥方法是为了解决类型擦除后与多态的冲突。为了理解什么是桥方法。下面举实例,假设有一个泛型类Pair,它用来保存两个值,first与second,first永远比second大。 现在,创建一个子类去继承它。 package Test;i…

自动化视频制作:深入解析 FFmpeg 图片转视频脚本

在处理大量生成的图片资产(如 AI 生成的艺术图)时,我们经常需要将它们转化为更易于分享的视频格式。手动操作不仅繁琐,而且难以保证每张图停留时间的精确性。本文将带你深入剖析一个自动化脚本,看看它是如何一键完成“…

C14-2025.1.18

XSS原理 XSS(跨站脚本攻击)漏洞是一种常见的安全漏洞,其原理是攻击者通过向网页中注入恶意脚本代码 (JavaScript代码) ,当其他用户访问该页面时,浏览器会执行这些脚本,从而盗取用户信息(如Cookie)、劫持会话…

AI论文写作效率飙升200%:从零到一9款工具实操指南,彻底告别拖延 - 麟书学长

论文写作新手常遇文档空白、修改难、查重焦虑等问题,本文对比9款AI论文工具核心能力,如一站式的图灵论文AI(30分钟出5万字初稿)、小众高效的AI论文及时雨、全学科覆盖的巨鲸写作等,还详解图灵实操步骤、其他工具指…

2026年国内专业的智能货架定制厂家推荐,钢制货架/悬臂货架/重载货架/不锈钢货架/模具架/货架,智能货架批发厂家选哪家 - 品牌推荐师

行业背景与市场趋势 随着制造业智能化升级加速,仓储物流环节对空间利用率、存取效率及数据追溯能力的要求日益严苛。智能货架作为工业4.0的核心载体,通过集成物联网、机器人及AI算法,实现从“静态存储”到“动态管理…

30、二分类和多分类的区别 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …