Skip to content

Feat:Feature upgrades and optimizations; several known issues have been fixed.- #2689 - #171

Closed
ucxn wants to merge 2 commits into
dev4harmony:ohosfrom
1-Reality:feat/network-speed-final
Closed

ucxn wants to merge 2 commits into
dev4harmony:ohosfrom
1-Reality:feat/network-speed-final

Conversation

@ucxn

@ucxn ucxn commented Aug 26, 2026

Copy link
Copy Markdown

本次更新内容

修复了用户连着 WiFi 看视频,然后出门用流量,点击推荐视频,下一个视频依然沿用 WiFi 画质,没有使用蜂窝画质和解码器,导致 烧流量 的问题。

除此之外,所有画质解码器之类的生命周期和状态机都已经修好。缓解了大量用户反映的卡顿问题。

本 PR 首先修复了以下 4 个已知 Issue:

同时进一步整理了播放器在不同网络状态、不同视频生命周期下的画质与编码选择逻辑,并补充 PC 网络状态联动、网络高峰期优化、长按倍速自定义等功能。

已知问题修复

bggRGjQaUbCoE#2671 二级评论排序文案

二级评论按照时间顺序排列时,原有的“最新评论”容易让人理解为最新发布的评论排在前面,与实际排序方向不够一致。

修复 CDN 测速严重虚高

  • 修复 CDN 测速结果严重失真的问题。Dio 回调中的 count 本身已经是累计下载量,原实现又将它反复累加,导致同一批数据被重复计算。按照现有回调节奏,部分结果会虚高到真实速度的约 8 倍,甚至超过当前网络的物理速率。
  • 测速改为直接读取响应流,按照实际收到的字节计算速度,并使用单调时钟分别记录首包等待时间和持续下载速度。前 4 MiB 用于预热,单个 CDN 最多读取 16 MiB,减少连接建立和 TCP 慢启动对结果的影响。
  • 每个 CDN 独立判断测量结果。完整样本、较短样本和兼容路径互不影响;连接较慢但仍在持续返回数据时保留粗测结果,不会因为速度低而误判为请求失败。
  • 兼容路径仍然修复了重复累计和单位换算问题,在提高准确度的同时保留原实现已经验证过的可用性。

bggRGjQaUbCoE#2660 从视频页进入 UP 主空间后,投稿列表加载范围不完整

此前从视频播放页进入 UP 主空间时,投稿列表会以当前视频作为加载锚点,因此只能看到当前视频以及更早的投稿,UP 主后来发布的视频可能不会出现在列表中。

同时清理了原有的前向加载状态和重复页码递增逻辑,避免分页结束状态残留以及部分情况下重复跳页的问题。

bggRGjQaUbCoE#2686 Windows 端直播间表情尺寸异常

直播间官方表情的接口尺寸原本与当前设备的屏幕像素比关联。手机通常拥有较高的设备像素比,因此显示结果看起来正常;Windows 设备的像素比通常接近 1,导致相同表情在桌面端被放大到明显超出正常范围。

本次统一按照表情资源本身的尺寸规则换算逻辑大小,普通表情使用稳定的默认尺寸,使 Windows 与移动端的显示效果保持一致,不再受到不同设备像素比的意外影响。

bggRGjQaUbCoE#2638 Android 端部分 HTTP 链接无法打开

评论区等位置可能出现仍然使用 HTTP 的历史链接。Android 内置 WebView 会受到明文流量限制,直接打开时可能显示 ERR_CLEARTEXT_NOT_PERMITTED。

本次在使用应用内 WebView 打开链接时自动尝试切换为 HTTPS;如果用户选择外部浏览器,则继续保留原始地址,由外部浏览器自行处理。这样可以解决应用内无法访问的问题,同时不会擅自改变传递给外部应用的链接。

统一画质与编码器的生命周期逻辑

bggRGjQaUbCoE#2571 已经引入了 Wi-Fi 与蜂窝网络下相互独立的编码偏好。本次进一步让编码器与现有画质选择采用完全一致的生命周期语义,避免同一个播放器中画质遵循一套规则、编码器又遵循另一套规则。

普通的换 P、自动联播和进入推荐视频仍然保留用户在本次播放周期内手动选择的画质与编码器;当网络类型真正发生变化时,则将其视为更强的状态变化信号,重新应用当前网络对应的画质、音质和编码偏好。

例如,用户在家中使用 Wi-Fi 观看高画质视频,随后携带设备出门并切换到蜂窝网络,播放器会重新采用蜂窝网络下的设置,避免继续按照 Wi-Fi 的高画质、高码率配置消耗移动流量。这样既保留了临时选择的连续性,也避免网络环境已经发生变化以后,播放器仍然机械沿用原来的网络策略。

PC 网络状态联动判断

新增“PC 网络状态联动判断”页面,使桌面端也可以根据实际连接质量选择是否沿用蜂窝网络下的画质、音质和编码偏好。

有线网络支持:

  • 按协商速率设置最低阈值,提供百兆、千兆、2.5G 和自定义数值。
  • 当实际协商速率低于用户设置的阈值时,按照蜂窝网络处理。
  • 可选识别非标准协商速率,用于发现部分手机网络共享、异常协商或特殊网卡连接。

Wi-Fi 支持:

  • 按 RSSI 信号强度判断, 按 Wi-Fi 协商速率判断。

  • 支持“仅信号”“仅速率”“信号和速率同时满足”“信号或速率任一满足”四种组合方式。

  • Windows 端在系统可以提供信息时读取当前 Wi-Fi RSSI。

  • 按流量计费的 Wi-Fi 可以直接沿用蜂窝网络偏好。

网络状态只在进入播放器、用户主动刷新以及系统报告网络连接发生变化时重新判断,避免因为短暂的信号波动反复切换配置。

网络高峰期优化

国内不同地区、不同运营商在网络高峰期可能出现 CDN 节点拥塞、跨网质量下降或特定线路限速,最终表现为带宽看似足够,但视频仍然频繁缓冲。为此新增“网络高峰期”机制,让用户可以在容易发生拥塞的时段临时使用更适合当前线路的编码顺序。

高峰期只替换播放器运行时使用的编码顺序,不改写用户原有的长期编码设置,也不覆盖画质设置。时段结束以后会重新读取正常偏好并自然恢复;如果没有单独设置高峰期编码顺序,则默认跟随蜂窝网络的编码偏好。

这套机制把网络高峰期作为运行时策略处理,原有播放流程不需要到处增加特殊分支,也不会为了临时网络状况污染用户的长期配置。

长按倍速与倍速时间统计

原有“当前倍速的两倍”只能覆盖一种使用习惯。对于长期使用较高倍速观看视频的用户,长按有时反而是为了暂时减速;其他用户也可能需要 1.5 倍、2.5 倍或者更自由的倍率。播放器无法替用户推测长按的真实意图,因此本次将其扩展为可自定义的倍速系数。

  • 默认仍然保持当前倍速的 2 倍,未修改设置的用户体验不变。
  • 滑块范围为 50%~300%。
  • 支持手动输入大于 0 的任意系数,不设置固定上限。
  • 系数可以小于 1,使长按也能用于临时减速。
  • 可选开启长按时上下滑动调整临时倍速,向上加速、向下减速,每档调整 0.25 倍,松手后恢复原来的播放倍速。

所有数据均保存在本地,不包含遥测和上传行为。

让用户多年后回首这些年看过的视频,不留遗憾。@dev4harmony

实现与兼容性

本次改动尽量复用播放器现有的网络状态、画质选择、编码顺序和生命周期入口,将网络变化、普通视频切换和用户临时选择统一处理,减少在各个页面分别维护特殊判断。

PC 网络联动保持默认关闭,原有用户不会因为升级而改变播放策略;长按倍速系数默认仍为 2 倍;网络高峰期只影响运行时变量,不修改长期偏好;Android 网络判断不申请新的定位权限。新增功能集中在对应的高级设置页面中,普通用户可以继续沿用原有行为,需要精细控制的用户则可以按照自己的网络环境和观看习惯自由配置。

这次更新一方面集中解决了多个已经确认的显示、导航和链接问题,另一方面把画质、编码器、网络状态与倍速行为整理为相互一致的完整逻辑,使播放器在不同设备、不同网络以及不同观看习惯下都能保持更加自然、可预测的表现。

@ucxn
ucxn force-pushed the feat/network-speed-final branch 3 times, most recently from 265f64d to 565e12d Compare August 26, 2026 05:49
@wm-develop

wm-develop commented Aug 27, 2026 •

Copy link
Copy Markdown
Member

非常感谢您的贡献!然而,PiliPlus 鸿蒙版分支的开发原则与上游仓库并不相同,在说明具体问题之前,有必要先把这套原则讲清楚,因为本次的绝大多数分歧都源自它,而不是源自代码质量。

一、本分支的开发原则

核心:本分支的唯一目的是让上游 PiliPlus 能在 HarmonyOS 上跑起来,并在可能的情况下开发一些 Harmony OS 平台独占的特性(如沉浸光感顶栏/导航栏、识别设备握持左右手等)、,而不是开发新功能的场所。Harmony OS 是唯一的目标平台,不需要能够在 Android / iOS / Windows / macOS / Linux 等其他无关平台上编译运行。因此,有以下几点原则:

1. 分层判断,决定跟进还是跳过

层级 处置
平台原生层(android/、ios/、windows/、linux/、macos/) ⛔ 全部跳过——鸿蒙有自己的 ohos/ 目录
原生桥接层(lib/ 中使用 JNI / win32 / Cocoa 等平台专属 API 的代码) ⛔ 跳过——鸿蒙有自己的桥接 lib/harmony_adapt/
共享业务层(lib/ 下的跨平台 Dart 代码) ✅ 评估后跟进
构建 / CI 层(.github/、lib/scripts/、tool/) ⛔ 跳过——鸿蒙有自己的 test.yml 与构建流程

其中对于共享业务层,鸿蒙已有等效实现的功能跳过;鸿蒙已用其他方式修复的 bug 跳过;只有确实带来新功能、或修复两边共有 bug 的改动才跟进。

2. 功能先进上游,我们通过定期三方合并跟进,不在下游首发

我们与上游的同步方式是三方合并(2.1.0、2.1.1、2.1.2 均如此),而不是逐个 cherry-pick,这样才能保证鸿蒙没碰过的上游改动全部跟进,且不会在同一处反复解冲突。

这条原则背后有实打实的代价核算。在 2.1.1 的同步评估中我们量化过:如果让鸿蒙分支的某类代码与上游产生永久性的行为分叉,那么此后每一轮同步、每一个双方都碰过的文件都会冲突;按当时 184 个重叠文件计算,等于给未来每一轮同步凭空增加 100+ 个冲突。因此我们对在本分支首发功能这一行为极度保守:

  • 若上游最终以不同方式实现同一功能 → 我们每次同步都要手工调解,永远无法收敛;
  • 若上游最终不采纳 → 我们需要长期自行维护。

3. 鸿蒙 SDK 约束是刚性的

  • 鸿蒙 Flutter fork 目前是 3.41.10-ohos / Dart 3.11.5,上游已在 3.47 / Dart 3.13。我们必须守住 .fvmrc 与 pubspec 的版本下限;
  • CI 跑的是官方 Flutter 3.41.9 stable,因此不得引入 TargetPlatform.ohos,一律用 OS.isHarmony;
  • 大量依赖锁定在鸿蒙 fork 版本(connectivity_plus 5.x 单值返回、media_kit Predidit fork、file_picker_ohos、share_plus 10.x 等),上游升级依赖时我们保留鸿蒙版并在共享层做 API 适配;
  • 由于 JNI 已被桩化(lib/utils/android/android_helper.dart 与 bindings.g.dart 都是同 API 形态的空实现),本分支已经不能构建安卓产物,这是符合"只保证鸿蒙"原则的有意取舍。

二、基于上述原则,本 PR 存在以下问题

1. 功能改动未先进入上游,直接在鸿蒙分支落地

本 PR 对应上游 bggRGjQaUbCoE#2689,据确认尚未被上游合入。更具体的例子是 lib/pages/video/member/controller.dart:PR 删除了 currAid / firstAid / hasPrev / isLoadPrevious 这一套以当前视频为锚点的双向分页,退回成从头单向分页,并连带删除了 view.dart 中定位到当前视频的 jumpTo(112.0 * index) 与 _buildSortBtn 的 lastAid 传递。

我们比对了上游 main 的当前状态,上游至今仍然是锚点双向分页。也就是说这不是应用上游的修复,而是在下游单方面回退了上游仍在维护的实现。bggRGjQaUbCoE#2660 的问题是真实的,但正确的解法是把修复推给上游。

2. PR 中携带了两个不应存在的 GitHub Actions workflow

.github/workflows/pr171-fix-analyzer.yml 声明 permissions: contents: write,在 push 到 resolve/pr171-ci 时检出 resolve/pr171-ohos,运行一段 Python 脚本改写源码,然后用硬编码的提交者身份 commit 并 git push origin HEAD:resolve/pr171-ohos。

这应该是你解决移植冲突时的临时脚手架。它一旦进入 ohos 主干,就等于在仓库里长期留下一个带写权限、由分支名触发、会自动改写代码并推送的作业,这是难以接受的。

同时这段脚本本身也说明了移植过程中的问题:它修复的第 1 条是 "one extra brace closed the State class early"(lib/plugin/pl_player/view/view.dart 出现过括号错配);第 3 条则直接承认 win32 FFI 与本分支锁定的版本不兼容,于是把 lib/utils/windows/network_info.dart 写成了恒返回空值的桩。PR 描述中"PC 端可评估连接质量"这项功能在本 PR 里实际并不存在。

另外 pr171-validate.yml 使用 dart analyze --no-fatal-warnings,掩盖了本 PR 引入的 unused_element(见问题 6)。

3. 主打功能在鸿蒙上完全不生效

lib/utils/connectivity_utils.dart 的 _sample() 中,链路质量只在两个分支取值:

if (Platform.isWindows) {
  final info = WindowsNetworkInfoReader.read();   // 恒返回全 null 的桩
} else if (Platform.isAndroid) {
  final info = PiliAndroidHelper.networkInfo();
}

鸿蒙上二者均为 false,rssi / linkSpeed / signalLevel 恒为 null,signalWeak、speedWeak 恒为 false,wifiWeak 永远是 false;markCurrentWifiWeak() 的第一个条件就是 !Platform.isAndroid 直接 return。

android/app/src/main/java/com/example/piliplus/AndroidHelper.java 中新增的 networkInfo() 同样是死代码。它落在平台原生层,而 Dart 侧的 PiliAndroidHelper 在本分支是空实现桩,networkInfo() 恒返回 null。

因此,本PR上实际生效的只剩"Wi-Fi ↔ 蜂窝切换时重新套用对应偏好",却为此引入了 535 行的 network_policy.dart 设置页,入口标题还是「PC 网络状态联动判断」,会在鸿蒙和手机上照常显示,点进去后 RSSI 阈值、链路速率阈值全部无效,对用户构成误导。

要在鸿蒙上做这个功能,前置条件是先在 lib/harmony_adapt/ 中通过 ArkTS 通道取链路信息(@ohos.net.connection / @ohos.wifiManager),本 PR 未涉及。

4. 蜂窝解码偏好绕过了鸿蒙的编解码器守卫

lib/utils/storage_pref.dart:

static List<VideoDecodeFormatType> get preferCodecsCellular {
  ...
  return const <VideoDecodeFormatType>[.HEVC, .AV1];   // 原为 return preferCodecs;
}

preferCodecs 里有 if (OS.isHarmony) return [HEVC, AVC];,这是我们为规避鸿蒙侧 AV1 解码问题在「视频解码优先级调整」中专门加的。preferCodecsCellular 没有对应守卫,改动后鸿蒙用户一切到蜂窝网就会优先请求 AV1,正好回到要避开的路径上。

同一处还把移动端 preferCodecs 默认值从 [AVC, AV1] 改成 [AVC, HEVC],这是对所有未手动设置过该项的存量用户的静默行为变更,PR 描述中未提及。

5. 启动路径与网络层的改动风险超出可接受范围

  • lib/main.dart 中 await ConnectivityUtils.initialize() 位于 Get.lazyPut(AccountService) 之前,处在启动关键路径上。原 connectivity_utils.dart 的注释正是警告过这一点:"鸿蒙上 checkConnectivity 可能抛异常"。新的 initialize() 只在 _sample() 内部有 try/catch,外层的 Connectivity().onConnectivityChanged.listen(...) 既无 try 也无 onError:——鸿蒙侧插件在此处抛出即等于启动崩溃。
  • lib/http/init.dart 把原本 iOS 专属的 _resetAdaptersForNetworkChange()(会 connectionManager.close(force: true) 并替换 adapter)推广到全平台。此前仅限 iOS 是有原因的。
  • connectivity_utils.dart 中 _bufferingWeak 是单向锁存:置 true 后只有 connectivity 事件才复位。Android 上一次 5 秒卡顿就会把整个会话钉死在"等效移网"偏好上,网络恢复也回不来;而这次状态变化会同时触发 _reloadForNetworkPolicy() 重开播放器和上面的连接池强制关闭。一次普通卡顿的代价过高。

6. CDN 测速:采样量提高了一个数量级

lib/pages/setting/widgets/select_dialog.dart:

? (warmup: 4194304, max: 16777216)   // 蜂窝 16 MiB
: (warmup: 8388608, max: 67108864);  // Wi-Fi 64 MiB

原实现是 const maxSize = 8 * 1024 * 1024(8 MB)统管全部。CDNService.values 共 21 项、_testAllCdnServices 串行遍历,Wi-Fi 下上限约 1.34 GB;每项还有 15 s 硬超时,全程最长 315 秒。此外 _measureStream 一旦抛异常就 fallback 到 _measureLegacy 重下一遍,流量再叠一次。

附带两个显示问题:三种采样类型输出三种单位(MiB/s / M/s / MB/s,其中 fallback 用 1000×1000、其余用 1024×1024),无法横向比较;'首包 ${firstByteMs.toStringAsPrecision(3)}ms' 在首包 >1000 ms 时会渲染成 1.23e+3ms。

7. 倍速统计服务的运行开销与收益不匹配

lib/services/playback_stats_service.dart(707 行):Timer.periodic(30s, flush) 全生命周期不取消;flush() 内 jsonDecode(jsonEncode(_stats!)) 做全量深拷贝再写 Hive;而 _dirty 几乎恒为 true,这会导致_settleAppForeground() 只要 app 在前台就会 _add('appForegroundUs', ...)。因此即使不播放任何视频,也会每 30 秒全量序列化并落盘一次。liveByUid 每观看一个直播间新增一个 key,无裁剪、无上限,开销随使用时长线性增长。

8. 其他技术问题

  • lib/plugin/pl_player/controller.dart 新增的 _onUserLeaveHint() 无任何调用方。本分支通过 MethodChannel 处理 onUserLeaveHint,这是从上游 JNI 版本误带过来的死代码,会触发 unused_element 警告。
  • adjustLongPressSpeed() 的 max(0.25, playbackSpeed + steps * 0.25) 只有下界没有上界;配合 onLongPressMoveUpdate 的 -dy / 24 一档,上滑 240px 即 +2.5x,无封顶。
  • onNetworkPolicyChanged 是 PlPlayerController 单例上的单个回调字段:VideoDetailController 无条件赋值且 onClose 不清理,LiveRoomController 则备份/还原,视频页与直播页交替打开时会互相覆盖;而 AudioController 用的又是直接订阅 ConnectivityUtils.changes,三处写法不一致。
  • lib/pages/live_room/controller.dart 的 _previousNetworkPolicyCallback 是 late final,异常路径下 onClose 先执行会抛 LateInitializationError;同文件还无故删除了本分支的注释「跨设备接续等场景显式指定初始状态(听直播/暂停),优先于默认行为」,并对调了 queryLiveUrl 的参数顺序(纯噪声 diff)。
  • lib/utils/page_utils.dart 强制 url.http2https,影响所有平台的 webview 跳转,而非仅 Android。
  • lib/pages/video/controller.dart 的 queryVideoUrl 被整体包进 try/finally,导致近 400 行是纯缩进变化,真实逻辑改动被完全淹没;同处 _pendingNetworkRefresh 存在丢事件路径(置位条件含 data.dash == null,但只在 finally 中且 hasDashResponse == true 时消费)。
  • 整个 PR 在一次提交中混合了 4 个互不相关的 bugfix([Bug] 二级评论展示那个设置项的文本有误 bggRGjQaUbCoE/PiliPlus#2671 / [Bug] 在播放页里打开的up主页显示问题 bggRGjQaUbCoE/PiliPlus#2660 / [Bug] windows端,直播间的弹幕表情包图片大小异常 bggRGjQaUbCoE/PiliPlus#2686 / [Bug] 评论区部分链接打开显示不允许HTTP明文传输 bggRGjQaUbCoE/PiliPlus#2638)、3 个独立功能和一个 707 行的新遥测服务。即便前述问题全部解决,这样的粒度我们也无法负责任地评审与回归。

三、因此,我们决定不合并该 PR,但以下贡献会被考虑

不合并是分支定位与工程风险的综合判断,不是对您贡献的否定。以下几点您的判断是对的,我们会由团队按自己的节奏实现,你无需再修改本 PR:

1. CDN 测速的计算错误。

2. Wi-Fi ↔ 蜂窝切换后不重新套用偏好。

3. 长按倍速系数可自定义。

4. 二级评论排序文案(bggRGjQaUbCoE#2671)。

5. 直播表情在高 DPR 设备上尺寸异常(bggRGjQaUbCoE#2686)。

6. 视频页进入创作者空间时列表不完整(bggRGjQaUbCoE#2660)。

7. 网络高峰期临时切换编码偏好。


最后,再次感谢您的投入和贡献。

@ucxn

ucxn commented Aug 27, 2026 •

Copy link
Copy Markdown
Author

非常感谢您的贡献!然而,PiliPlus 鸿蒙版分支的开发原则与上游仓库并不相同,在说明具体问题之前,有必要先把这套原则讲清楚,因为本次的绝大多数分歧都源自它,而不是源自代码质量。

一、本分支的开发原则

核心:本分支的唯一目的是让上游 PiliPlus 能在 HarmonyOS 上跑起来,并在可能的情况下开发一些 Harmony OS 平台独占的特性(如沉浸光感顶栏/导航栏、识别设备握持左右手等)、,而不是开发新功能的场所。Harmony OS 是唯一的目标平台,不需要能够在 Android / iOS / Windows / macOS / Linux 等其他无关平台上编译运行。因此,有以下几点原则:

1. 分层判断,决定跟进还是跳过

层级 处置
平台原生层(android/、ios/、windows/、linux/、macos/) ⛔ 全部跳过——鸿蒙有自己的 ohos/ 目录
原生桥接层(lib/ 中使用 JNI / win32 / Cocoa 等平台专属 API 的代码) ⛔ 跳过——鸿蒙有自己的桥接 lib/harmony_adapt/
共享业务层(lib/ 下的跨平台 Dart 代码) ✅ 评估后跟进
构建 / CI 层(.github/、lib/scripts/、tool/) ⛔ 跳过——鸿蒙有自己的 test.yml 与构建流程
其中对于共享业务层,鸿蒙已有等效实现的功能跳过;鸿蒙已用其他方式修复的 bug 跳过;只有确实带来新功能、或修复两边共有 bug 的改动才跟进。

2. 功能先进上游,我们通过定期三方合并跟进,不在下游首发

我们与上游的同步方式是三方合并(2.1.0、2.1.1、2.1.2 均如此),而不是逐个 cherry-pick,这样才能保证鸿蒙没碰过的上游改动全部跟进,且不会在同一处反复解冲突。

这条原则背后有实打实的代价核算。在 2.1.1 的同步评估中我们量化过:如果让鸿蒙分支的某类代码与上游产生永久性的行为分叉,那么此后每一轮同步、每一个双方都碰过的文件都会冲突;按当时 184 个重叠文件计算,等于给未来每一轮同步凭空增加 100+ 个冲突。因此我们对在本分支首发功能这一行为极度保守:

  • 若上游最终以不同方式实现同一功能 → 我们每次同步都要手工调解,永远无法收敛;
  • 若上游最终不采纳 → 我们需要长期自行维护。

3. 鸿蒙 SDK 约束是刚性的

  • 鸿蒙 Flutter fork 目前是 3.41.10-ohos / Dart 3.11.5,上游已在 3.47 / Dart 3.13。我们必须守住 .fvmrc 与 pubspec 的版本下限;
  • CI 跑的是官方 Flutter 3.41.9 stable,因此不得引入 TargetPlatform.ohos,一律用 OS.isHarmony;
  • 大量依赖锁定在鸿蒙 fork 版本(connectivity_plus 5.x 单值返回、media_kit Predidit fork、file_picker_ohos、share_plus 10.x 等),上游升级依赖时我们保留鸿蒙版并在共享层做 API 适配;
  • 由于 JNI 已被桩化(lib/utils/android/android_helper.dart 与 bindings.g.dart 都是同 API 形态的空实现),本分支已经不能构建安卓产物,这是符合"只保证鸿蒙"原则的有意取舍。

二、基于上述原则,本 PR 存在以下问题

1. 功能改动未先进入上游,直接在鸿蒙分支落地

本 PR 对应上游 bggRGjQaUbCoE#2689,据确认尚未被上游合入。更具体的例子是 lib/pages/video/member/controller.dart:PR 删除了 currAid / firstAid / hasPrev / isLoadPrevious 这一套以当前视频为锚点的双向分页,退回成从头单向分页,并连带删除了 view.dart 中定位到当前视频的 jumpTo(112.0 * index) 与 _buildSortBtn 的 lastAid 传递。

我们比对了上游 main 的当前状态,上游至今仍然是锚点双向分页。也就是说这不是应用上游的修复,而是在下游单方面回退了上游仍在维护的实现。bggRGjQaUbCoE#2660 的问题是真实的,但正确的解法是把修复推给上游。

2. PR 中携带了两个不应存在的 GitHub Actions workflow

.github/workflows/pr171-fix-analyzer.yml 声明 permissions: contents: write,在 push 到 resolve/pr171-ci 时检出 resolve/pr171-ohos,运行一段 Python 脚本改写源码,然后用硬编码的提交者身份 commit 并 git push origin HEAD:resolve/pr171-ohos。

这应该是你解决移植冲突时的临时脚手架。它一旦进入 ohos 主干,就等于在仓库里长期留下一个带写权限、由分支名触发、会自动改写代码并推送的作业,这是难以接受的。

同时这段脚本本身也说明了移植过程中的问题:它修复的第 1 条是 "one extra brace closed the State class early"(lib/plugin/pl_player/view/view.dart 出现过括号错配);第 3 条则直接承认 win32 FFI 与本分支锁定的版本不兼容,于是把 lib/utils/windows/network_info.dart 写成了恒返回空值的桩。PR 描述中"PC 端可评估连接质量"这项功能在本 PR 里实际并不存在。

另外 pr171-validate.yml 使用 dart analyze --no-fatal-warnings,掩盖了本 PR 引入的 unused_element(见问题 6)。

3. 主打功能在鸿蒙上完全不生效

lib/utils/connectivity_utils.dart 的 _sample() 中,链路质量只在两个分支取值:

if (Platform.isWindows) {
  final info = WindowsNetworkInfoReader.read();   // 恒返回全 null 的桩
} else if (Platform.isAndroid) {
  final info = PiliAndroidHelper.networkInfo();
}

鸿蒙上二者均为 false,rssi / linkSpeed / signalLevel 恒为 null,signalWeak、speedWeak 恒为 false,wifiWeak 永远是 false;markCurrentWifiWeak() 的第一个条件就是 !Platform.isAndroid 直接 return。

android/app/src/main/java/com/example/piliplus/AndroidHelper.java 中新增的 networkInfo() 同样是死代码。它落在平台原生层,而 Dart 侧的 PiliAndroidHelper 在本分支是空实现桩,networkInfo() 恒返回 null。

因此,本PR上实际生效的只剩"Wi-Fi ↔ 蜂窝切换时重新套用对应偏好",却为此引入了 535 行的 network_policy.dart 设置页,入口标题还是「PC 网络状态联动判断」,会在鸿蒙和手机上照常显示,点进去后 RSSI 阈值、链路速率阈值全部无效,对用户构成误导。

要在鸿蒙上做这个功能,前置条件是先在 lib/harmony_adapt/ 中通过 ArkTS 通道取链路信息(@ohos.net.connection / @ohos.wifiManager),本 PR 未涉及。

4. 蜂窝解码偏好绕过了鸿蒙的编解码器守卫

lib/utils/storage_pref.dart:

static List<VideoDecodeFormatType> get preferCodecsCellular {
  ...
  return const <VideoDecodeFormatType>[.HEVC, .AV1];   // 原为 return preferCodecs;
}

preferCodecs 里有 if (OS.isHarmony) return [HEVC, AVC];,这是我们为规避鸿蒙侧 AV1 解码问题在「视频解码优先级调整」中专门加的。preferCodecsCellular 没有对应守卫,改动后鸿蒙用户一切到蜂窝网就会优先请求 AV1,正好回到要避开的路径上。

同一处还把移动端 preferCodecs 默认值从 [AVC, AV1] 改成 [AVC, HEVC],这是对所有未手动设置过该项的存量用户的静默行为变更,PR 描述中未提及。

5. 启动路径与网络层的改动风险超出可接受范围

  • lib/main.dart 中 await ConnectivityUtils.initialize() 位于 Get.lazyPut(AccountService) 之前,处在启动关键路径上。原 connectivity_utils.dart 的注释正是警告过这一点:"鸿蒙上 checkConnectivity 可能抛异常"。新的 initialize() 只在 _sample() 内部有 try/catch,外层的 Connectivity().onConnectivityChanged.listen(...) 既无 try 也无 onError:——鸿蒙侧插件在此处抛出即等于启动崩溃。
  • lib/http/init.dart 把原本 iOS 专属的 _resetAdaptersForNetworkChange()(会 connectionManager.close(force: true) 并替换 adapter)推广到全平台。此前仅限 iOS 是有原因的。
  • connectivity_utils.dart 中 _bufferingWeak 是单向锁存:置 true 后只有 connectivity 事件才复位。Android 上一次 5 秒卡顿就会把整个会话钉死在"等效移网"偏好上,网络恢复也回不来;而这次状态变化会同时触发 _reloadForNetworkPolicy() 重开播放器和上面的连接池强制关闭。一次普通卡顿的代价过高。

6. CDN 测速:采样量提高了一个数量级

lib/pages/setting/widgets/select_dialog.dart:

? (warmup: 4194304, max: 16777216)   // 蜂窝 16 MiB
: (warmup: 8388608, max: 67108864);  // Wi-Fi 64 MiB

原实现是 const maxSize = 8 * 1024 * 1024(8 MB)统管全部。CDNService.values 共 21 项、_testAllCdnServices 串行遍历,Wi-Fi 下上限约 1.34 GB;每项还有 15 s 硬超时,全程最长 315 秒。此外 _measureStream 一旦抛异常就 fallback 到 _measureLegacy 重下一遍,流量再叠一次。

附带两个显示问题:三种采样类型输出三种单位(MiB/s / M/s / MB/s,其中 fallback 用 1000×1000、其余用 1024×1024),无法横向比较;'首包 ${firstByteMs.toStringAsPrecision(3)}ms' 在首包 >1000 ms 时会渲染成 1.23e+3ms。

7. 倍速统计服务的运行开销与收益不匹配

lib/services/playback_stats_service.dart(707 行):Timer.periodic(30s, flush) 全生命周期不取消;flush() 内 jsonDecode(jsonEncode(_stats!)) 做全量深拷贝再写 Hive;而 _dirty 几乎恒为 true,这会导致_settleAppForeground() 只要 app 在前台就会 _add('appForegroundUs', ...)。因此即使不播放任何视频,也会每 30 秒全量序列化并落盘一次。liveByUid 每观看一个直播间新增一个 key,无裁剪、无上限,开销随使用时长线性增长。

8. 其他技术问题

  • lib/plugin/pl_player/controller.dart 新增的 _onUserLeaveHint() 无任何调用方。本分支通过 MethodChannel 处理 onUserLeaveHint,这是从上游 JNI 版本误带过来的死代码,会触发 unused_element 警告。
  • adjustLongPressSpeed() 的 max(0.25, playbackSpeed + steps * 0.25) 只有下界没有上界;配合 onLongPressMoveUpdate 的 -dy / 24 一档,上滑 240px 即 +2.5x,无封顶。
  • onNetworkPolicyChanged 是 PlPlayerController 单例上的单个回调字段:VideoDetailController 无条件赋值且 onClose 不清理,LiveRoomController 则备份/还原,视频页与直播页交替打开时会互相覆盖;而 AudioController 用的又是直接订阅 ConnectivityUtils.changes,三处写法不一致。
  • lib/pages/live_room/controller.dart 的 _previousNetworkPolicyCallback 是 late final,异常路径下 onClose 先执行会抛 LateInitializationError;同文件还无故删除了本分支的注释「跨设备接续等场景显式指定初始状态(听直播/暂停),优先于默认行为」,并对调了 queryLiveUrl 的参数顺序(纯噪声 diff)。
  • lib/utils/page_utils.dart 强制 url.http2https,影响所有平台的 webview 跳转,而非仅 Android。
  • lib/pages/video/controller.dart 的 queryVideoUrl 被整体包进 try/finally,导致近 400 行是纯缩进变化,真实逻辑改动被完全淹没;同处 _pendingNetworkRefresh 存在丢事件路径(置位条件含 data.dash == null,但只在 finally 中且 hasDashResponse == true 时消费)。
  • 整个 PR 在一次提交中混合了 4 个互不相关的 bugfix([Bug] 二级评论展示那个设置项的文本有误 bggRGjQaUbCoE/PiliPlus#2671 / [Bug] 在播放页里打开的up主页显示问题 bggRGjQaUbCoE/PiliPlus#2660 / [Bug] windows端,直播间的弹幕表情包图片大小异常 bggRGjQaUbCoE/PiliPlus#2686 / [Bug] 评论区部分链接打开显示不允许HTTP明文传输 bggRGjQaUbCoE/PiliPlus#2638)、3 个独立功能和一个 707 行的新遥测服务。即便前述问题全部解决,这样的粒度我们也无法负责任地评审与回归。

三、因此,我们决定不合并该 PR,但以下贡献会被考虑

不合并是分支定位与工程风险的综合判断,不是对您贡献的否定。以下几点您的判断是对的,我们会由团队按自己的节奏实现,你无需再修改本 PR:

1. CDN 测速的计算错误。

2. Wi-Fi ↔ 蜂窝切换后不重新套用偏好。

3. 长按倍速系数可自定义。

4. 二级评论排序文案(bggRGjQaUbCoE#2671)。

5. 直播表情在高 DPR 设备上尺寸异常(bggRGjQaUbCoE#2686)。

6. 视频页进入创作者空间时列表不完整(bggRGjQaUbCoE#2660)。

7. 网络高峰期临时切换编码偏好。

最后,再次感谢您的投入和贡献。

好的,由于这个修改确实过于激进,不合并我确实可以理解。不过关于单位换算方面,我解释一下我的哲学:

这并不是 bug,这是有意为之的设计。

上游的测速使用的时间单位不太科学。

因为我自己是做路由器脚本的(https://github.com/ucxn/ZTE-Stat_Max)。

所以我对这个网络流量的精确度啊、公式啊,以及时间之类的,就是说会比较执着。

上游的话,他犯了一个大错误,也就是:把一个累计量变成了等差数列相加,导致错误的大 8 倍。

然后新版的话,我采用了更科学的测速方式,但是貌似只有登录的用户才能享受到。未登录用户无法享受到。所以必须得回退到原作者的方式。

简而言之,当前的情况是:
新版(我的测速方式)
状态 A:全程跑完
状态 B:方式本质不变,但由于该 CDN 速度太慢,超过了 15 秒的超时期,所以提前结束测试。

状态 C:原作者的测速方式。

我的设计哲学是:

  1. 原作者使用的是 1000 进制,所以既然回退到了原作者的方式,我就尊重原作者采用 1000 进制。
  2. 我自己设置的部分要遵循科学的单位,绝大多数人对 M 的理解都是 MiB,也就是 1024 进制。
  3. 我非常讨厌写作 MB,但是实际上用 1000 进制的地方。但是我们也不能教育每个人都使用 MiB,所以我的想法是,当你实在想简写的时候,宁可简写成 K、M、G 之类的,也不要错误的用 KB、MB、GB 表示 1024 进制。MB 就必须是 1000 进制,1024 进制可以用 MiB,也可以用 M。
    4.打个不恰当的比方,这个世界并不是按照自己的理想运行的,假如家里的老人就是死活不愿意吃医生开的药,此时有一个通过认证的保健品和一个江湖骗子卖的药。你宁可老人吃保健品,还是骗子药?

这里也是一样的,其实我传达的哲学是,当你要使用 1024 进制,并且你实在觉得 MiB 麻烦的时候,你宁可简写成 M,也不要用 MB 去污染术语,MB 就必须是 1000 进制。

就像索尼污染了 NAT2 一样,NAT2 是 IP 地址受限锥型,也就是 EIM+ ADF,比大熊猫还稀有。猫典范站长说其不到 2/1000。为什么任天堂和 Xbox 不令人讨厌呢?因为人家新创了术语,不像 PS 一样沿用了已有的一二三四数字,结果又和民间约定俗成的说法不一样。

5.所以这里相当于通过最小的认知代价,让大家可以直观的看出我们的测速走的是路径 A、路径 B 还是路径 C。

MiB 就是路径 A,M 就是路径 B,都是 1024 进制。

MB 就是路径 C,是 1000 进制,尊重原作者。

也就是说,绝大多数用户他都不是网络极客。

他不关心这个 CDN 到底有没有 fallback 到其他的测速机制

如果每一轮都写出来的话会很丑,但是这个信息相当于一个小彩蛋,开发者可以看懂。

6.然后关于编解码器,您指出的问题确实存在。其实最理想的是,未调整过值,使用默认值的用户可以更新。我的设计是安卓端优先使用 AVC,其次 H265,电脑才是其次 AV1。原作者作为开源作者,他可能是比较喜欢开源精神,所以使用 AV 1,不想使用那个有授权费的 H 265。当然,这只是猜测,不是结论。Windows 电脑确实有很多没有 H265 的版权,所以会转换成软件解码。此时拥抱开源是正确的。但问题是,绝大多数手机包括华为,你在购买的时候就已经为 H265 授权付了费,此时不用反而可惜。

如果在使用 WiFi 的话,AV1 节约的流量并不可观。况且 AV 1 也只比 h 265 小一点,b 站的 h 265 已经很小了,比 h 264 小一半。此时收益不匹配,而且 AV1 复杂的解码度在移动设备上会导致发热变高。

最后,再次感谢作者的耐心解答。您就像一位耐心的老师一样,作为一个成熟的开源开发者,为我指出了很多问题。这就像改作文一样,很多时候自己是很难理解的。经过您的指点之后,我就大概知道应该如何更好的参与到开源项目当中了,非常感谢!@wm-develop

@ucxn

ucxn commented Aug 27, 2026

Copy link
Copy Markdown
Author

好,刚刚有一些设计意图,比如为什么混合单位,我已经讲过了。顺带一提,我现在决定干脆自己维护一个分支算了。因为确实我这种极高倍速的爱好者,还增加了离线解码实验之类的,属于较极端的用户,正常用户都用不到。

我承诺里面绝对没有任何恶意代码或者上传隐私信息的公网遥测。您可以把给当做一个产品体验一下,签名和官方 APK 不同,可以在安卓机上面安装着玩一下。

也就是说,如果您感兴趣的话,可以把它给当成一个产品,看看里面增加了哪些功能,纯粹玩玩。但是代码确实没有多大的参考价值,而且我现在也正在自己修复性能问题。

当然,不玩也没关系,我只是顺口提一句。

就真的很感谢您耐心指出了这么多问题。不过有一些设计确实是有原因的,我在上面也解释了。就当聊天一样,可以花个 5 分钟闲暇时间看一眼就行了。

https://github.com/ucxn/Bili.Libre.Speed

祝您生活愉快,天天开心~(≥▽≤)/~

@wm-develop

Copy link
Copy Markdown
Member

好的,理解你说的原因,也看到你维护的分支了。其实我个人感觉上游对这些新东西的引入也是非常审慎和克制的,先前我PR了一个windows客户端画中画可以不置顶的小修改也没有被接受,哈哈

@ucxn

ucxn commented Aug 27, 2026

Copy link
Copy Markdown
Author

好的,理解你说的原因,也看到你维护的分支了。其实我个人感觉上游对这些新东西的引入也是非常审慎和克制的,先前我PR了一个windows客户端画中画可以不置顶的小修改也没有被接受,哈哈

是的,其实像那个蜂窝下也可以选择编码器,这玩意给合并进去以后,我上次的 PR 确实被成功合并了,这软件就基本上可以算是语义上有点类似于图灵完备的这种感觉了,该统一该暴露的地方总算暴露了剩下的应用层的东西不涉及到底层,其实合不合并也无所谓了,不如自己开个分支自己玩。因为确实很多时候编码器带来的收益会比画质还要高得多,如果蜂窝采用粗暴降画质,反而有点类似于那种大锅饭思维,不太科学。管它实现有没有 bug ,很多时候哲学更重要,哲学被合并了我就很开心了。bug后续肯定有人修(doge)

bggRGjQaUbCoE#2571

再次感谢您!

@wm-develop wm-develop closed this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 二级评论展示那个设置项的文本有误

2 participants