一个 Android 播放器,支持 ed2k:// 与 magnet: 链接的边下边播(下载未完成即可开始播放)。
不需要 115 会员,不需要先把文件下完,也不需要装 eMule。
资源站提供的下载方式通常是 ed2k 或网盘。网盘要会员,ed2k 链接又无法被普通播放器直接打开—— 必须先经 eMule / aMule 之类的 P2P 客户端完整下载,才能播放。
这个 App 把整条链路收进一个安卓应用:
- 粘贴 ed2k / magnet 链接(或从系统分享菜单、浏览器点
ed2k://、magnet:直接唤起) - 内置下载引擎自动开始下载
- 下载到设定比例后自动开播,边下边看
两个容易被忽略的坑,也是本项目大部分复杂度的来源:
1. 分片乱序 + 空洞
P2P 下载是按分片(piece)乱序完成的,文件会被预先分配成完整长度,未下载的区域全是 0。
播放器直接读这个文件,会把空洞里的 0 当成有效数据,结果是容器解析失败,或者一直转圈缓冲。
解决方式:内置一个本地 HTTP Range 服务(stream/LocalStreamServer.kt),把"分片可用性位图"
翻译成标准 HTTP 语义——已下载区域立即返回,未下载区域先挂住连接等下载追上(默认最多等 10 分钟)。
播放器看到的就是一个"长度固定、按需变慢"的正常 HTTP 资源,Seek 也自然可用。
2. 头部优先
容器(尤其是 mp4 的 moov)通常在文件开头,播放器一上来就要读它。
所以给引擎传了 --bt-prioritize-piece=head=8M,tail=1M,优先拿首尾分片,开播时间因此大幅提前。
┌──────────────────────────────────────────────────┐
│ UI MainActivity / PlayerActivity / Downloads │
├──────────────────────────────────────────────────┤
│ LocalStreamServer ← 把分片位图翻译成 HTTP Range │
│ StreamAvailability ← 分片可用性视图 │
├────────────────────────┬─────────────────────────┤
│ Aria2Manager │ Ed2kEngine │
│ ├ Aria2Process │ └ jed2k (原生 eD2k) │
│ ├ Aria2Rpc (JSON-RPC) │ │
│ └ aria2c (ELF) │ │
│ BT / magnet / HTTP │ ed2k:// │
├────────────────────────┴─────────────────────────┤
│ ExoPlayer (Media3) ← 播放本地流地址 │
└──────────────────────────────────────────────────┘
两套下载引擎并存,因为协议栈不同:
| 链接类型 | 引擎 | 说明 |
|---|---|---|
magnet: |
aria2c | BitTorrent(DHT + Trackers) |
ed2k:// |
jed2k | eD2k 原生协议(连服务器、找源、分片校验) |
| 模块 | 文件 | 说明 |
|---|---|---|
| 链接解析 | ed2k/LinkParser.kt |
统一识别 ed2k:// 与 magnet: |
| ed2k 解析 | ed2k/Ed2kParser.kt |
解析 ed2k://|file|名称|大小|hash|/ |
| ed2k 引擎 | ed2k/Ed2kEngine.kt |
封装 jed2k:连接、找源、下载、分片位图 |
| aria2 二进制 | aria2/Aria2Binary.kt |
从 jniLibs 取 libaria2c.so,构建启动参数 |
| aria2 进程 | aria2/Aria2Process.kt |
启动 / 停止 aria2c 子进程 |
| aria2 RPC | aria2/Aria2Rpc.kt |
JSON-RPC 2.0 客户端 |
| aria2 服务 | aria2/Aria2Service.kt |
前台服务,保持引擎存活 |
| 高层管理 | aria2/Aria2Manager.kt |
UI 与下载引擎的唯一入口 |
| 流服务 | stream/LocalStreamServer.kt |
本地 HTTP Range 服务(边下边播的关键) |
| 播放器 | ui/PlayerActivity.kt |
ExoPlayer + 下载进度浮层 |
org/dkf/jed2k/ 是 eD2k 协议栈的实现(约 240 个类),包含 Kad DHT、eD2k 服务器协议、
分片校验与磁盘 IO。
- Android Studio Hedgehog (2023.1) 或更高
- JDK 17
- Android SDK 34
- NDK(仅在需要自行编译 aria2c 时)
-
克隆
git clone https://github.com/ydqsgl/yded2k.git cd yded2k -
准备 aria2c 二进制
aria2c 在 Android 上以
libaria2c.so的形式随 APK 打包 (放在jniLibs/而非assets/,这样能被解压到可执行的nativeLibraryDir, 绕开 Android 的 W^X 限制)。仓库已包含 arm64-v8a 的预编译产物。若要自行编译:
git clone https://github.com/aria2/aria2.git cd aria2 ./configure --host=aarch64-linux-android \ --with-libcares --without-libxml2 \ --disable-ssl --disable-gssapi make -j$(nproc) cp src/aria2c /path/to/yded2k/app/src/main/jniLibs/arm64-v8a/libaria2c.so
scripts/fetch_aria2c.sh提供了从 Termux 仓库提取预编译二进制的备选路径。 -
配置 SDK 路径
创建
local.properties:sdk.dir=/path/to/Android/sdk -
编译
./gradlew assembleDebug # 产物:app/build/outputs/apk/debug/app-debug.apk
-
打开 App,等待引擎就绪(绿色圆点)
-
粘贴链接,例如:
ed2k://|file|movie.mkv|7340032000|ABCDEF0123456789ABCDEF0123456789|/ magnet:?xt=urn:btih:08ADA5A7A6183Aae1E09D831df6748d566095A10&dn=Sintel -
点击解析
-
进入播放页,下载到设定比例后自动开播
- 系统分享:任意 App 中分享链接给本应用
- 直接打开:浏览器点
ed2k:///magnet:链接自动拦截 - 下载列表:暂停 / 继续 / 删除 / 播放,查看连接数与实时速度
| 设置项 | 默认值 | 说明 |
|---|---|---|
| 下载目录 | 应用外部存储 | 文件保存位置 |
| 最大并行任务数 | 3 | 同时下载数 |
| 分片大小 | 1024 KB | aria2 分片 |
| RPC 端口 | 6800 | aria2 JSON-RPC 端口 |
| 缓冲百分比 | 5% | 下载到此比例后开播 |
| 上传限速 | 64 KB/s | P2P 上传限速(P2P 礼仪,不建议设为 0) |
1. 没有源就下不动,这不是 Bug
ed2k 与 BitTorrent 都是 P2P,下载速度完全取决于网络中在线源的数量。 链接里写死了文件名和哈希,所以即使全网无人持有,客户端照样能显示"已解析"—— 能解析 ≠ 有源。
磁力更明显:拿不到元数据时连文件名都显示不出来,表现为停在"加载中"。
判断方法:在电脑上用 eMule / qBittorrent 打开同一条链接。 来源数为 0 的资源,任何 P2P 客户端都下不动(迅雷能下是因为它有中心化缓存,不走 P2P)。
2. 文件头在末尾的 mp4
部分 mp4 把 moov 放在文件末尾。这类文件在下载完成前无法解析出时长与索引,
会等到尾部下载完成才开播。--bt-prioritize-piece=tail 已缓解,但无法根治。
3. 编码支持取决于设备
播放依赖系统 MediaCodec。H.264 / HEVC 普遍支持;部分冷门编码(如 RMVB)需要软解, 本项目未内置软解器。
4. eD2k 端口
监听 4662(TCP)/ 4672(UDP)为标准端口。被运营商或路由器阻断时会拿到 LowID, 可用源会显著减少。
- Kotlin + Coroutines
- AndroidX / Material 3
- Media3 (ExoPlayer) 1.2.1
- OkHttp 4.12
- aria2c(BitTorrent 引擎)
- jed2k(eD2k 协议栈)
应用代码采用 MIT 协议。
内嵌组件遵循其自身协议:
- aria2c — GPLv2,本项目仅作为前端调用,未修改其源码
- jed2k — GPLv2