开发React Chrome扩展:如何实现无需额外标签页的会议音频录制?
核心实现思路:基于Chrome桌面捕获API+后台脚本/内嵌UI实现无新标签音频录制
一、关键Chrome API组合
- chrome.tabCapture:专门针对标签页的媒体捕获接口,可直接获取指定活跃标签页的音频流,无需用户手动选择(若提前拿到标签ID),是实现特定会议页面录制的核心。
- chrome.desktopCapture.chooseDesktopMedia:备选方案,允许用户弹窗选择要捕获的标签页/窗口音频,适合需要用户自主选择目标场景。
- chrome.runtime.sendMessage/onMessage:用于React控制UI(弹窗/侧边栏)与后台脚本的通信,传递录制启停指令。
- chrome.storage.local:临时存储录制状态、音频数据片段,避免页面刷新丢失进度。
- chrome.sidePanel:实现内嵌侧边栏UI的核心API,让录制控制全程在当前浏览器窗口内完成,完全不干扰会议页面。
二、无新标签UI的两种实现方案(对应TacTiq的无缝体验)
- 浏览器动作Popup:点击扩展图标弹出React开发的小型控制UI,弹窗不会打开新标签,关闭后后台脚本仍可继续录制,适合轻量控制场景。
- 侧边栏(Side Panel):利用Chrome侧边栏API将录制UI嵌入浏览器右侧,全程与会议页面共存,操作更直观,适合需要持续显示录制状态的场景。
三、具体实现步骤(React + Chrome扩展)
1. 配置manifest.json
{ "manifest_version": 3, "name": "会议音频录制工具", "version": "1.0", "permissions": ["tabCapture", "storage", "activeTab"], "background": { "service_worker": "background.js" }, "action": { "default_popup": "popup.html" }, "side_panel": { "default_path": "sidepanel.html" } }
2. 后台脚本(Service Worker)处理录制逻辑
// background.js let mediaRecorder = null; let audioChunks = []; chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => { switch (message.action) { case 'start': try { const stream = await chrome.tabCapture.capture({ audio: true, video: false }); mediaRecorder = new MediaRecorder(stream); audioChunks = []; mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data); mediaRecorder.onstop = async () => { const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); const audioBase64 = await blobToBase64(audioBlob); await chrome.storage.local.set({ recordedAudio: audioBase64 }); }; mediaRecorder.start(); sendResponse({ status: 'success', msg: '录制已启动' }); } catch (err) { sendResponse({ status: 'error', msg: err.message }); } break; case 'stop': if (mediaRecorder && mediaRecorder.state === 'recording') { mediaRecorder.stop(); // 停止后释放流 mediaRecorder.stream.getTracks().forEach(track => track.stop()); sendResponse({ status: 'success', msg: '录制已停止' }); } else { sendResponse({ status: 'error', msg: '无正在进行的录制' }); } break; default: sendResponse({ status: 'error', msg: '无效指令' }); } return true; // 保持消息通道开放,支持异步响应 }); function blobToBase64(blob) { return new Promise((resolve) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.readAsDataURL(blob); }); }
3. React控制UI示例(Popup)
// Popup.jsx import { useState } from 'react'; function RecordingPopup() { const [isRecording, setIsRecording] = useState(false); const [statusMsg, setStatusMsg] = useState(''); const toggleRecording = async () => { const action = isRecording ? 'stop' : 'start'; const response = await chrome.runtime.sendMessage({ action }); setStatusMsg(response.msg); if (response.status === 'success') { setIsRecording(!isRecording); } }; return ( <div style={{ padding: '16px', width: '220px' }}> <button onClick={toggleRecording} style={{ width: '100%', padding: '10px', border: 'none', borderRadius: '4px', backgroundColor: isRecording ? '#dc3545' : '#28a745', color: '#fff', fontSize: '14px', cursor: 'pointer' }} > {isRecording ? '停止录制' : '开始录制'} </button> {statusMsg && <p style={{ marginTop: '10px', textAlign: 'center', fontSize: '12px' }}>{statusMsg}</p>} </div> ); } export default RecordingPopup;
四、会议转录集成思路
录制完成后,可通过以下方式实现转录:
- 前端直接调用Web Speech API的
SpeechRecognition接口(兼容性有限,适合轻量场景); - 将存储的Base64音频数据发送到后端,对接OpenAI Whisper、Google Speech-to-Text等专业语音转文字服务。
五、TacTiq无缝体验的核心逻辑推测
- 用后台Service Worker持久化处理录制任务,UI仅作为控制入口,不占用独立标签页;
- 提前通过权限申请流程获取用户授权,避免每次录制重复弹窗;
- 音频数据直接在后台内存/IndexedDB处理,无需渲染到页面,降低资源消耗。
内容的提问来源于stack exchange,提问作者Igor Leonir
相关产品推荐
相关产品推荐

