# pymedia **Repository Path**: chenxushui/pymedia ## Basic Information - **Project Name**: pymedia - **Description**: No description available - **Primary Language**: Python - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-13 - **Last Updated**: 2026-09-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README --- AIGC: ContentProducer: '001191110102MAD55U9H0F10002' ContentPropagator: '001191110102MAD55U9H0F10002' Label: '1' ProduceID: '752137ef-0007-4f63-97e1-e8f0868f6af5' PropagateID: '752137ef-0007-4f63-97e1-e8f0868f6af5' ReservedCode1: '972a778d-0d8b-4515-91d8-2969cddcb2d1' ReservedCode2: '972a778d-0d8b-4515-91d8-2969cddcb2d1' --- # PyMedia > 一个用 Python 写的免费桌面视频播放器 | 零广告 · 零遥测 · 零依赖 --- ## 这是什么 PyMedia 是一个用 Python 从零开始写的 Windows 桌面视频播放器。 为什么写它?因为市面上的播放器要么塞广告,要么装捆绑软件,要么格式不支持,要么界面丑得让人没心情看电影。我想要一个**干净的、自己的、能控制每一个细节的**播放器——所以就有了 PyMedia。 它不追踪你的数据,不弹广告,不装后台服务,不需要注册账号。你下载一个 EXE,双击就能用,就这么简单。 --- ## 功能特性 ### 播放核心 - **全格式支持** — MP4 / MKV / FLV / AVI / MOV / WebM / RMVB / TS / MPG / WMV / M4V / 3GP / VOB / OGV / M2TS 等 18 种视频格式,MP3 / FLAC / AAC / OGG / WAV / WMA / M4A / APE / OPUS / AMR 等 10 种音频格式 - **硬件解码** — GPU 加速,4K HDR 流畅播放 - **流媒体协议** — HTTP / HTTPS / RTMP / RTP 网络流直链播放 - **播放速度** — 0.5x ~ 4.0x 八档调速,音调自动修正 ### 播放管理 - **播放列表** — 拖拽添加文件/文件夹,列表内排序、删除、上下移动 - **播放历史** — 自动记录最近 200 条播放记录,LRU 策略管理 - **断点续播** — 关闭时记住播放位置,下次打开提示「上次播放到 1:23:45,是否继续?」 - **最近播放菜单** — 菜单栏「最近播放」快速访问历史记录,可清空 ### 字幕与音轨 - **外挂字幕** — 支持 SRT / ASS / SSA / VTT / SUB / SMI 六种格式 - **自动匹配** — 字幕文件和视频放一起自动加载(同名匹配 > 语言后缀 > 唯一字幕) - **字幕调整** — 显示/隐藏切换、提前/延后 0.5 秒微调(Z/X 键) - **多音轨切换** — 原生支持多音轨 MKV,菜单中自由切换 - **5.1 声道智能下混** — 自动适配立体声输出设备,避免 USB DAC 周期性无声 ### 界面体验 - **深色 / 浅色主题** — 一键切换,深色主题默认(#1E1F22 + #4A9EFF 强调色) - **进度条宽命中** — 不用精确点到那条细线,点击进度条上下区域也能直接跳转 - **全屏播放** — 自动隐藏菜单栏和播放列表,沉浸式观影 - **空状态引导** — 启动时显示「拖入文件 / 打开文件」提示 - **控制栏自动隐藏** — 播放时控制栏 2 秒后淡出,鼠标移动恢复 - **拖拽打开** — 文件或文件夹直接拖入窗口 - **单实例运行** — 重复打开自动聚焦已有窗口 ### 隐私与安全 - **零广告** — 没有任何广告,没有推广,没有「会员专属」 - **零遥测** — 不联网上报任何数据,所有设置和历史存在本地 `%APPDATA%/PyMedia/` - **零依赖** — 打包后的 EXE 是单文件,不需要安装任何运行环境 --- ## 快捷键 | 快捷键 | 功能 | 快捷键 | 功能 | |--------|------|--------|------| | Space | 播放 / 暂停 | M | 静音 | | ← / → | 后退 / 前进 5 秒 | F | 全屏 | | ↑ / ↓ | 音量 ±5 | V | 字幕显示 / 隐藏 | | PgUp / PgDn | 上一个 / 下一个 | Z / X | 字幕提前 / 延后 0.5s | | Ctrl+O | 打开文件 | Ctrl+Shift+O | 打开文件夹 | | Ctrl+B | 加载字幕 | Ctrl+L | 显示 / 隐藏播放列表 | | Ctrl+, | 设置 | Esc | 退出全屏 | | Delete | 从列表删除当前项 | | | --- ## 下载与使用 ### 方式一:直接下载 EXE(推荐普通用户) 1. 到 [Gitee Releases](../releases) 下载 `PyMedia-Setup-1.0.0.exe` 2. 双击安装,选择安装目录(默认 `C:\Program Files\PyMedia`) 3. 安装完成自动创建桌面快捷方式 4. 双击快捷方式即可使用,无需安装任何其他软件 > 安装包约 100MB(内含 libmpv 引擎),安装后可直接播放所有支持格式。 ### 方式二:免安装便携版 1. 到 [Gitee Releases](../releases) 下载 `PyMedia.exe`(单文件便携版) 2. 放到任意目录,双击运行 3. 首次运行会在 `%APPDATA%/PyMedia/` 下创建配置文件 --- ## 技术架构 ### 三层解耦设计 ``` ┌─────────────────────────────────────────────────┐ │ main.py │ │ (入口 · 日志 · 单实例锁) │ ├─────────────────────────────────────────────────┤ │ UI 层 (PySide6) │ │ main_window · control_bar · playlist_panel │ │ settings_dialog · theme │ ├─────────────────────────────────────────────────┤ │ 应用层 (Python) │ │ controller · events · history · playlist │ │ config · files · single_instance │ ├─────────────────────────────────────────────────┤ │ 引擎层 (libmpv) │ │ backend (mpv 封装) │ └─────────────────────────────────────────────────┘ ``` 引擎层只暴露 Python 接口,UI 层不直接接触 libmpv。如果未来想换播放引擎(比如换成 VLC),只改 `backend.py` 一个文件。 ### 项目结构 ``` pymedia/ ├── main.py # 程序入口(105 行) ├── app/ │ ├── core/ # 核心数据管理 │ │ ├── config.py # 配置管理(JSON 持久化到 %APPDATA%) │ │ ├── history.py # 播放历史(LRU + 断点续播,上限 200 条) │ │ └── playlist.py # 播放列表管理 │ ├── player/ # 播放引擎层 │ │ ├── backend.py # libmpv 封装(核心,~490 行) │ │ ├── controller.py # 播放控制器(中间层,~310 行) │ │ └── events.py # mpv 事件 → Qt 信号桥接 │ ├── ui/ # 界面层 │ │ ├── main_window.py # 主窗口(~590 行) │ │ ├── control_bar.py # 控制栏 + 自定义进度条 │ │ ├── playlist_panel.py # 播放列表面板 │ │ ├── settings_dialog.py # 设置对话框 │ │ └── theme.py # QSS 主题样式(深色/浅色) │ └── utils/ # 工具 │ ├── files.py # 媒体识别 / 字幕匹配 / 时间格式化 │ └── single_instance.py # 单实例锁(防止重复启动) ├── build/ # 打包配置 │ ├── pymedia.spec # PyInstaller 打包配置 │ └── pymedia_installer.iss # Inno Setup 安装程序脚本 ├── runtime/mpv/ # libmpv 运行时(DLL ~99MB) ├── tests/test_core.py # 单元测试(43 项) └── requirements.txt ``` ### 技术栈 | 组件 | 版本 | 用途 | |------|------|------| | Python | 3.13 | 开发语言 | | PySide6 | 6.11 | Qt for Python GUI 框架 | | python-mpv | 1.0.8 | libmpv 的 Python 绑定 | | libmpv | 0.41.0 (LGPL) | mpv 媒体播放引擎 | | PyInstaller | 6.22 | 打包成 Windows EXE | | Inno Setup | 6.x | 生成安装程序 | ### 代码规模 | 维度 | 数值 | |------|------| | Python 代码总行数 | ~3,300 行 | | Python 模块数 | 20 个 | | 单元测试 | 43 项(files / playlist / history / config 四大模块全覆盖) | | 打包后体积 | ~102MB(单文件 EXE,含 libmpv 引擎) | --- ## 自己编译 ### 环境要求 - Python 3.13+ - PySide6 6.11+ - python-mpv 1.0+ - libmpv-2.dll(放到 `runtime/mpv/` 目录下) ### 安装依赖 ```bash pip install -r requirements.txt -i https://pypi.org/simple ``` > 如果用清华镜像源遇到 403,用官方源 `pypi.org/simple`。 ### 运行 ```bash python main.py ``` ### 测试 ```bash python -m pytest tests/ -v ``` ### 打包成 EXE 项目已配置好 PyInstaller 和 Inno Setup 打包脚本,两步即可生成安装包: **第一步:PyInstaller 打包成 EXE** ```bash # 需要先安装 PyInstaller pip install pyinstaller -i https://pypi.org/simple # 打包(单文件模式,libmpv-2.dll 会自动打包进去) pyinstaller build/pymedia.spec ``` 打包完成后 `dist/PyMedia.exe` 就是可执行文件,约 102MB,双击即可运行,不需要安装 Python。 **第二步(可选):生成安装程序** ```bash # 需要安装 Inno Setup 6 # 编译安装脚本,生成带向导的安装包 iscc build/pymedia_installer.iss ``` 生成 `dist/PyMedia-Setup-1.0.0.exe`,用户双击即可安装,自动创建桌面快捷方式。 > **给其他开发者的说明**:`runtime/mpv/libmpv-2.dll` 不在 git 仓库中(.gitignore 排除了),需要从 [mpv 官网](https://sourceforge.net/projects/mpv-player-for-windows/files/libmpv/) 下载 `mpv-dev-lgpl-x86_64` 压缩包,解压后把 `libmpv-2.dll` 放到 `runtime/mpv/` 目录。 --- ## 开发历程 项目按 9 个里程碑推进,每个里程碑完整交付并提交 git: | 里程碑 | 内容 | Commit | |--------|------|--------| | M0 + M1 | 项目骨架 + 引擎层 + UI 骨架 | `d671c0b` | | M2 + M3 | 播放控制 + 播放列表 | `054af90` | | M4 | 播放历史 JSON 持久化 + 断点续播 | `102105b` | | M5 | 字幕轨道功能(加载/切换/延迟/缩放) | `abb276f` | | M6 | 主题打磨 · 设置对话框 · 全屏优化 | `43fe7e4` | | M7 | 单元测试(43 项全部通过) | `21e30a9` | | M8 | PyInstaller 打包 + Inno Setup 安装程序 | `f6bef0e` | ### 踩坑记录 开发过程中排查并解决了多个疑难问题,这里记录下来,也给后来者避坑: **1. 进度条拖动不可用** 播放时每 100ms 更新进度条,同时反复调用 `setRange` 干扰用户拖拽。解决方案:缓存 duration,只在变化超过 0.5 秒时才重设范围;释放滑块后给 500ms 宽限期等 mpv seek 完成,防止滑块跳回旧位置。 **2. 间歇性无声** mpv 的 `end-file` 回调在 mpv 事件线程中直接调用 `loadfile` 会导致音频设备冲突。解决方案:用 `QTimer.singleShot(0, ...)` 投递到 Qt 主线程执行文件切换。 **3. 大文件 EAC-3 5.1 周期性无声** 10GB 的 MKV 电影(EAC-3 5.1 声道),播放几秒正常然后几秒没声音,循环往复。排查过程: - 写了 100ms 间隔的高频监控脚本,60 秒 600 个采样点——underrun=0, drop=0, cache=100%,所有 mpv 指标完美正常 - 对比了 4 种 `audio_buffer` 和 `ao` 驱动配置——全部指标无差异 - 最后发现系统音频设备是 USB DAC(aigo T52 Stereo,仅 2 声道),mpv 把 5.1 声道 floatp 直接发给 Windows,Windows 音频引擎实时下混到立体声再通过 USB 传输,USB 等时传输的时序抖动导致周期性 buffer 不足 - mpv 的 `underrun` 计数器不追踪 WASAPI 设备级 buffer 状态,所以指标正常但实际听感有卡顿 - 解决:`audio_buffer` 从 0.5s 增大到 1.0s + 启用 `audio_normalize_downmix=yes` 标准化下混 **4. 切换音轨/字幕轨报错** mpv 的 `command("set", "audio-track", "1")` 通过 Node API 传字符串给需要整数的属性,返回 error -12(MPV_ERROR_COMMAND)。解决方案:改用直接属性赋值 `self._mpv.audio_track = int(track_id)`。 **5. 切换视频时播放/暂停按钮失效** 切换视频时状态在 LOADING → ENDED → PLAYING 间快速切换,按钮图标与实际状态短暂不同步。解决方案:新增 `set_loading()` 状态,加载中禁用按钮防止误操作。 **6. 打包后找不到 libmpv DLL** PyInstaller 单文件模式 DLL 解包到 `sys._MEIPASS` 临时目录,代码原来用 `sys.executable.parent` 找 DLL 路径导致找不到。解决方案:`get_runtime_mpv_dir()` 改用 `sys._MEIPASS`。 --- ## 许可证 GPLv3 ## 致谢 - [mpv](https://mpv.io/) — 媒体播放引擎,强大且开源 - [PySide6](https://doc.qt.io/qtforpython/) — Qt for Python,优秀的 GUI 框架 - [python-mpv](https://github.com/jaseg/python-mpv) — libmpv 的 Python 绑定 - [PyInstaller](https://pyinstaller.org/) — Python 打包工具 > AI生成