English | 中文
GitHub https://github.com/mzhsy1/MzDKPlayer | Gitee 镜像 https://gitee.com/mzhsy/MzDKPlayer | 官网 https://mzdkplayer.pages.dev/
MzDKPlayer 是一款本地音乐与视频播放器,分为电视端与手机端两个版本:电视端(Android TV)面向遥控器与大屏,手机端为触屏重做了整套交互。两者共用同一套业务层,支持弹幕功能、多种网络协议播放、音频视频格式播放与影视刮削。
- 🎬 视频播放 - 支持多种视频格式的本地与网络协议播放
- 🎵 音频播放 - 支持多种音频格式的本地与网络协议播放,歌词专辑封面显示与音乐信息,播放列表等常见功能
- 🖼️ 图片查看 - 支持多种图片格式的本地与网络协议查看
- 🏡 媒体库 - 包含电影/电视剧/音乐库,从 TMDB 或国内刮削源获取电影电视剧信息,支持批量添加
- 🕛 历史记录 - 播放历史记录,包含音视频
- 🔍 搜索功能 - 搜索电影/电视剧
- 💬 弹幕功能 - 支持 B 站风格弹幕显示与自定义
- ⚙️ 设置功能 - 较为详尽的应用与播放设置
- 🌐 网络协议支持:
- ✅ SMB 协议(当前已支持)
- ✅ FTP 协议(当前已支持)
- ✅ WebDAV 协议(当前已支持,其中如飞牛 NAS 提供的局域网 WebDAV 服务只支持 http,不支持 https,其他公网如阿里云正常支持)
- ✅ NFS 协议(当前已支持)
- ✅ HTTP 协议(当前已支持 NGINX 服务器)
- 🎚️ 多轨道选择 - 支持音轨、视频轨、字幕轨切换,倍数功能,音频软硬解码
- 🔤 字幕自定义 - 纯文本字幕(ExoPlayer)支持自定义字体、字号、颜色、背景色与底部边距
- 🔤 字幕时间轴微调 - 字幕整体提前或延后(0.5 秒步进,最多 ±30 秒);ExoPlayer 引擎下仅文本字幕生效,PGS 等图形字幕暂不支持
- 🧠 记住播放偏好 - 按文件记住音轨、字幕轨、倍速与画面比例,再次播放同一个文件时自动恢复
- 📱 手机扫码遥控 - 应用内可开启局域网遥控页,手机扫码即可当遥控器用
- 🌏 多语言界面 - 简体中文 / English / 日本語 / 繁體中文
同一套业务层,另外做了一个独立的手机 App(applicationId = org.mz.mzdkplayer.phone,可与电视端装在同一台设备上):
- 六种来源的文件浏览:本机 / SMB / FTP / NFS / WebDAV / HTTP,与电视端共用同一个业务层与刮削缓存
- 触屏播放页:拖动进度条定位、双击左右两侧快进 / 快退、底部面板切换音视频字幕轨与倍速、沉浸式全屏
- 触屏手势:左侧上下滑调亮度、右侧上下滑调音量、长按画面临时倍速(开关与档位都在手机端设置页里)
- 音乐与图片:内嵌 / 外挂
.lrc歌词、封面与播放列表;图片查看支持同目录左右滑动切换、双指缩放旋转 - 首页与设置:最近观看 / 最近添加 / 最近访问 + 顶栏「刮削本目录」入口;设置页覆盖电视端全部设置,另有手机专属项
MzDKPlayer 支持音频直通功能,可以将原始音频信号(源码)直接输出至功放、回音壁或支持多声道解码的电视,以获得影院级的听觉体验。
- 路径:
设置->音频设置->音频透传 (Passthrough) - 适用范围:此开关仅对 VLC 播放引擎生效。ExoPlayer 引擎会根据您的设备自动智能判断,无需手动干预。
- 使用建议:
- 默认状态:建议保持 关闭 (Off)。ExoPlayer 已能满足大部分设备的自动适配需求。
- 开启前提:仅当您拥有外接功放或高端音频解码设备,且确定其支持所播放视频的音频编码格式(如 DTS-HD, TrueHD)时再开启。
- 故障排查:若开启后播放时遇到无声情况,说明您的音频设备不支持当前视频的音轨格式(例如某些电视不支持 TrueHD 直通)。此时请务必关闭该开关,让播放器通过软件解码转换为 PCM 输出。
- 常用封装:MP4, MKV, MOV, AVI, WMV, FLV, WebM
- 蓝光/专业格式:ISO (蓝光原盘镜像), M2TS, MTS, TS, VOB
- 视频编码:H.264 (AVC), H.265 (HEVC), AV1, VP9, MPEG-2
- 特性支持:4K/8K 超高清播放、HDR10/HLG、杜比视界 (Dolby Vision)
- 无损/高保真:FLAC, WAV, ALAC (Apple Lossless)
- 通用格式:MP3, AAC, OGG, Opus, WMA
- 影院级音轨:DTS, DTS-HD, TrueHD, AC3 (Dolby Digital), E-AC3
- 标准格式:JPEG (JPG), PNG, WebP, BMP
- 现代格式:HEIC / HEIF
- 注:暂不支持 Apple Live Photo。
- 外挂字幕:SRT, ASS, SSA, VTT
- 内嵌字幕:MKV 内嵌字幕、PGS (蓝光原盘字幕)、DVB、Teletext
⚠️ 注意:TMDB 在国内可能需要代理或修改 Hosts 才能稳定访问,也可在设置 -> 刮削与媒体库 -> TMDB API 地址 (镜像)中填写镜像地址;或把设置 -> 刮削与媒体库 -> 刮削数据源改成「国内刮削源」(默认就是它,走公开接口、不需要 API Key)。
💡 小提示1:如果经常使用,建议在电视系统里把本播放器设为默认视频播放器,体验更顺滑。
💡 小提示2:如果设备性能不足,播放 70、80G 的原盘视频时开启弹幕可能会造成播放卡顿。
💡 小提示3:如果遇到使用 ExoPlayer(默认)播放器无法正常播放,可以尝试在
设置 -> 播放与视频 -> 默认播放器内核选择 VLC 播放器。
适合只想在电视上使用的用户,不需要任何开发环境。
📱 Releases 同时提供电视端(
app-tv-*-release.apk)与手机端(app-phone-*-release.apk)安装包;两端applicationId不同,可以同机安装。
-
打开 Releases 页面,下载最新版本的 APK。
-
根据电视芯片架构选择安装包(不确定就先用通用包):
安装包 适用设备 arm64-v8a绝大多数近几年的电视盒子 / 电视(晶晨 S905X3/S928X、MT9653 等) armeabi-v7a较老的低配盒子(如 S905L 等 32 位系统) universal(通用包)体积最大,兼容以上全部架构 -
把 APK 传到电视上安装(U 盘 / 当贝市场 / 电视自带文件管理器均可)。
如果通过电脑安装,推荐用 ADB:
# 电视上先开启「开发者选项 -> USB 调试 / 网络调试」
adb connect 192.168.1.100:5555 # 换成电视的实际 IP
adb install -r app-arm64-v8a-release.apk| 项目 | 要求 | 说明 |
|---|---|---|
| JDK | 17 或更高 | 源码与目标字节码均为 Java 17 |
| Kotlin 工具链 | JDK 21 | 项目声明了 jvmToolchain(21),本机没有时 Gradle 会自动下载 |
| Android SDK | compileSdk 37 | 需要装 Android SDK Platform 37 与 Build-Tools |
| Gradle | 9.7.1 | 用仓库自带的 gradlew 即可,无需单独安装 |
| Android Studio | 最新稳定版 | 可选,命令行也能完整构建 |
git clone https://github.com/mzhsy1/MzDKPlayer.git
cd MzDKPlayer在项目根目录新建 local.properties(此文件已在 .gitignore 中,不会入库),填写 SDK 路径与 TMDB API Key:
sdk.dir=D:\\Android\\Sdk
TMDB_API_KEY=你的TMDB_API_KEY
⚠️ TMDB_API_KEY是必填项。它会被写进BuildConfig.TMDB_API_KEY,缺失时项目仍能编译,但媒体库的 TMDB 刮削功能会失效。 API Key 可在 TMDB 开发者设置 免费申请,并在设置页将 API 地址指向可访问的镜像。 只想用国内刮削源的话,也可以在设置 -> 刮削与媒体库 -> 刮削数据源里选「国内刮削源」,它不需要任何 Key。
# Windows 用 gradlew.bat,macOS / Linux 用 ./gradlew
# 调试包(用 debug 签名,直接能装,日常自测用这个)
./gradlew :app:assembleTvDebug # 电视端
./gradlew :app:assemblePhoneDebug # 手机端
# 发布包(开启混淆与资源压缩,产物未签名)
./gradlew :app:assembleTvRelease
./gradlew :app:assemblePhoneRelease
# 只做编译校验,速度最快
./gradlew :app:compileTvDebugKotlin # 电视端;手机端把 Tv 换成 Phone电视端与手机端是同一 module 下的两个 product flavor(
tv/phone), 源码分别在app/src/tv/、app/src/phone/,业务层在独立的:coremodule。 它们的applicationId不同(org.mz.mzdkplayer/org.mz.mzdkplayer.phone),可以同时装在一台设备上。
产物路径:
| 命令 | 产物位置 |
|---|---|
assembleTvDebug / assemblePhoneDebug |
app/build/outputs/apk/tv/debug/app-tv-debug.apk、app/build/outputs/apk/phone/debug/app-phone-debug.apk |
assembleTvRelease |
app/build/outputs/apk/tv/release/app-tv-<abi>-release.apk、app-tv-universal-release.apk |
assemblePhoneRelease |
app/build/outputs/apk/phone/release/app-phone-<abi>-release.apk、app-phone-universal-release.apk |
发布包默认不签名,上架或分发前需要用
apksigner/ Android Studio 的 Generate Signed Bundle or APK 补上签名。 项目开启了 ABI 拆分(armeabi-v7a、arm64-v8a+ 通用包),所以 release 目录下会有多个 APK。 用向导签名时产物默认落在app/<flavor>/release/(已在.gitignore中忽略),别误提交进仓库。
adb connect 192.168.1.100:5555
adb install -r app/build/outputs/apk/tv/debug/app-tv-debug.apk./gradlew :core:testDebugUnitTest单元测试全部跟着业务层放在
:core,跑一次:core:testDebugUnitTest即可覆盖两端。
| 现象 | 原因与解决办法 |
|---|---|
Unable to delete directory ... a process has files open |
Android Studio 正开着并占用了 app/build 产物。关掉 Studio 再跑命令行,不要手动删 app/build |
找不到 TMDB_API_KEY / 刮削无效 |
local.properties 缺失或键名写错,注意不要写成 TMDB_KEY;或者把刮削数据源切成「国内刮削源」,它不需要 Key |
SDK location not found |
local.properties 中的 sdk.dir 没配,或在 Android Studio 里设置 SDK 路径 |
| Kotlin 工具链下载失败 | jvmToolchain(21) 需要联网下载 JDK 21;可先在本机安装 JDK 21 让 Gradle 直接复用 |
| 依赖拉取超时 | settings.gradle.kts 已配置阿里云/腾讯云镜像,可自行调整仓库顺序 |
fileHashes.lock 拒绝访问 |
先 ./gradlew --stop 确认守护进程已退出,再删除 .gradle/9.7.1/fileHashes/fileHashes.lock |
Android 视图里看不到 app/src/tv(或 phone) |
Android 视图只显示当前选中的构建变体的源集。到 View -> Tool Windows -> Build Variants 把 app 切成 tvDebug / phoneDebug 即可,切变体只影响 IDE 索引,不影响编译 |
- 首次启动会请求存储权限,在 Android 11+ 上选择 「允许管理所有文件」 体验最好(否则部分目录扫描不到)。
- 侧边栏「文件浏览」-> 本地文件 -> 选择分类(视频 / 音频 / 图片)。
- 进入目录后点击文件即可播放;播放器会自动加载同目录下的同名
.xml弹幕文件与同名字幕文件。
-
侧边栏进入 「文件浏览」,选择协议类型(SMB / WebDAV / FTP / NFS / HTTP)。
-
点击右上角 「新建连接」,填写地址、账号、密码(SMB 需填共享名,FTP 需填端口)。
-
保存后回到连接列表即可看到该存储,点进去像本地目录一样浏览、播放。
-
支持的 URI 形态(用于排查问题时参考):
协议 形态示例 SMB smb://user:pass@host/share/pathFTP ftp://user:pass@host:21/pathWebDAV https://user:pass@host/pathNFS nfs://host:/export:pathHTTP http://host/path(NGINX 目录列表)本地 file:///storage/emulated/0/Movies/a.mkv
所有网络协议连接都设置了超时,网络异常时会「软降级」,不会导致应用卡死或闪退。
- 弹幕文件为 B 站格式的
.xml,与视频放在同一目录、文件名相同即可自动加载。 - 播放时按遥控器 上键 打开弹幕设置,可调整字号、速度、透明度、显示区域等。
- 电影 / 电视剧信息有两个数据源:TMDB 与国内刮削源,在
设置 -> 刮削与媒体库 -> 刮削数据源里切换(国内刮削源 / TMDB),默认国内刮削源。选哪个就只刮哪个,不会用另一个源兜底 —— 两个源的信息口径不同(国内刮削源没有分集信息、没有上映状态),混着用会让同一批文件在媒体库里的展示不一致;直连环境下 TMDB 也不可达,兜底只会让每个文件白等一次超时。 - 国内刮削源不需要 API Key(走公开接口);TMDB 需要可用的网络与 API Key。
- 国内刮削源没有分集信息:选它时,剧集只会拿到剧集级信息(单集标题 / 简介 / 剧照为空,详情页会隐藏「当前单集详情」卡片)。更在意分集信息就把数据源切成 TMDB —— TMDB 的字段是它的超集(多出单集标题 / 简介 / 剧照、季数与集数、真实上映状态),代价是需要能访问
api.themoviedb.org;反过来它的条目对冷门国产剧 / 综艺 / 纪录片更全。 - 接口不可达时这一次按「没刮到」处理,不会重试。单次请求超时可调:
设置 -> 刮削与媒体库 -> 刮削超时时间(两个源共用,默认 5 秒,档位 3/5/8/10/15/30)—— 两个源正常时都是亚秒级响应,调小能让断网时的批量刮削快很多,网络慢或走镜像时可以调大。没有熔断:早期做过「连续失败就把这个源短路一段时间」,实测会误伤 —— 一次网络抖动就会让随后几分钟内本来能成功的请求直接失败。 - 两个源的编号不是同一套,刮削记录会带上来源标记:媒体库按来源分别分组,国内源条目与 TMDB 条目不会互相串。
- 手动匹配(「修改文件对应影视信息」/ 手机端「重新匹配」)两个源都能搜:页面上有「搜索来源」开关(TMDB / 国内刮削源,默认跟随设置里选的「刮削数据源」),点选的条目按它自己的来源写库;国内源条目不会因为编号与某部 TMDB 影片撞车而串到另一部片子上。
- TMDB 语言只有一个设置项(TMDB 语言):搜索与详情共用同一个值,留空则跟随系统语言。
- 若媒体目录下存在同名
.nfo文件,可在设置 -> 刮削与媒体库 -> 优先加载本地 NFO 文件打开优先读取,避免联网。 - 刮削结果会缓存到本地数据库,播放页标题会优先显示刮削片名(剧集自动追加
SxxExx与年份),无刮削记录时回退文件名。 - 批量扫描的递归层级可在
设置 -> 刮削与媒体库 -> 批量扫描子文件夹层级调整。
- 播放时按遥控器 菜单键 调出控制栏,进入字幕菜单切换内外挂字幕轨。
- 打开
设置 -> 字幕设置 -> 自动加载同名字幕后,播放时会自动扫描视频所在目录并加载同名字幕。 - 纯文本字幕(SRT/ASS 经 ExoPlayer 渲染)支持自定义字体、字号、颜色、背景色与底部边距;字体可从本地文件夹中挑选。
- 字幕与画面不同步时,用播放页浮层里的「字幕时间轴」按 0.5 秒为步进整体提前或延后(最多 ±30 秒);
设置 -> 字幕设置 -> 字幕时间轴是同一个值的另一个入口。注意 ExoPlayer 引擎下只对文本字幕生效,PGS 等图形字幕请改用 VLC 引擎。
- 打开界面上的遥控入口,应用会在局域网内启动一个轻量 HTTP 服务并显示二维码。
- 手机连同一个 Wi-Fi,扫码打开网页即可当作遥控器使用。
- 视频 / 音频播放页支持播放列表与播放完成后的动作(如自动切下一集)。
- 播放进度自动保存(每 10 秒落盘一次,退出播放页时再存一次),在 历史记录 中可继续上次进度。
| 按键 | 播放页功能 |
|---|---|
| 左右键 | 快进 / 快退(长按可连续快进快退;单击是否唤起底部控制栏可关) |
| 确认键 | 暂停 / 播放 |
| 菜单键 | 显示 / 隐藏控制栏(选轨、字幕、倍速、弹幕等) |
| 上键 | 弹幕设置(可在设置中改为其他功能) |
| 下键 | 音轨选择(可在设置中改为其他功能) |
| 返回键 | 退出播放 / 收起控制栏 |
上键与下键的行为可以在
设置 -> 遥控器与交互 -> 遥控器上键功能 / 遥控器下键功能中自定义:音轨 / 字幕 / 弹幕 / 视频轨 / 倍速 / 画面比例 / 底部进度条 / 播放列表。选「底部进度条」时按一下就把底部进度条叫出来,不开右侧设置面板;选「播放列表」时直接打开右侧播放列表。
底部控制栏里除了进度条,还有「上一个视频 / 暂停播放 / 下一个视频」三个按钮:控制栏可见时按「下」把焦点挪到按钮行,左右键在三个按钮之间切换,按「上」交回进度条(此时左右键才是快进快退)。
暂停时右上角显示当前时间(播放中同一位置显示实时网速),长时间暂停回来一眼就能看到几点了。
单击左右键默认会弹出底部进度条(连带整屏渐变压暗);只想快进快退、不想画面变暗时,可以在
设置 -> 遥控器与交互 -> 左右键唤起底部进度条中关掉,关闭后单击只跳步、不弹进度条,长按的迷你进度提示不受影响。
在首页按返回键不会直接退出:第一次只提示「再按一次退出」,2 秒内再按一次才真正退出;其它页面按返回键仍是先回上一级。
手机端没有遥控器,播放页全部为触屏操作:
| 手势 | 作用 |
|---|---|
| 左侧上下滑 | 调节亮度 |
| 右侧上下滑 | 调节音量 |
| 长按画面 | 临时倍速播放(档位可在设置里调) |
| 双击左侧 / 右侧 | 快退 / 快进 |
| 拖动进度条 | 定位播放位置 |
手势开关、长按倍速档位、控制栏自动隐藏秒数等都在手机端设置页里。
| 分类 | 选型 | 版本 |
|---|---|---|
| 语言 | Kotlin | 2.4.20 |
| 构建 | Gradle / AGP / KSP | 9.7.1 / 9.4.0 / 2.3.9 |
| 界面 | Jetpack Compose:电视端用 Compose for TV(tv-foundation / tv-material),手机端用 Material 3 + Navigation Compose |
Compose BOM 2026.09.00 / Navigation 2.9.8 |
| 播放内核 | Media3 ExoPlayer + libVLC(双引擎,统一抽象层) | 1.11.1 / 3.7.7 |
| 弹幕 | AKDanmaku + libGDX / Ashley | — |
| 数据库 | Room 3(androidx.room3,跑在 SQLiteDriver 上) |
3.0.3 |
| 偏好存储 | DataStore Preferences | 1.2.1 |
| 网络 | smbj (SMB) / commons-net (FTP) / sardine (WebDAV) / nfs-client (NFS) / OkHttp | — |
| 图片加载 | Coil 3 | 3.6.3 |
| 内置服务 | NanoHTTPD(本地代理 + 手机遥控页)、ZXing(二维码) | — |
| 元数据 | Retrofit + Gson(TMDB)、OkHttp + Gson(国内刮削源)、jaudiotagger(音频标签) | — |
工程分成一个业务层 module(:core)+ 一个带两个 product flavor 的应用 module(:app):
core/src/main/java/org/mz/mzdkplayer/ # :core —— 业务层,tv / phone 共用
├── danmaku/ # 弹幕解析
├── data/
│ ├── api/ # TMDB 接口(Retrofit)
│ ├── douban/ # 国内刮削源接口(OkHttp):搜索、详情、剧照
│ ├── local/ # Room:AppDatabase、MediaCacheEntity、AudioCacheEntity、MediaHistoryEntity
│ ├── model/ # 数据模型
│ └── repository/ # 仓库层,屏蔽数据来源
├── di/ # RepositoryProvider(以 viewModelWithFactory 注入 DAO)、AppContext
├── player/
│ ├── core/ # IMzPlayer 抽象、轨道模型、数据源工厂
│ ├── exo/ # MzExoPlayer 实现
│ └── vlc/ # MzVlcPlayer 实现
├── tool/ # 工具层:协议 DataSource、字幕扫描、时间解析、局域网代理与遥控服务
│ └── logic/ # 纯逻辑(可 JVM 单测):文件名解析、协议 URI、刮削数据源单选与超时档位等
└── viewmodel/ # 各页面的 ViewModel
app/src/main/java/org/mz/mzdkplayer/
└── MzDkPlayerApplication.kt # 两个 flavor 共用的 Application(含国内源图床的 Coil 配置)
app/src/tv/java/org/mz/mzdkplayer/ # 电视端 flavor(只用 androidx.tv.material3)
├── MainActivity.kt / LaunchScreen.kt # LEANBACK_LAUNCHER 入口
└── ui/
├── MzDKPlayerAPP.kt # 导航图与主框架
├── videoplayer/ # 播放页(components/ 下为控件、标题、弹层)
├── audioplayer/ # 音频播放页
├── picviewer/ # 图片查看
├── screen/ # 各页面:filehome / localfile / smbfile / ftp / webdavfile / nfs /
│ # httplink / library / movie / tv / history / search / setting
└── theme/ # 电视端主题
app/src/phone/java/org/mz/mzdkplayer/ui/phone/ # 手机端 flavor(只用 androidx.compose.material3)
├── PhoneMainActivity.kt # LAUNCHER 入口(applicationId = org.mz.mzdkplayer.phone)
├── PhoneApp.kt # 底部导航 + 导航图
├── PhoneTheme.kt / PhoneIcons.kt / PhoneRoutes.kt
└── screen/ # 首页 / 文件 / 各协议浏览 / 播放 / 设置
两套设计系统零交叉::core 不依赖任何 Material 库,电视端只引 androidx.tv.material3(compose 1.12.x),
手机端只引 androidx.compose.material3 1.5.0-alpha(会带进 compose 1.13.0-alpha),依赖按 variant 解析、互不污染。
core/src/test/java/org/mz/mzdkplayer/ 下为纯 JVM 单元测试。
播放逻辑通过 player/core/IMzPlayer.kt 抽象,MzExoPlayer(默认,硬件解码友好、启动快)与 MzVlcPlayer(格式兼容性更广、支持直通)各自实现。上层界面只依赖接口,因此可以在 设置 -> 播放与视频 -> 默认播放器内核 中随时切换,也便于继续接入新的内核。
VideoPlayerScreen- 主播放器界面BuilderMzPlayer- 播放器构建与配置AkDanmakuPlayer- 弹幕播放组件MovieDetailsScreen/TVSeriesDetailsScreen- 电影/电视剧详情页面FullDescriptionDialog- 详细简介弹窗LocalProxyServer/ProxyManager- 本地 HTTP 代理,解决部分协议与 ExoPlayer 的兼容问题RemoteInputServer/RemoteInputQRPanel- 手机扫码遥控
- Room 数据库
AppDatabase包含三张表:media_cache(影视刮削缓存,主键为videoUri)、音频缓存、播放历史。 - 列表页传给播放页的 URI 与
media_cache.videoUri保持一致,播放页可直接按 URI 命中刮削缓存,无需重复联网。 - 导航参数(URI、文件名、连接名等)统一走 Base64 编码,避免路径中的特殊字符破坏路由。
- 刮削记录带来源列(
tmdb/douban):国内源条目编号与 TMDB 编号的数值区间是重叠的,来源标记用于媒体库分组与详情页判断,避免两部不相干的片子被并成一张卡片、或拿国内源编号去查 TMDB。
./gradlew :app:compileTvDebugKotlin # 只编译,最快的语法校验
./gradlew :app:assembleTvDebug # 打调试包
./gradlew :app:assembleTvRelease # 打发布包(未签名)
./gradlew :app:compilePhoneDebugKotlin # 手机端只编译
./gradlew :core:testDebugUnitTest # 跑全部 JVM 单元测试
./gradlew :core:testDebugUnitTest --tests "org.mz.mzdkplayer.tool.PlayerMediaTextTest" # 跑单个测试类
./gradlew clean # 清理构建产物
./gradlew --stop # 停掉 Gradle 守护进程(锁文件冲突时用)- 测试只依赖 JUnit 4,没有引入 mockito / robolectric,也没有开启
returnDefaultValues。 因此测试必须写成纯 JVM 测试:android.net.Uri、android.util.Log、android.util.Base64、Context等 Android 类型一旦调用就会抛RuntimeException("Stub!")。 - 需要测试的纯逻辑请抽到不依赖 Android 的
object/internal object中;测试与被测代码同在:core(internal对 app 不可见,这条边界是有意保留的)。 - 命名约定:类名
XxxTest,测试函数用反引号中文描述,例如`剧集 - 标题加季集加年份`。 - 现有 31 个测试类、510 个用例,全部集中在
:core。较有代表性的:MediaInfoExtractorFormFileNameTest(文件名解析)、PlayerMediaTextTest(播放页标题与日期)、HttpDirectoryListingTest/FileTimeParseTest(HTTP 目录页与日期解析)、ProtocolUriParserTest/SidecarPathTest(协议 URI 与伴生文件路径)、StreamSizePolicyTest(各协议的流读取策略)、Phone*Test(手机端纯逻辑)、DoubanLogicTest/ScrapeSourcePolicyTest/ScrapeTimeoutPolicyTest(国内源字段归一化、数据源单选、刮削超时档位)。完整清单见core/src/test/。
项目使用 logback-android;网络协议类实现(SMB / FTP / WebDAV / NFS)都配置了超时,失败时会「软降级」返回空列表或回退本地读取,不会中断播放流程。
- 芯片组:Amlogic S928X-J
- 内存:4GB RAM及以上
- 系统:Android TV 11及以上
- 芯片组:MT9653或同等性能芯片
- 内存:2GB RAM
- 系统:Android TV 7及以上
- 芯片组:晶晨S905L或同等性能芯片
- 内存:1GB RAM
- 系统:Android 6.0 (API 23) 及以上(
minSdk = 23),遥控操作体验需要电视 / 盒子的方向键支持
📱 手机端:
minSdk = 24(Android 7.0 及以上)—— 比电视端高一级,因为手机端的 Material 3 Expressive 依赖里material3-ripple自身要求 24;全部为触屏操作、不需要遥控器;applicationId与电视端不同,两者可以同时安装。
⚠️ 注意:代码写的烂,不会优化,能跑就成功,都是bug,设备性能不足可能导致视频与弹幕播放卡顿,或无法正常播放高码率视频
- FTP协议支持
- WebDAV协议支持
- NFS协议支持
- 音频文件,图片文件支持
- 播放列表管理
- 电影/电视剧详情页面
- 电视端 / 手机端拆分(
:core业务层 +tv/phone两个 flavor,共用一套业务逻辑) - 手机端:文件浏览 / 刮削与详情页 / 视频播放页(手势与轨道面板)/ 设置页 / 音乐 / 图片
- 国内刮削源与「刮削数据源」单选(含可调超时)
- 网络弹幕加载功能
- 设置界面优化
完整的版本变更记录见 CHANGELOG.md。
欢迎任何形式的贡献,尤其欢迎对 播放器稳定性 的改进!
提交 Issue 时请尽量包含:设备型号与芯片、系统版本、应用版本(设置 -> 关于软件)、使用的协议类型(SMB / FTP / WebDAV / NFS / HTTP 或本地)、复现步骤,以及一段 adb logcat 抓取的日志。带日志的 Issue 通常能很快定位。
刮削相关的问题请一并说明数据源:
设置 -> 刮削与媒体库 -> 刮削数据源选的是国内刮削源还是 TMDB,以及该文件最终落到哪个来源。
-
Fork 本仓库,从
main分支切出功能分支,建议命名:前缀 用途 示例 feat/新功能 feat/nfs-reconnectfix/缺陷修复 fix/vlc-audio-crackledocs/文档 docs/readme-usagerefactor/重构 refactor/player-engine-interface -
提交前先在本地跑通编译与测试:
./gradlew :app:compileTvDebugKotlin ./gradlew :app:compilePhoneDebugKotlin ./gradlew :core:testDebugUnitTest
-
提交代码,向
main发起 Pull Request,并在描述中说明:改了什么问题、怎么验证的、涉及哪些模块。
沿用仓库现有的中文描述式提交,一句话说清「改了什么」,多个改动点用逗号分隔:
播放页显示刮削标题与文件日期,播放进度定时落盘,重写文件名解析
- 遵循 Kotlin 官方代码风格(
kotlin.code.style=official)。 - 业务逻辑一律放
:core,且不要往里引 Material:电视端唯一的设计系统是androidx.tv.material3,手机端是androidx.compose.material3,两者不得交叉。 - 电视端新增页面请在
app/src/tv/java/org/mz/mzdkplayer/ui/screen/下建对应包,并在MzDKPlayerAPP.kt中注册路由;手机端页面放app/src/phone/java/org/mz/mzdkplayer/ui/phone/screen/,路由在PhoneRoutes.kt。 - 需要访问数据库的 ViewModel 请通过
viewModelWithFactory { RepositoryProvider.xxx() }注入,不要在 Composable 里直接拿 DAO。 - 网络协议相关代码必须设置超时,失败时走「软降级」,不能阻塞播放或抛到界面线程。
- 涉及播放内核的改动,请在
IMzPlayer层面实现,不要在 UI 层写if (exo) ... else ...。 - 界面文案不要硬编码,加到
res/values/strings.xml,并同步补齐values-en/values-ja/values-zh-rTW。 - 修改了用户可见行为,请同步更新 CHANGELOG.md 与
app/build.gradle.kts中的versionName。
-
./gradlew :app:compileTvDebugKotlin/./gradlew :app:compilePhoneDebugKotlin通过(两个 flavor 都要能编) -
./gradlew :core:testDebugUnitTest通过(有新增纯逻辑时补测试) - 没有提交
local.properties、app/build/、.gradle/、.idea/、app/<flavor>/release/(「生成签名 APK」向导的输出目录)等本地文件 - 新增文案已加入各语言
strings.xml - 用户可见的改动已更新
CHANGELOG.md(必要时更新versionName) - 在真机 / 电视上实际验证过(说明设备与系统版本)
app/libs/ 下的 akdanmaku.aar 与 lib-decoder-ffmpeg-release*.aar 是播放器依赖的本地预编译库,已随仓库提供,无需自行构建,也不要删除。
仓库里有两个工作流,不需要手动打包:
| 工作流 | 触发时机 | 做什么 |
|---|---|---|
ci.yml |
推送到 main、每个 PR |
编译两个 flavor + 跑 JVM 单元测试,测试报告作为 artifact 上传 |
release.yml |
推送到 main(版本号是新的)、推送 V* tag、手动触发 |
跑测试 → 构建电视端签名 APK → 创建 Release 并上传电视端的三个包(universal / arm64-v8a / armeabi-v7a);手机端包目前是手动构建后一并附上 |
发布是版本号驱动的:release.yml 会读取 app/build.gradle.kts 里的 versionName,如果还没有对应的 V<版本号> Release,就自动构建并发布。也就是说,按约定 bump 完 versionName 并推送,Release 就自动出来了,不需要手动打 tag(tag 会由工作流创建,沿用历史的 V1.17.4 这种大写 V 前缀)。
Release 正文取自 CHANGELOG.md 顶部的 ## [未发布] 段落(工作流按这个标题取正文,改名会让正文取空),所以发布前记得先把变更写进这一段的顶部。
发布需要以下 Secrets(Settings → Secrets and variables → Actions),缺任意一项工作流会跳过发布并在运行摘要里列出缺什么:
| Secret | 用途 |
|---|---|
TMDB_API_KEY |
打包时写入 BuildConfig.TMDB_API_KEY,空值会让 TMDB 刮削静默失效(国内刮削源不依赖它) |
RELEASE_KEYSTORE_BASE64 |
签名密钥库的 base64:base64 -w0 release.jks |
RELEASE_KEYSTORE_PASSWORD |
密钥库口令 |
RELEASE_KEY_ALIAS |
密钥别名 |
RELEASE_KEY_PASSWORD |
密钥口令 |
签名材料只通过环境变量传给 Gradle(MZDK_KEYSTORE_FILE 等),密钥文件不要提交到仓库。本地没有配这些变量时,assembleTvRelease 会照旧产出 app-tv-*-release-unsigned.apk,行为与以前一致。
想重新发布同一个版本:到 Actions 页面手动运行 Release,并把 force 勾上。
本软件仅供学习交流使用,请勿用于商业用途。使用本软件造成的任何问题,开发者不承担相关责任。
本项目基于 GNU General Public License v3.0 开源。
注意:杜比视界、杜比全景声、DTS-HD等功能的正常使用需要设备硬件支持,部分功能可能需要特定的音频视频设备才能获得最佳体验。











