Animeko 数据源整合:五类源如何协同
【免费下载链接】animation-garden集找番、追番、看番的一站式弹幕追番平台,云收藏同步 (Bangumi),离线缓存,BitTorrent,弹幕云过滤。100% Kotlin/Compose Multiplatform项目地址: https://gitcode.com/gh_mirrors/an/animation-garden
播放页起播一集之前,Animeko 会并行向蜜柑计划、dmhy、Jellyfin 以及自配的多个网站发起数据源查询,再把全部结果交给同一个选择器挑出最合适的资源。你不需要知道资源来自哪个站点。
核心价值速览
| 项目 | 内容 |
|---|---|
| 解决什么 | 动漫资源分散在多个站点,同一部番需逐站手工搜索,页面结构与结果质量各不相同 |
| 工作方式 | 每个站点封装在统一的 MediaSource 接口之后,由选择器对全部源的做过滤、排序与自动选择 |
| 适用场景 | WEB 在线直连、BT 种子下载、Jellyfin/Emby/Ikaros 本地媒体库 |
| 前置依赖 | 条目数据由 Bangumi 提供;内置源随应用就绪,自定义源需编写 CSS Selector 或 JSONPath |
快速上手
- 安装应用后打开任一作品播放页,内置数据源(蜜柑计划、dmhy、Jellyfin、Emby、Ikaros)默认即参与首次查询。
- 打开「设置 → 数据源管理」查看源列表,用开关启停某个源,并用测试按钮验证站点连通性。
- 新增站点选择「Selector」类型,为搜索页、条目页、播放页分别编写 CSS Selector,如以 acg.rip 为例配置好搜索与剧集列表规则,经内置测试面板通过后再保存。
- 若站点数据全部走 JSON 接口,改选「JSON API」类型,为搜索、条目剧集、播放地址三个阶段各配置请求模板与 JSONPath 表达式。
- 想直接复用他人配置时导入订阅文件,其中的数据源会批量导入,并可按订阅整体启停。
原理拆解
数据源只负责把资源列出来
接口层,无论 WEB 还是 BT 站点,都实现同一个 MediaSource。核心方法fetch接收一个MediaFetchRequest,返回该条目在本源上的全部可下载资源。请求携带 Bangumi 条目 ID、条目全部已知名称与完整剧集列表;结果中的每个资源带MatchKind标记:按条目 ID 精确定位为 EXACT,按关键字搜到为 FUZZY。数据源不裁剪剧集,按集筛选由选择器完成。
源由工厂声明并动态加载
应用不硬编码源实例。工厂MediaSourceFactory声明 factoryId 与可配置参数,按配置创建实例。MediaSourceManager初始化时的这段代码枚举了应用可识别的全部源:SPI 部分用于模块化的第三方源,内置源逐个补齐。
private val factories: List<MediaSourceFactory> = buildSet { addAll(ServiceLoader.loadServices(MediaSourceFactory::class)) add(MikanMediaSource.Factory()) add(MikanCNMediaSource.Factory()) add(RssMediaSource.Factory()) add(JellyfinMediaSource.Factory()) add(EmbyMediaSource.Factory()) add(IkarosMediaSource.Factory()) add(SelectorMediaSource.Factory(selectorMediaSourceEpisodeCacheRepository, webSessionManager)) add(JsonApiMediaSource.Factory(selectorMediaSourceEpisodeCacheRepository)) }.toList()配置变化会重建对应源实例;代理设置变化同样触发重建,因此换网络配置无需重启应用。
Mikan 的两段式查询与索引缓存
Mikan 展示了比裸关键字搜索更快一条路的写法:先把 Bangumi 条目 ID 映射成 Mikan 的条目 ID,直接查该条目的 RSS;映射查不到才回退到关键字搜索。
private suspend fun HttpClient.searchByIndexOrNull(request: MediaFetchRequest): List<MediaMatch>? { val subjectId = indexCacheProvider.getMikanSubjectId(request.subjectId) ?: request.subjectNames.firstNotNullOfOrNull { findMikanSubjectIdByName(it, request.subjectId) } ?.also { indexCacheProvider.setMikanSubjectId(request.subjectId, it) } ?: return null return client.prepareGet("$baseUrl/RSS/Bangumi?bangumiId=$subjectId").body<ByteReadChannel>().toSource().use { parseRssTopicList(Xml.parse(it, baseUrl), baseUrl) }.map { MediaMatch(it.toOnlineMedia(mediaSourceId), MatchKind.EXACT) } }映射先查缓存,未命中时搜索站点并从条目页读回 Bangumi 链接校验后写回缓存,同一条目下次查询直接走缓存。应用内该缓存由 DataStore 持久化,重启不清空。
全部源查询完成后才做选择
选择器分过滤、排序、偏好、选择四个阶段。每个数据源有一个阶级MediaSourceTier,数值越小优先级越高;tier 为 0 的 WEB 源在查询完成且命中精确匹配时会被立即选中,不用等其他源。
进阶配置与调优
以下参数写在数据源的 arguments 中,随订阅文件分发时旧版本客户端会忽略不认识的新字段:
| 配置项 | 位置 | 作用 | 推荐值 |
|---|---|---|---|
| tier | 源 arguments | 数据源阶级,数值越小排序越靠前并参与快速选择 | 可信优质源设 0,未配置时默认 2 |
| channelTiers | 源 arguments | 按线路名覆盖源整体阶级 | 常用线路设为 0 |
| autoMatch.searchUseSubjectNamesCount | Selector 源 searchConfig | 依次尝试搜索的条目名数量 | 2 |
| requestInterval | JSON API 源配置 | 三个阶段请求共用的最小间隔,即源级限流 | 500 ms |
tier 同时影响两个环节:排序阶段按有效阶级(channelTiers[线路名] ?: 源 tier)决定先后,快速选择阶段按阈值判断能否立即选中。一个 tier 较高的源若拥有 tier 0 的线路,该线路资源仍可被立即选中。
避坑指南
- 完结番的 BT 源只见整季合集、不见单集。原因是默认开启的「完结番隐藏单集资源」会剔除完结条目的单集资源。解法是在播放与缓存的高级设置中关闭对应项,或直接选用整季资源节省流量。
- Mikan 对长名称条目查不到结果。原因是站点搜索无法匹配超过 19 个字的名称,源端请求前会截断名字。解法是源端优先走 Bangumi ID 索引映射精确定位,缓存未命中才做名称搜索,通常自行恢复,无需人工干预。
- 升级后的订阅文件被旧版客户端整体拒绝。原因是订阅导出格式带版本号,不识别新 schemaVersion 的客户端会拒收整个文件。解法是分发时避免无谓升版本号,新字段依赖 ignoreUnknownKeys 机制保持向后兼容。
- 自动选择列表里出现不相关条目的资源。原因是数据源检索基于关键字,同名或部分匹配的条目必然进入结果。解法是选择器过滤阶段按条目名剔除,手动选择列表中也可直接点选正确资源,选择会被记住。
延伸阅读
- docs/contributing/code/media/media-source.md:官方数据源文档,讲清 fetch 契约、选择器四阶段与 tier 机制,写新源或改订阅前的第一站。
- datasource/api/src/commonMain/kotlin/source/MediaSource.kt:接口的 KDoc 给出了「数据源应承诺什么」最完整的定义:结果完整性、episodeRange 准确、mediaId 稳定。
【免费下载链接】animation-garden集找番、追番、看番的一站式弹幕追番平台,云收藏同步 (Bangumi),离线缓存,BitTorrent,弹幕云过滤。100% Kotlin/Compose Multiplatform项目地址: https://gitcode.com/gh_mirrors/an/animation-garden
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考