Important
本项目全部由 AI 完成。 从技术选型、架构设计、代码编写、调试排错,到文档撰写与发版说明,全部由 AI 独立完成;人类只负责提出需求、提供测试设备、验收结果。 仓库里的每一行代码、每一份文档都出自 AI 之手,没有一行是人工手写的。 详见文末 关于 AI 参与。
更名说明:本项目原名 LocalEmby(本地影院),自 2.6.0 起统一更名为 Embylite。 为不影响老用户升级,包名仍保留
com.local.embylite(Android 以包名识别应用,改包名会导致新旧版本无法覆盖安装、且已保存的设置与播放进度丢失),因此桌面图标名称显示为 Embylite,而系统内部标识依旧是com.local.embylite。 GitHub 仓库与历史 Release 仍沿用原地址github.com/Dylan-1107/LocalEmby,上方下载链接不受影响。
直接下载(永久链接,始终指向最新版)
https://github.com/Dylan-1107/LocalEmby/releases/latest/download/LocalEmby.apk
历史版本见 Releases。
装好后打开 Embylite → 授权视频目录(选择文件夹或授予全部文件访问)→ 启动服务器。与手机同一 Wi-Fi 的设备,请在 Emby 客户端添加 App 显示的“局域网”地址(例如 http://192.168.1.23:8096);只有本机客户端使用 http://127.0.0.1:8096,用户名密码随意填。
底部导航分为四项:首页、视频信息、日志、设置。
- 首页:只保留服务器状态、访问地址、启停/复制/刷新、服务数值与运行指标
- 视频信息:独立一页,固定头部(弹幕绑定 + 元数据刮削两组统计 + 筛选页签),下方是独立滚动的结果列表,逐文件查看与确认候选
- 设置:媒体目录、服务端口、开机自启、Root 保活、弹幕源、TMDB 刮削
| 首页 | 弹幕识别 |
|---|---|
![]() |
![]() |
| 设置 | 请求日志 |
|---|---|
![]() |
![]() |
把手机里的本地视频目录,变成一个支持本机和局域网访问的 Emby 服务器。装好之后,本机客户端添加 http://127.0.0.1:8096,同一 Wi-Fi 的其他设备添加 App 显示的局域网地址,就能像浏览真 Emby 一样浏览、播放本地视频,并且播放进度会记忆、可续播,还支持自动匹配弹幕。
本项目是为了解决一个具体问题:想在 Emby 客户端里看手机本地视频,又不想为此专门跑一台 NAS / Jellyfin。它在手机本地模拟 Emby Server 的 HTTP API,零依赖、零转码,全部直连播放。
-
HyperOS 风格界面:基于 Miuix(Compose Multiplatform UI 库)重写,卡片式布局、系统级观感;底部导航切换首页 / 弹幕 / 日志 / 设置,弹幕识别与设置各自独立成页
-
本地目录即媒体库,两种授权方式:
- 选择文件夹(SAF):逐个目录授权,可持久化、不需要危险权限;Android 11+ 系统不允许选存储根目录,所以只能选具体文件夹
- 所有文件访问权限:在系统设置里一次性授予
MANAGE_EXTERNAL_STORAGE,之后直接扫全盘,无需选目录(本项目不上架 Play,不受其政策限制)
-
Emby API 模拟:实现登录、媒体库、条目列表/详情、PlaybackInfo、图片、视频流(HTTP Range 拖动)、播放进度上报等接口
-
续播与进度记忆:通过 Emby 的
PlaybackPositionTicks协议上报,客户端原生「继续观看」生效;多个客户端共享同一份进度 -
剧集识别:结合父目录名与文件名解析「剧名 / 季 / 集」(
SxxExx、第N集、EPxx、纯数字等),区分「明确季」「未分季」「特别篇」,并把多集合并、上下篇等文件标记为命名有歧义,不参与自动绑定 -
剧集结构浏览:Emby 客户端里按「剧集 → 季 → 集」层级浏览(
/shows/{id}/Episodes、/shows/nextup、/items),不再把所有文件摊平成电影 -
自动弹幕:客户端请求
/api/danmu/{itemId}时,按剧集身份(剧名 + 季 + 集)匹配弹弹play 兼容弹幕源,并对候选做二次校验,避免错绑同名剧集;绑定结果带指纹缓存(弹幕源 / 路径 / 大小 / 身份),文件或配置变化会自动失效重识 -
识别结果可视化:「视频信息」页顶部为 HyperOS 设置风格分组卡片(开关 / 锚定下拉 / 进度条),展示弹幕绑定与元数据刮削的进度、已处理 / 待确认 / 失败统计,可按状态筛选;列表项展开可查看详情、确认候选、重新搜索(可改关键词)或清除结果——刮错了只纠错那一个文件
-
增量识别与刮削:识别/刮削任务分三种模式——增量刷新(默认,只处理新增与失败重试)、重试失败与待确认、全量重建(需二次确认,人工锁定结果不覆盖);待确认/未命中条目保持安静,不再每次扫描反复请求;失败按指数退避重试;支持中途取消
-
剧集级刮削聚合:同一部剧只做一次 TMDB 搜索 / 详情 / 海报下载,分集共享(请求量降 ~95%);一部剧确认候选后同剧其他待确认集自动继承;
tvshow.nfo与剧集海报每剧只写一次 -
媒体库索引增量:扫描结果持久化索引,重启 / 刷新只 diff——新增/变化的文件才探测时长并入队,消失的文件自动清理状态;文件未变时秒出,不再全量重扫
-
元数据刮削(TMDB):扫描后自动按文件名 / 剧名搜索 TMDB(zh-CN),唯一命中自动刮削,多候选进「待确认」人工选择;结果写入元数据覆盖层(标题、简介、年份、评分、演职人员、海报),并尽力生成 Kodi 兼容 NFO(
<视频名>.nfo/tvshow.nfo/movie.nfo)与poster.jpg落盘到视频目录(SAF 或只读目录时自动回退应用私有目录);本地已有 NFO 的文件直接导入、不走网络;刮削状态带指纹缓存(Key / 路径 / 大小 / 身份),文件或配置变化自动重刮。TMDB Key 默认内置公共 Key 开箱即用,可在「设置 → 视频信息」替换为你自己的(themoviedb.org 免费申请) -
刮削元数据:支持通过
/api/dev/metadata注入标题、简介、年份、评分、演职人员、海报 -
后台常驻:前台服务 + WakeLock + 开机自启(仅在你主动开启过服务器时恢复)
-
Root 保活(可选):设备已 root 时,可在「设置」页开启;开启后每次启动服务器会用
su把本应用加入电池优化白名单、解除后台限制并拉起前台服务,降低被 ROM 杀后台的概率。未 root 或拒绝授权时自动跳过,不影响正常使用;也可随时手动触发一次 -
请求日志:App 内可查看全部请求(底部「日志」页),便于排查客户端兼容问题
app/
src/
AndroidManifest.xml
com/local/embylite/
# ---- Compose UI(沿用原有卡片样式)----
MainActivity.kt # 导航壳 + 首页内容(HomeContent)+ 底部导航
VideoInfoPage.kt # 视频信息页:弹幕绑定 + 元数据刮削统计/筛选/候选确认
SettingsPage.kt # 设置页:媒体目录、端口、开机自启、Root 保活、弹幕源、TMDB 刮削
LogActivity.kt # 请求日志页
# ---- 服务端逻辑(Java)----
EmbyHttpServer.java # 原生 HTTP 服务器:路由、Range 流媒体、弹幕/元数据接口
EmbyApi.java # Emby 协议层:System/Users/Items/PlaybackInfo/Sessions 等
VideoLibrary.java # 媒体库扫描(SAF / MediaStore / 目录遍历)
Item.java # 条目模型 + 文件名剧集解析(SxxExx、第N集、EPxx)
EpisodeIdentity.java # 剧集身份解析:父目录 + 文件名 → 剧名/季/集,标记歧义与特别篇
EpisodeMatcher.java # 弹幕剧集绑定:搜索、校验、缓存(带指纹)、进度
EpisodeMatchPolicy.java # 候选校验策略(纯 Java,独立回归测试)
ScrapeTitle.java # 文件名 → 搜索标题 + 年份(纯 Java,独立回归测试)
ScrapePolicy.java # TMDB 候选接受策略(纯 Java,独立回归测试)
NfoData/NfoWriter/NfoParser.java # Kodi 兼容 NFO 模型/生成/解析(纯 Java)
TmdbClient.java # TMDB API v3 客户端(搜索/详情/分集/海报)
VideoInfoService.java # 元数据刮削编排:TMDB → 覆盖层 + NFO/海报落盘(带指纹)
DanmuNetwork.java # 弹幕源网络层(OkHttp + HTTPS DNS 兜底)
ProgressDb.java # 播放进度存储(Emby ticks)
MetadataStore.java # 刮削元数据覆盖层
DanmuProxy.java # 弹幕源代理(match + comment)
ThumbProvider.java # 视频抽帧缩略图 / 占位图
RootKeepAlive.java # Root 保活:su 白名单 + 解除后台限制(可选,需用户显式开启)
HttpService.java # 前台服务
Settings.java # 偏好设置
BootReceiver.java # 开机恢复
res/ # 布局与资源(strings.xml 中 app_name = Embylite)
assets/web/index.html # 浏览器调试页(列表 + 播放 + 进度上报)
tests/
EpisodeRegression.java # 剧集解析 / 候选校验回归测试(纯 Java,无需设备)
docs/ # README 截图
需要 JDK 17 与 Android SDK(compileSdk 36 / build-tools 36.0.0)。
# 1. 配置 SDK 路径
echo "sdk.dir=/path/to/android-sdk" > local.properties
# 2. 打包
./gradlew assembleRelease
# 产物:app/build/outputs/apk/release/app-release.apk签名默认使用仓库根目录的 debug.keystore(signingConfigs.local),正式发布请替换为你自己的密钥。
技术栈:AGP 8.13.2 · Kotlin 2.3.20 · Compose Multiplatform 1.10.3 · Miuix 0.8.8 · minSdk 26 / targetSdk 36 · 版本 2.8.0(versionCode 13)。
剧集解析与刮削核心(身份解析 / 标题净化 / 候选校验 / NFO 读写)都是纯 Java 逻辑,改完规则先跑回归,不需要设备或 Gradle:
# 剧集解析 / 弹幕候选校验
javac -encoding UTF-8 -d build/regress \
app/src/com/local/embylite/EpisodeIdentity.java \
app/src/com/local/embylite/EpisodeMatchPolicy.java \
app/src/com/local/embylite/Item.java \
tests/EpisodeRegression.java
java -cp build/regress com.local.embylite.EpisodeRegression
# 期望输出:PASS 38 episode identity and candidate regression cases
# 刮削核心(标题净化 / TMDB 策略 / NFO 读写)
javac -encoding UTF-8 -d build/scrape \
app/src/com/local/embylite/EpisodeIdentity.java \
app/src/com/local/embylite/ScrapeTitle.java \
app/src/com/local/embylite/ScrapePolicy.java \
app/src/com/local/embylite/NfoData.java \
app/src/com/local/embylite/NfoWriter.java \
app/src/com/local/embylite/NfoParser.java \
tests/ScrapeRegression.java
java -cp build/scrape com.local.embylite.ScrapeRegression
# 期望输出:PASS 22 scrape regression cases- 安装 APK,打开「Embylite」
- 在首页点「视频目录」卡片,选择文件夹授权视频目录(也可授予「所有文件访问权限」直接扫全盘)
- 点「启动」,界面会显示本机地址和局域网地址(点「复制」会复制局域网地址)
- 在同一 Wi-Fi 的 Emby 客户端里添加服务器:填写界面显示的局域网地址,例如
http://192.168.1.23:8096;如果客户端就在手机本机,再填写http://127.0.0.1:8096。用户名密码随意填 - 点底部「弹幕」进入弹幕识别页,可查看识别进度、逐文件绑定状态并确认候选
- 浏览播放即可,进度自动记忆
局域网内其他设备也可以访问 http://<手机IP>:8096(同 WiFi)。
客户端会向服务器请求 /api/danmu/{itemId}?option=GetJsonById。服务端先建立「文件 → 弹幕剧集」绑定,再取该剧集的弹幕:
POST {base}/api/v2/match {"fileName":"..."} → matches[0].episodeId
GET {base}/api/v2/comment/{id} ?withRelated=true → {count, comments:[{p,m}]}
绑定不是简单取 matches[0]:EpisodeMatchPolicy 会用本地解析出的「剧名 / 季 / 集」去校验每个候选的标题与集号,只有剧名归一化后一致、集号相同、季信息不冲突才接受。
- 明确季的文件(
S02E03、第二季第3集)必须有季证据才能绑定,避免第二季被绑到第一季 - 未分季的文件只会绑定到同样未分季的候选;特别篇(
S00)不会绑到正片 - 命名有歧义的文件(
S01E01-E02合集、上下篇等)不参与自动绑定,需拆分后重识 - 绑定结果按「弹幕源 + 路径 + 大小 + 解析身份」生成指纹缓存,任一变化即失效重识;「弹幕」页可查看进度、确认候选、重新识别
弹幕源地址在 Settings.getDanmuApi() 中配置(默认值见 DanmuProxy.DEFAULT_API,可自行替换为你部署的弹弹play 兼容服务)。网络层(DanmuNetwork)在系统 DNS 失败时走 DoH 兜底,同时保留原始主机名、SNI 与证书校验。
弹幕内容来自第三方服务,本项目不存储、不提供任何弹幕内容。
| 接口 | 说明 |
|---|---|
GET /System/Info[/Public] |
服务器信息 |
POST /Users/AuthenticateByName |
登录(任意账号密码),返回 AuthenticationResult |
GET /Users/{uid}/Views |
媒体库视图 |
GET /Users/{uid}/Items |
条目列表(分页、排序、搜索、ParentId 过滤) |
GET /Users/{uid}/Items/Latest、/Items/Resume |
最新 / 继续观看 |
GET /Users/{uid}/Items/{id} |
条目详情(含 Series / Season 虚拟条目) |
GET /Shows/{seriesId}/Episodes、/Shows/{seriesId}/Seasons |
剧集 → 季 → 集层级浏览 |
GET /Shows/NextUp |
下一集 |
POST /Items/{id}/PlaybackInfo |
播放信息(直连播放,缺失时长时自动探测) |
GET /Videos/{id}/stream |
视频流,支持 HTTP Range |
GET /Items/{id}/Images/Primary |
缩略图 / 刮削海报(Series / Season 复用代表帧) |
| `POST /Sessions/Playing[/Progress | /Stopped]` |
GET /api/danmu/{id} |
弹幕代理(按剧集绑定) |
GET /api/episodes/status |
剧集识别进度与每个文件的绑定状态 |
POST /api/dev/metadata |
注入刮削元数据 |
GET /api/dev/log |
请求日志 |
- 不做转码:只提供直连播放,客户端需自行支持文件编码(Hills / YAmby 内置 mpv,支持良好)
- 无用户体系:任意账号密码都能登录,仅建议本机或可信局域网使用
- 无 WebSocket:部分客户端的实时会话推送不可用,不影响播放
- APK 体积约 7MB:Compose 运行时占用较大
MediaStreams为占位信息:部分客户端会显示固定的 1080p H.264 / AAC,实际轨道由播放器自行探测- 小米等 ROM 对后台限制较严,若服务器被杀,可在系统设置里允许自启动并锁定后台
这个项目全部由 AI 完成。
Embylite 不是一个"AI 辅助写了几段代码"的项目,而是从零到发版全程由 AI 驱动:需求由人类提出,其余全部由 AI 落地。
| 环节 | 由谁完成 |
|---|---|
| 提出需求、决定功能取舍、最终验收 | 人类(项目所有者) |
| 技术选型与架构设计 | AI |
| 全部 Java / Kotlin 代码编写 | AI |
| 调试排错、真机联调(adb / logcat / 截图逐帧比对) | AI |
| 剧集解析、刮削策略的回归测试用例 | AI |
| README、交接文档、Release 说明 | AI |
人类在这个项目里不写代码,只做三件事:说清楚想要什么、提供一台真机供验证、对结果说"行"或"不行"。所有代码、所有文档、所有排查过程都出自 AI。
这样做的好处是迭代速度:一个功能从"提需求"到"真机验证通过"通常在一次对话内完成。代价是每个结论都必须实测——所以仓库里才会有一堆回归测试和 docs/BUILD-HANDOFF.md 这样的交接文档,让下一个人(或下一个 AI)能独立接续。
- 弹幕:弹幕通过弹弹play 兼容接口获取,服务端基于开源项目 danmu_api(作者 huangxd-)自部署,仓库内置的默认弹幕源即该项目的一个部署实例。弹幕内容版权归原平台及发布者所有。
- 元数据刮削:影视元数据(标题、简介、评分、演职人员、海报等)来自 TMDB 的公开 API。This product uses the TMDB API but is not endorsed or certified by TMDB.
本项目仅用于管理和播放你自己拥有的本地媒体文件,不提供、不索引、不托管任何影视内容。刮削与弹幕数据来自第三方公开服务,请遵守相应服务条款与当地法律法规。



