Electron 视频播放器开发

创建于:2025年1月9日

使用 O1 回答 Chat01

问题

使用electron写一个本地视频播放器,要求:

1,多种视频格式支持:如 MP4、MKV、AVI、MOV、WMV 等。
2,点击播放进度条可以快速跳到对应的某个位置
3,按下左右键分别后退、前进5秒
4,按下上下键分别增加、减少5%音量
5,按下f/F键(不区分大小写)实现全屏播放
6,鼠标单击播放/暂停,双击视频区域进入/退出全屏
7,按下j/k键(不区分大小写)分别前进、后退10%
8,播放本地视频
9,视频界面有两种语言,英语和汉语,默认使用英语
10,第一个标签页为“本地视频”,第二个标签页为“远程视频”,可以在用户输入iframe标签加载对应的视频

思考

Envisioning the video player

I'm working on coding an Electron-based video player with features like multiple format support, clickable progress bar, and keyboard shortcuts for control.

Shaping code structure

I'm finalizing the folder layout, crafting minimal code for local and remote video playback, and setting up language switching logic using strings in an object or JSON.

Thought about local video player design,持续 8 秒

回答

下面给出一个示例 Electron 项目的结构示例及关键代码,演示如何实现一个具有以下功能的视频播放器:

  1. 支持多种视频格式:MP4、MKV、AVI、MOV、WMV 等
  2. 点击播放进度条可快速跳到对应位置
  3. 按下左右键分别后退、前进 5 秒
  4. 按下上下键分别增加、减少 5% 音量
  5. 按下 f/F 键(不区分大小写)实现全屏播放/退出全屏
  6. 鼠标单击视频区域:播放/暂停,双击视频区域:进入/退出全屏
  7. 按下 j/k 键(不区分大小写)分别前进、后退 10%(相对于视频总时长)
  8. 可以播放本地视频(第一个标签页)
  9. 视频界面有两种语言,英语和汉语,默认使用英语
  10. 第二个标签页为“远程视频”,可以在输入框中输入 iframe 标签地址,加载对应视频

下面代码仅供参考,建议在此基础上进行扩展和调整,以满足更多需求。


项目结构示例

text
electron-video-player/ ├── package.json ├── main.js # Electron 主进程入口文件 ├── index.html # 渲染进程主页面 ├── renderer.js # 页面逻辑脚本 ├── style.css # 样式文件 └── assets/ └── sample.mp4 # 测试用的视频(可自行替换)

1. package.json

json
{ "name": "electron-video-player", "version": "1.0.0", "main": "main.js", "scripts": { "start": "electron ." }, "dependencies": { "electron": "^22.0.0" } }

注意:如果你想使用更高版本的 Electron,请在此处进行相应修改。

2. main.js

js
const { app, BrowserWindow } = require('electron'); const path = require('path'); let mainWindow; function createWindow() { mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { nodeIntegration: false, // 建议关闭以提高安全性 contextIsolation: true, // 启用 contextIsolation 后,需使用 preload 或 IPC 通信 preload: path.join(__dirname, 'preload.js') // 如果有预加载脚本,可在此处配置 } }); mainWindow.loadFile('index.html'); // 打开开发者工具,方便调试 // mainWindow.webContents.openDevTools(); mainWindow.on('closed', () => { mainWindow = null; }); } app.whenReady().then(createWindow); app.on('window-all-closed', () => { // 在 macOS 上,除非用户用 Cmd + Q 退出,否则绝大部分应用会保持激活 if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { // 在 macOS 上,当单击 dock 图标并且没有其他窗口打开时 if (mainWindow === null) { createWindow(); } });

如果你想在渲染进程中直接使用 require,可将 nodeIntegration 设置为 true,但这会带来安全隐患,需自行评估。

3. index.html

html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Electron Video Player</title> <link rel="stylesheet" href="style.css"> </head> <body> <!-- 语言切换按钮 --> <div class="language-switch"> <button id="toggle-lang">切换到中文</button> </div> <!-- 标签页导航 --> <div class="tab-nav"> <button class="tab-button" id="local-video-tab">Local Video</button> <button class="tab-button" id="remote-video-tab">Remote Video</button> </div> <!-- 内容区域 --> <div class="tab-content" id="local-video-content"> <div id="video-container"> <video id="video-player" src="assets/sample.mp4" controls="false"></video> <!-- 自定义控件示例,这里主要演示进度条 --> <div id="controls-container"> <input type="range" id="progress-bar" value="0" step="0.1" min="0" max="100"> </div> </div> </div> <div class="tab-content" id="remote-video-content" style="display: none;"> <label for="iframe-url" class="lang lang-en">Please input iframe URL: </label> <label for="iframe-url" class="lang lang-zh" style="display: none;">请输入iframe地址: </label> <input type="text" id="iframe-url" placeholder="https://example.com/video-iframe"> <button id="load-iframe" class="lang lang-en">Load iFrame</button> <button id="load-iframe" class="lang lang-zh" style="display: none;">加载视频</button> <div id="iframe-container"></div> </div> <script src="renderer.js"></script> </body> </html>

这里为了演示,使用了一个自定义进度条 <input type="range">。如果想更精细控制,可以使用 <progress> 或者完全自定义一个元素并手动绘制。

4. style.css

css
body { margin: 0; padding: 0; font-family: sans-serif; } .language-switch { margin: 10px; } .tab-nav { margin: 10px; } .tab-button { padding: 6px 12px; margin-right: 5px; cursor: pointer; } .tab-content { margin: 10px; } #video-container { position: relative; width: 800px; max-width: 100%; } #video-player { width: 100%; outline: none; background: black; cursor: pointer; /* 让视频区域表现为可点击 */ } #controls-container { display: flex; align-items: center; width: 100%; margin-top: 5px; } #progress-bar { width: 100%; } #iframe-container { width: 800px; height: 450px; margin-top: 10px; background: #ccc; /* 仅做示意,可自行替换或去掉 */ }

5. renderer.js

主要实现以下功能:

  • 键盘事件(左右、上下、f/F、j/k)
  • 点击/双击视频区域(单击播放/暂停,双击全屏)
  • 进度条拖动(跳转到对应进度)
  • Tab 切换(本地视频/远程视频)
  • 语言切换(默认英语,点击切换到中文)
  • 远程视频 iframe 加载
js
// 缓存一些 DOM 元素 const video = document.getElementById('video-player'); const progressBar = document.getElementById('progress-bar'); const localVideoContent = document.getElementById('local-video-content'); const remoteVideoContent = document.getElementById('remote-video-content'); const localVideoTab = document.getElementById('local-video-tab'); const remoteVideoTab = document.getElementById('remote-video-tab'); const iframeUrlInput = document.getElementById('iframe-url'); const loadIframeButton = document.getElementById('load-iframe'); const iframeContainer = document.getElementById('iframe-container'); const toggleLangBtn = document.getElementById('toggle-lang'); // 语言相关 // 用于切换隐藏/显示类名为 .lang-en / .lang-zh 的元素 let currentLang = 'en'; // 默认英文 // 切换语言函数 function toggleLanguage() { const enElements = document.querySelectorAll('.lang-en'); const zhElements = document.querySelectorAll('.lang-zh'); if (currentLang === 'en') { // 切换到中文 enElements.forEach(el => el.style.display = 'none'); zhElements.forEach(el => el.style.display = 'inline-block'); toggleLangBtn.innerText = 'Switch to English'; currentLang = 'zh'; } else { // 切换到英文 enElements.forEach(el => el.style.display = 'inline-block'); zhElements.forEach(el => el.style.display = 'none'); toggleLangBtn.innerText = '切换到中文'; currentLang = 'en'; } } // 初始化语言 toggleLangBtn.addEventListener('click', toggleLanguage); // 标签页切换 localVideoTab.addEventListener('click', () => { localVideoContent.style.display = 'block'; remoteVideoContent.style.display = 'none'; }); remoteVideoTab.addEventListener('click', () => { localVideoContent.style.display = 'none'; remoteVideoContent.style.display = 'block'; }); // 远程视频 iframe 加载 loadIframeButton.addEventListener('click', () => { const url = iframeUrlInput.value.trim(); if (url) { // 这里简单演示直接生成 iframe // 注意:实际使用中要确保 iframe URL 安全 iframeContainer.innerHTML = `<iframe src="${url}" width="100%" height="100%" frameborder="0"></iframe>`; } }); // 播放器事件 // 1. 更新进度条 video.addEventListener('timeupdate', () => { if (!video.duration) return; const percent = (video.currentTime / video.duration) * 100; progressBar.value = percent; }); // 2. 拖动进度条跳转 progressBar.addEventListener('input', () => { if (!video.duration) return; const newTime = (progressBar.value / 100) * video.duration; video.currentTime = newTime; }); // 3. 单击播放/暂停,双击全屏 let clickTimer = null; video.addEventListener('click', (event) => { if (clickTimer) { // 如果双击 clearTimeout(clickTimer); clickTimer = null; toggleFullScreen(); } else { // 单击 clickTimer = setTimeout(() => { clickTimer = null; togglePlayPause(); }, 300); // 300ms 内第二次点击,则视为双击 } }); // 播放/暂停 function togglePlayPause() { if (video.paused) { video.play(); } else { video.pause(); } } // 4. 进入/退出全屏 function toggleFullScreen() { if (!document.fullscreenElement) { video.requestFullscreen(); } else { document.exitFullscreen(); } } // 键盘事件监听 document.addEventListener('keydown', (event) => { const key = event.key.toLowerCase(); switch (key) { case 'arrowleft': // 左箭头:后退 5 秒 video.currentTime = Math.max(0, video.currentTime - 5); break; case 'arrowright': // 右箭头:前进 5 秒 video.currentTime = Math.min(video.duration, video.currentTime + 5); break; case 'arrowup': // 上箭头:音量 +5% video.volume = Math.min(1, video.volume + 0.05); break; case 'arrowdown': // 下箭头:音量 -5% video.volume = Math.max(0, video.volume - 0.05); break; case 'f': // f/F:全屏切换 toggleFullScreen(); break; case 'j': // j/J:前进 10% if (video.duration) { video.currentTime = Math.min( video.duration, video.currentTime + video.duration * 0.1 ); } break; case 'k': // k/K:后退 10% if (video.duration) { video.currentTime = Math.max( 0, video.currentTime - video.duration * 0.1 ); } break; default: break; } });

运行示例

  1. 在项目根目录下(即 electron-video-player/)执行:

    bash
    npm install
  2. 安装依赖后,执行:

    bash
    npm start
  3. Electron 窗口启动后,即可看到本示例项目提供的界面:

    • 第一个标签页播放本地视频(默认使用 assets/sample.mp4
    • 第二个标签页可以输入 iframe 的地址并点击“Load iFrame”/“加载视频”以查看远程视频

功能说明及扩展思路

  1. 多格式支持
    只要 Electron 运行的环境中安装了对应解码器,或者使用的是系统原生支持的格式,<video> 标签基本都能播放。也可以考虑使用第三方库(如 mpv.jsvideo.js 等)来扩展更多格式/功能支持。

  2. 单击/双击区分
    目前采用在第一次点击时启动一个 300ms 的定时器,若 300ms 内检测到第二次点击,则视为双击,可以根据项目需求调整此阈值。

  3. 自定义进度条
    可以改用 <progress> 或其他更华丽的自定义控件来显示进度条,也可以配合 canvas 绘制更酷炫的样式。

  4. 语言切换
    这里的示例是通过在 HTML 中给元素加 .lang-en.lang-zh 类名,并控制 display 来切换语言。如果需要更多语言,或者更丰富的文本内容,可以使用 i18n(如 i18next)等专业国际化库来做。

  5. 远程视频
    本示例仅仅是通过 <iframe> 加载远程内容;实际应用中,可能需要鉴权、跨域等处理,甚至还需要自己搭建视频服务器,也可直接使用 <video> 加载远程视频流。

  6. 安全性

    • 如果你需要在渲染进程中使用 Node.js,需要将 nodeIntegration 设置为 true,但要注意这样做会带来安全风险。
    • 通常建议使用 preload.js + contextIsolation + IPC 通信在主进程和渲染进程之间传递数据。

上述示例仅供参考,如果你需要更完整或更丰富的样式及功能,请根据自身需求自行扩展。祝你开发顺利!

分享这个问答