Skip to content

LinPlayer 架构说明(Architecture)

面向二次开发与排障的开发者文档。本文以当前 main 分支代码结构为准。

1. 文档目标

  • 快速理解项目的“分层边界”和“关键调用链”。
  • 明确每个目录/模块该放什么,不该放什么。
  • 为后续新增服务器类型、播放能力、TV 能力提供落点参考。

2. 架构总览

LinPlayer 采用“页面层 + 状态层 + 适配层 + API 层 + 播放层 + 平台服务层”的结构:

text
Flutter 页面(lib/*.dart)
  -> AppState (packages/lin_player_state)
    -> Server Adapter (packages/lin_player_server_adapters)
      -> API Client (packages/lin_player_server_api)
        -> Emby/Jellyfin/WebDAV/Plex 等服务

播放页(lib/player_screen*.dart, lib/play_network_page*.dart)
  -> lin_player_player (PlayerService / Danmaku / 播放控制)
    -> media_kit(mpv) 或 video_player_android(Exo)

补充:仓库内还保留一条独立的兼容线 legacy/tv-legacy/,用于 Android 4.4 / API 19 电视盒子。它不是 Flutter 壳的一个 flavor,而是单独的 Android Application:

text
Legacy TV Activity(XML/View)
  -> AppPrefs / ServerStore
    -> EmbyClient(首页/收藏/搜索/Item 详情)
       或 Backends.media(MediaBackend)
         -> EmbyLike / Plex / WebDAV / Demo Backend
  -> NetworkClients / ProxyService / RemoteHttpServer
  -> PlayerActivity
    -> PlayerCores / VlcPlayerCore
      -> libVLC

3. 顶层目录职责

  • .github/
    • CI/CD、Nightly/Latest 发布流程、安装包脚本。
  • assets/
    • 图标、Anime4K shader、TV 代理资源、TV 远程网页静态资源。
  • lib/
    • Flutter 业务页面与应用编排(入口、路由、页面、平台服务 glue)。
  • packages/
    • 模块化后的核心包(state/ui/player/server/api/core/prefs)与 patched 依赖。
  • docs/
    • 用户与开发文档。
  • legacy/
    • 独立兼容工程与历史实现;当前主要是 legacy/tv-legacy/(Java + XML/View 的 Android 4.4 TV 端)。
  • tool/
    • 构建辅助脚本(如 TV 代理资源拉取)。
  • workers/
    • 辅助 Worker(当前有 workers/dandanplay-proxy)。

4. 分层说明

4.1 页面与编排层(lib/

入口:lib/main.dart

职责:

  • 初始化媒体后端(MediaKit.ensureInitialized())。
  • 初始化设备信息与 User-Agent(ServerApiBootstrap.configure(...))。
  • 加载全局状态(AppState.loadFromStorage())。
  • 根据设备类型与当前服务类型,选择 Home 壳:
    • TV:lib/tv/tv_shell.dart
    • Desktop:lib/desktop_ui/desktop_shell.dart
    • Mobile/Tablet:lib/home_page.dartlib/webdav_home_page.dart

关键页面:

  • 服务管理:lib/server_page.dart
  • 首页与库:lib/home_page.dartlib/library_page.dartlib/library_items_page.dart
  • 详情页:lib/show_detail_page.dart
  • 播放页:
    • MPV:lib/player_screen.dartlib/play_network_page.dart
    • Exo:lib/player_screen_exo.dartlib/play_network_page_exo.dart
  • WebDAV:首页壳与浏览器:lib/webdav_home_page.dartlib/webdav_browser_page.dart
  • 聚合检索:lib/aggregate_service_page.dart

4.2 状态层(packages/lin_player_state

核心:packages/lin_player_state/lib/app_state.dart

职责:

  • 全局状态中心(ChangeNotifier)。
  • 持久化(SharedPreferences)与缓存(库缓存、首页缓存、播放偏好、弹幕偏好、TV 偏好等)。
  • 服务管理流程:
    • addServer(...)(Emby/Jellyfin)
    • addWebDavServer(...)
    • addPlexServer(...)
    • enterServer(...) / leaveServer()
  • 数据加载流程:loadItems(...)loadHome(...)loadMediaStats(...)

相关模型:

  • packages/lin_player_state/lib/server_profile.dart
  • packages/lin_player_state/lib/local_playback_handoff.dart
  • packages/lin_player_state/lib/route_entries.dart

4.3 适配层(packages/lin_player_server_adapters

核心接口:packages/lin_player_server_adapters/lib/server_adapters/server_adapter.dart

职责:

  • 定义统一服务能力接口 MediaServerAdapter,对页面层屏蔽服务端差异。
  • 在工厂 server_adapter_factory.dart 中按产品线选择具体适配器实现。

现有实现:

  • lin/lin_emby_adapter.dart(Emby/Jellyfin 主实现)
  • uhd/uhd_adapter.dart

页面侧统一接入点:

  • lib/server_adapters/server_access.dartresolveServerAccess(...)

4.4 API 层(packages/lin_player_server_api

主要文件:

  • services/emby_api.dart
  • services/plex_api.dart
  • services/webdav_api.dart
  • services/webdav_proxy.dart
  • services/server_share_text_parser.dart
  • network/lin_http_client.dart

职责:

  • 原始 HTTP 能力与协议细节(header、鉴权、重试、fallback)。
  • 业务接口封装:鉴权、列表、详情、播放信息、播放上报、章节、相似推荐等。

说明:

  • Emby/Jellyfin 共用 EmbyApi(通过 MediaServerType 区分 header 与 prefix 策略)。
  • WebDAV 负责目录列举与鉴权处理(含 Digest/Basic)。
  • Plex 负责 PIN 登录和服务器资源发现(当前项目中主要是“登录与保存服务器信息”)。

4.5 播放层(packages/lin_player_player

主要文件:

  • player_service.dart:MPV 播放能力封装。
  • src/player/playback_controls.dart:播放控制 UI 复用组件。
  • src/player/danmaku_stage.dart + danmaku*.dart:弹幕管线。
  • src/player/anime4k.dart:Anime4K shader 管线。
  • dandanplay_api.dart:在线弹幕匹配与下载。

说明:

  • MPV 路径使用 media_kit
  • Exo 路径使用 video_player_android(并通过 patched 包增强轨道能力)。

4.5.1 预加载(Preload)

开关:AppState.preloadEnabled(设置 → 预加载,默认关闭)

实现:

  • PlaybackSourceBuilder:统一构建真实播放与预加载共享的 ResolvedPlaybackSource
  • PlaybackPreloadCoordinator:收口详情页 / 播放页的 current / next / resume 预加载入口
  • StreamPreloadServicepackages/lin_player_player/lib/src/preload/stream_preload_service.dart):执行实际预热请求与去重 / 熔断

补充说明:

  • 共享 source builder 内部已复用 STRM / redirect / body-link 解析逻辑,并把 proxyUrl 等元数据保留在 ResolvedPlaybackSource 中,供播放页与预加载共同消费。
  • PlaybackPreloadCoordinator 会把 targetKind 编进去重命名空间,因此 currentItem / nextItem 分开判重,但不会因为 triggerSource 不同而重复预热同一语义目标。
  • 预加载固定命中共享 ResolvedPlaybackSource 的远端 URL;若真实播放需要自定义 HTTP 代理,则通过 httpProxyUrl 继承代理语义,而不是改为命中本地回环代理 URL。
  • direct file / redirect file / body-link file 的预热已收口到 HttpStreamProxyServer.warmRangeToCache(...):按 Range 直接写入磁盘缓存,续播场景会复用同一 CacheKey 追加热续播附近区间,并把 redirect-final URL 写回缓存元数据供后续播放补尾复用。
  • StreamCacheDownloadService 已删除;StreamCacheDownloadRequest 是当前保留的缓存语义载体,用来把共享 CacheKey、代理语义和 redirect-final URL 绑定到同一份缓存对象。

行为:

  • 集详情页(EpisodeDetailPage)加载完成后,使用 UA preload-linplayer 预取:
    • 当前集前 3 秒
    • 下一集前 3 秒
  • 播放过程中,当观看进度达到用户设置的观看阈值时,预取下一集前 3 秒(兜底:非从详情页进入的场景)。

失败策略:

  • 单次预取最多尝试 3 次;若同一 source / proxy scope 在短时间内连续失败,会进入带 TTL 的短时熔断,并在恢复窗口后重新允许尝试。
  • 预取实现为 best-effort:优先使用直链流的 Range 拉取;若为 HLS(.m3u8)则解析并请求初始化段与前若干分片。
  • HLS 当前仍采用“最高带宽 variant + 最多 3 段”的保守预热策略,相关常量已收口在 preload service。

4.6 UI 基建层(packages/lin_player_ui

主要文件:

  • src/ui/app_theme.darttheme_sheet.dartui_scale.dart
  • src/ui/glass_background.dartfrosted_card.dart
  • src/ui/app_components.dartrating_badge.dart

职责:

  • 统一主题、风格模板、缩放与基础组件。
  • 保证页面层不重复实现公共视觉逻辑。

4.7 配置与偏好层(packages/lin_player_prefs

主要文件:

  • preferences.dart
  • interaction_preferences.dart
  • danmaku_preferences.dart
  • anime4k_preferences.dart

职责:

  • 偏好枚举、配置模型定义(供 AppState 与 UI 使用)。

4.8 核心配置层(packages/lin_player_core

主要文件:

  • app_config/app_config.dart
  • app_config/app_feature_flags.dart
  • app_config/app_product.dart
  • state/media_server_type.dart

职责:

  • 产品线差异(lin/uhd
  • 功能开关(允许的 server type)
  • 基础枚举与全局配置上下文

4.9 Legacy TV 兼容工程(legacy/tv-legacy/

定位:

  • 独立 Android 应用,不参与 Flutter 运行时。
  • 技术栈:Java + XML/View,minSdk = 19,兼容 Android 4.4 TV/盒子设备。
  • 主要依赖:AppCompat 1.6.1、RecyclerView、OkHttp 3.12、libVLC 3.6、ZXing。

入口与页面:

  • LinPlayerApp
  • 首页:MainActivity
  • 服务器管理:ServersActivityServerEditActivity
  • 搜索/媒体库:SearchActivityLibraryDetailActivity
  • 详情页:ItemDetailActivityShowDetailActivityEpisodeListActivityEpisodeDetailActivity
  • 播放页:PlayerActivity
  • 设置页:SettingsActivity

数据与状态:

  • 持久化入口:AppPrefs
  • 服务器配置:servers/ServerStore + servers/ServerConfig
  • 当前实现同时存在两条数据访问路径:
    • Emby 专用路径:MainActivitySearchActivityLibraryDetailActivityItemDetailActivity 直接使用 emby/EmbyClient
    • 统一后端路径:backend/Backends.media(...) 根据 active server 选择 EmbyLikeMediaBackendPlexMediaBackendWebDavMediaBackendDemoMediaBackend
  • 这意味着 tv-legacy 仍处在“专用实现 + 统一抽象并存”的过渡状态。

播放与平台服务:

  • 播放核心:player/PlayerCores -> player/VlcPlayerCore -> libVLC
  • PlayerActivity 负责播放 HUD、选集、音轨/字幕切换、进度控制,以及 STRM / Emby-like URL 的解析与重定向处理。
  • 网络层:NetworkClients 统一注入 User-Agent,并按设置决定直连或 per-app 代理。
  • 内置代理:ProxyService + MihomoConfig + MihomoProcess + ProxyEnv
  • 遥控/配网:remote/RemoteControl + remote/RemoteHttpServer + remote/PlaybackSession
    • ServersActivity 会启动局域网 HTTP 服务并展示 QR,手机网页可批量添加服务器、调整代理设置、控制播放。

UI 组织:

  • activity_*.xmlitem_*.xml 为主的 XML 布局 + RecyclerView.Adapter
  • 共享 TV 视觉参数通过 TvStyle + AppPrefs 管理(面板透明度、背景模糊等)
  • 背景图和毛玻璃视觉由 BitmapFetcher / BitmapBlur / ImageLoader 等工具类完成

5. patched 依赖说明

  • packages/media_kit_patched
    • 为 MPV 路径提供更细粒度参数传递能力。
  • packages/video_player_android_patched
    • 增强 Exo 轨道相关能力(音轨/字幕轨选择等)。

pubspec.yaml 中通过 dependency_overrides 强制覆盖到项目内 patched 包。

6. 关键业务链路

6.1 启动链路

  1. main.dart 初始化媒体内核、设备信息、AppConfig。
  2. AppState.loadFromStorage() 恢复服务、主题、偏好、缓存。
  3. 根据设备类型进入 TvShell / DesktopShell / 普通 Home。
  4. 启动可选服务:TV Remote、Built-in Proxy、自动更新检查。

6.2 添加服务器链路

页面:lib/server_page.dart

  • Emby/Jellyfin:AppState.addServer(...)
    • 内部通过 adapter/API 完成鉴权与基础信息拉取。
  • WebDAV:AppState.addWebDavServer(...)
  • Plex:
    • 账号授权流(PIN)+ 资源选择,或手动填 token。
    • 最终进入 AppState.addPlexServer(...)

6.3 首页/列表加载链路

  1. 页面触发 AppState.loadHome() / loadItems(...)
  2. AppState 使用当前 active server 构建访问上下文。
  3. 调 adapter 拉取数据并写入本地缓存。
  4. notifyListeners() 驱动 UI 更新。

6.4 播放链路(网络媒体)

入口:show_detail_page.dart / 列表页 -> play_network_page*.dart

  1. 通过 resolveServerAccess(...) 获取 adapter + auth
  2. fetchPlaybackInfo(...) 获取 PlaySessionId/MediaSources
  3. (可选)若开启「预加载」,会在集详情页/播放达到观看阈值后触发 StreamPreloadService,用 UA preload-linplayer 预取当前集与下一集的前 3 秒数据。
  4. 组装流 URL 与 header,交给 MPV 或 Exo 播放。
  5. 播放期间上报:
  • reportPlaybackStart
  • reportPlaybackProgress
  • reportPlaybackStopped
  • updatePlaybackPosition

6.5 播放链路(本地文件/WebDAV)

  • 本地:player_screen*.dart 直接从本地路径建立播放列表。
  • WebDAV:webdav_browser_page.dart
    • 先通过 webdav_proxy.dart 注册本地代理 URL。
    • 再把 URL 作为本地播放队列交给播放器。

6.6 弹幕链路

  1. 播放页叠加 DanmakuStage
  2. 数据来源:本地 XML 或在线(dandanplay_api.dart)。
  3. 渲染参数来自 AppState(透明度、字号、速度、去重、防遮挡等)。

6.7 TV 专项链路

  • TV 遥控网页:tv_remote_service.dart
    • 内置 HTTP + WebSocket 服务,提供移动端控制入口。
  • TV 内置代理:built_in_proxy_service.dart
    • 管理 mihomo 进程、配置生成、代理规则及 UI 面板资源。

6.8 Legacy TV 关键链路

  1. MainActivity 启动时先检查 ServerStore.hasAny(...);若没有服务器则跳转 ServersActivity
  2. ServersActivity / ServerEditActivity 负责登录 Emby/Jellyfin、保存 ServerConfig,并可通过 QR Remote 做批量导入与远程配网。
  3. 首页/搜索/媒体库当前直接使用 EmbyClient 拉取 Emby/Jellyfin 数据;剧集详情与播放器侧则更多通过 Backends.media(...) 访问 Emby-like / Plex / WebDAV。
  4. PlayerActivity 根据 URL、showId、episodeIndex 等上下文调用 VlcPlayerCore 打开流,并接管选集、音轨、字幕、seek 与远程控制状态同步。
  5. 若启用代理,则 ProxyService 启动本地 mihomo,NetworkClientsProxyEnv 让 App 内网络请求经 127.0.0.1 走 per-app 代理。

7. 平台与壳层

7.1 Desktop 壳层

目录:lib/desktop_ui/

职责:

  • 提供桌面端独立壳与页面组织。
  • 与移动端共享状态层、适配层、API 层。
  • 详细设计与组件拆分:docs/dev/DESKTOP_UI_ARCHITECTURE.md

7.2 TV 壳层

目录:lib/tv/

职责:

  • TV 首页、背景模式、首启向导、遥控操作体验。

7.3 Legacy TV 兼容壳层

目录:legacy/tv-legacy/

职责:

  • 为 Android 4.4 / API 19 设备提供独立 TV 壳与播放器。
  • 与主 Flutter 工程共存,但构建、运行、依赖链完全独立。
  • 作为低版本设备兼容线保留,便于继续维护传统 Java/XML TV 端能力。

8. 数据与持久化策略

持久化入口:AppState(SharedPreferences)

典型内容:

  • 当前服务与服务器列表。
  • 主题、缩放、交互手势、播放器偏好。
  • 弹幕偏好、TV 偏好、自动更新设置。
  • 轻量缓存:库列表、首页区块、部分统计信息。

9. 扩展指南

9.1 新增一个服务端类型

建议步骤:

  1. lin_player_core 扩展 MediaServerType 与 FeatureFlag。
  2. lin_player_server_api 新增 API 客户端。
  3. lin_player_server_adapters 实现新的 Adapter。
  4. ServerAdapterFactoryserver_page.dart 接入。
  5. AppState 补充服务保存/切换逻辑。

9.2 新增播放能力

建议步骤:

  1. 优先落在 lin_player_player(而不是页面直接写)。
  2. 页面层只做参数编排与状态展示。
  3. 涉及平台特性时,通过 patched 包或平台通道落地。

9.3 新增 UI 模板或全局视觉能力

建议步骤:

  1. lin_player_ui 扩展主题/样式模型。
  2. AppState 持久化模板/参数。
  3. 页面层仅消费,不重复定义 token。

10. 调试与维护建议

  • 静态检查:flutter analyze
  • 测试:flutter test
  • 平台构建前先确认:flutter doctor -v
  • 低版本 TV 工程单独构建:cd legacy/tv-legacy; .\gradlew.bat :app:assembleDebug
  • 对外接口异常优先查:
    • 当前 active server 信息
    • Adapter 选型是否正确
    • API prefix / token / userId 是否一致
  • 播放异常优先查:
    • fetchPlaybackInfo 返回的 MediaSources
    • 选中的音轨/字幕索引
    • MPV/Exo 内核是否匹配当前片源

11. 相关文档

  • docs/dev/README.md
  • docs/dev/ANDROID_SIGNING.md
  • docs/dev/TV_PROXY_ROADMAP.md
  • docs/SERVER_IMPORT.md
  • legacy/tv-legacy/README.md
  • legacy/tv-legacy/TV_GUIDE.md
  • legacy/tv-legacy/API.md

如果你准备继续模块化重构,建议下一步先统一:

  • 页面层对 AppState 的直接读写边界
  • 播放页的共享 ViewModel/Controller 抽象
  • 详情页(剧/集)的公共组件拆分

Built with VitePress