Repository navigation
feat(encyclopedia): 新增歌曲百科并适配上游 Clean 双主题 - #541
Conversation
- 首页新增「Bilibili 音乐榜」与「Bilibili 音乐热门」两个模块,位置排在网易云「私人雷达 / 雷达歌单」之后、「热力飙升 / 我的推荐」之前 - 音乐榜支持热门榜 / 原创榜切换,详情页在期数旁展示更新时间 - 音乐热门改用移动端分区榜接口 app.bilibili.com/x/v2/rank/region,Web 端 ranking/v2 分区榜数据已冻结废弃 - 新增 BiliVideoIdCodec 做 av→bv 本地换算(移动端接口只返回 aid) - 卡片样式与 B 站收藏夹一致:16:9 缩略图、统一缩略图参数、名次 / 时长角标
- Coalesce Bilibili startup loads so the international bootstrap path no longer cancels/restarts the in-flight rank and hot requests. - Count the Bilibili sections in the home visibility check, so they still render when the Netease card switches are all off. - Propagate offlineMode to the shared HomeViewModel from both detail screens and gate refresh entry points accordingly. - Drop the root-level BackHandler in both detail screens so the host predictive back transition keeps working. - Add KDoc to the new public/internal declarations to satisfy the docstring coverage check.
The rank type switch read the cache only when online and always set loading = true, so an offline switch briefly showed a spinner before loadBiliMusicRank took its cache-only branch.
- BiliMusicHomeRepository: 官方 MV 补全改为按 4 条一批串行推进, 避免冷启动/强制刷新时一次性打出几十个 WBI view 请求触发 412 风控 - BiliMusicDetailScreens: 两个详情列表底部内边距改为 24.dp + LocalMiniPlayerHeight, 修复播放中迷你播放器遮挡最后一行与「打开原视频」按钮的问题
- config/quality/domain-dependencies.json:把新增的 B 站音乐模型类 (BiliMusicHotVideo / BiliMusicRankPeriod / BiliMusicRankSong)登记进 platform-bilibili-client 域的 allowed_classes,修复 :platform:verifyDomainDependencies 的 6 条违规(该任务在 CI 的 Gradle tests 里失败)。 - BiliMusicModels:新增 BiliMusicPlaybackSource.UNAVAILABLE,当官方 MV 与 榜单授权投稿都不完整时如实标注「暂无可播放音频」,不再误报为投稿视频。 - BiliMusicHomeRepository:MV 补全按批推进时,若某一批全部失败(典型是风控 412 或超时)就停止后续批次,立即把剩余曲目原样返回,避免榜单长时间加载。
:app:testDebugUnitTest 中 OwnedMainSourceLineBudgetTest 失败: HomeScreen.kt 达 2032 物理行,超过 LocalManagementLineBudget MAX_EXCLUSIVE = 2000 的预算。 将 14 个 HomeScrollKey* 常量与 7 个 home*ScrollKey 辅助函数 整体移入新增的 HomeScrollKeys.kt,均为纯字符串拼装逻辑, 不改变任何运行时行为。声明用 internal(Kotlin 无包私有, 两文件同包可直接引用)。 同时清理两个失效 import: - androidx.compose.ui.text.font.FontWeight(本就无使用点) - moe.ouom.neriplayer.ui.viewmodel.tab.NeteaseHomePlaylistSource (提取后类型名不再显式出现,仅经类型推断) HomeScreen.kt: 2032 → 1982 行,两文件未使用 import 均为 0。
提取滚动锚点 key 到 HomeScrollKeys.kt 时漏了 favoriteId 扩展函数的 import,导致 CI :app:compileDebugKotlin 报 Unresolved reference 'favoriteId'。 favoriteId 是 YouTubeMusicPlaylist 的扩展函数(YouTubeMusicModels.kt:28), 只需补 import,无需改 key 语义。 顺带在 gradle-tests artifact 里加上 **/build/reports/problems/**, 让下次 CI 失败能直接拿到 problems report 的 file:line。
评论1 持久化榜单类型:HomeSelectedItem.BiliMusicRank 从 object 改为携带 rankType 的 data class,saveState/restore 都把 rankType 编进 savedState; BiliMusicRankDetailScreen 新增 restoredRankType 参数,进程恢复时用它对齐 共享 HomeViewModel(否则恢复后展示默认 HOT 而非用户上次选的榜单)。 评论2 离线重进重启空加载:BiliMusicRankDetailScreen 自己追踪 wasOffline, 从离线切回在线且当前该榜为空时强制重载一次。首页不再组合时 HomeScreen 的 wasOffline 恢复刷新不会抵达这个详情目的地,故在详情页内补齐。
- lint: modules/common 新增的 15 条 home_bili_music_* 只有 values/ 与 values-en/ 两份,values-zh/ 缺失,触发 :common:lintDebug 的 MissingTranslation(默认 severity=error),打挂 CI 的 Gradle tests job。补上对应的中文翻译。 - P2-A: 榜单详情页不再把 BiliMusicRankType 编进导航身份。原先 HomeHostScreen 硬编码 HomeSelectedItem.BiliMusicRank(BiliMusicRankType.HOT),首页选中原创榜后 打开详情会被 restore effect 立刻切回热门榜;详情内切榜也不会回写,进程恢复后 仍回到热门榜。改为 BiliMusicRank 恢复为 object,类型单独用 rememberSaveable 持久化在宿主侧:首页「查看全部」时经 onBiliMusicRankMore(type) 记住当前 ui.biliMusicRank.type,详情内切榜经 onRankTypeSelected 回写。类型与导航身份 解耦后,切榜不会改变 AnimatedContent 的 targetState,避免多余转场动画,也不会 重跑 LaunchedEffect(selected) 破坏返回时的滚动恢复。 - P2-B: BiliMusicHotDetailScreen 镜像榜单详情页的 wasOffline 空载恢复。原先该页 的 LaunchedEffect(offlineMode) 只同步离线标志,进入或恢复时若首屏加载因离线被 跳过,回到在线后不会重启,页面会一直停在「暂无内容」直到手动刷新。
在播放页更多选项面板中加入「歌曲百科」二级页,数据全部来自网易云 公开接口,不推断、不占位: - model:新增 SongEncyclopedia 数据族与 encyclopediaSongKey(), 用于跨层统一的歌曲标识(platform 不能依赖 local 的 stableKey) - platform:NeteaseClient 新增两个匿名 GET 接口 (/api/song/play/about/block/page 与 /api/song/creators); 新增解析器、内存缓存(10 分钟 TTL / 32 条 LRU)与仓库实现, 按 songKey 缓存,CancellationException 向上传播 - viewmodel:NowPlayingViewModel 新增百科状态机,用 RequestGeneration 票据 + Job 取消防串台,切歌后旧响应不会覆盖新歌 - ui:新增 SongEncyclopediaContent 二级页(加载/空/不支持/失败重试/ 成功五态),入口挂在更多选项主列表 - 资源:新增三套 strings_encyclopedia.xml - 门禁:platform-netease-repository 域白名单补入 encyclopedia 包 - 测试:新增解析器、缓存、仓库三个单测
在「更多」面板新增「歌曲百科」二级页,接入网易云歌曲百科数据源。 数据层(modules/platform/netease/encyclopedia) - NeteaseClient: 新增 getSongEncyclopediaSummaryCancellable / getSongCreatorsCancellable (/api/song/play/about/block/page 与 /api/song/creators,API 模式匿名 GET) - NeteaseEncyclopediaParser: 解析 SONG_PLAY_ABOUT_SONG_BASIC 板块的曲风/推荐标签/ 语种/BPM/获奖/综艺收录,以及创作者角色表 - NeteaseSongEncyclopediaRepository: 严格按 songKey 匹配、内存 LRU+TTL 缓存、 CancellationException 向上传播、创作团队失败单独降级不影响整页 - SongEncyclopediaMemoryCache: 同步 LRU 缓存,10 分钟 TTL / 32 条 - SongEncyclopedia(modules/model/encyclopedia): 数据模型 + encyclopediaSongKey() 界面层(app/.../ui/screen/nowplaying/actions/,分片薄函数) - SongEncyclopediaSongDetailSection: 曲风/专辑/语种/发行日期/BPM/乐器/制作团队 - SongEncyclopediaListeningSection: 本机听歌记录(首听时间/累计时长/播放次数), 明确标注非账号完整历史 - SongEncyclopediaArtistSection: 艺人头像/名称/专辑与单曲数/可展开简介; 不渲染关注按钮与粉丝数(数据源无匿名来源,避免误导) - SongEncyclopediaSimilarSongsSection / RelatedPlaylistsSection / CommentEntry - SongEncyclopediaContent: 状态机承载(Success 携带 songKey 二次校验防串台) 接线 - MoreOptionsPage 新增 ENCYCLOPEDIA 枚举与分派,经 navigation 复用既有 艺人/歌单路由;NowPlayingScreen 传 onEnterArtist / onEnterPlaylist - NowPlayingViewModel: encyclopediaState + load/cancel/retry, RequestGeneration 代际票据丢弃旧请求,禁止 fetch-then-set - domain-dependencies.json: platform-netease-repository 追加 encyclopedia 模型白名单 测试 - modules/platform: 57 项(解析器 23 / 仓库 22 / 缓存 12) - app: 9 项(状态机、代际防串台、重复请求跳过、取消传播) - :app:verifyDomainDependencies 0 violations; :app:crapToolTests 为既有 javac EXCEPTION_ACCESS_VIOLATION 问题,需 -x 跳过 未实现(数据源不存在,不伪造) - 模块四 AI 解读(「小宝」在原项目无对应服务) - 模块九 相关播客(MUSIC_MEMORY 板块匿名不可用) - 粉丝数 / 关注按钮 / 乐谱上传(无匿名来源或成本过高)
UI 隔离 - 新增 SongEncyclopediaSectionCard:每个模块独立 Card 容器, 单模块的空态/失败态/内容高度变化不再影响其它模块边界 - SongEncyclopediaBody 改为逐模块包裹卡片:歌曲详情、听歌记录、 关于艺人、评论预览、相似歌曲、相关歌单、获奖收录 评论预览(仅网易云) - 新增 SongEncyclopediaCommentPreview:复用 CommentViewModel 与 CommentItem,展示前 3 条评论、评论总数与「查看全部评论」入口 - 使用独立 key 的 CommentViewModel,避免与完整 CommentSheet 共用实例 导致一方离开组合时清掉另一方状态 - 只读预览:点赞/回复/楼中楼全部禁用,不在百科页产生副作用操作 - 仅 commentSource.platform == NETEASE 时渲染;B 站/本地/其它来源不显示 - 完整评论列表仍由既有 CommentSheet 提供 登录态关注 - 新增 SongEncyclopediaArtistFollowButton:仅网易云登录态为 Valid 时渲染, 未登录完全隐藏(关注无法与账号同步时显示按钮会误导) - 关注动作复用项目既有的 FavoritePlaylistRepository 与 toggleCreatorFollow,与艺人详情页同一套本地收藏逻辑 - 关注中显示加载态,失败在按钮下方显示错误,不伪造成功 字符串 - values / values-zh / values-en 各新增 2 条: encyclopedia_comment_preview_failed、encyclopedia_comment_show_all 测试 - 新增 SongEncyclopediaCommentPreviewPolicyTest(4 项): 网易云可预览、B 站不可预览、本地歌曲无来源、resourceId 非正不可预览 - 既有 platform 57 项百科测试、app 9 项状态机测试、4 项 MoreOptions 测试全部通过 - :app:verifyDomainDependencies 0 violations(需 -x :app:crapToolTests) - :app:assembleDebug 通过
崩溃原因 - SongEncyclopediaCommentPreview 使用 LazyColumn,而百科页外层是 Column(Modifier.verticalScroll(...))。垂直可滚动组件被测量为无限高度 约束时 Compose 直接抛 IllegalStateException。 - 真机堆栈: LazyListKt.rememberLazyListMeasurePolicy -> checkScrollableContainerConstraints -> throwIllegalStateException 修复 - 预览只有 3 条,不需要懒加载,改为普通 Column + forEachIndexed + key - 移除 LazyColumn / itemsIndexed 导入,新增 key 导入 约束记录 - 百科页内任何模块都不得再引入 LazyColumn / LazyRow / LazyVerticalGrid / verticalScroll,外层滚动是唯一滚动容器
1. 模块区分度:抽取 SongEncyclopediaSectionCard 带标题栏(图标+标题+分隔线), 卡片下沉到各 section 内部,有内容才渲染,卡片之间统一 12dp 间距; 删除各 section 内部的 SubsectionTitle 与 SongEncyclopediaContent 的重复标题。 2. 半屏:把评论面板内联的半屏逻辑抽成公共组件 HalfExpandableSheetPanel, 评论与歌曲百科共用同一套拖拽/吸附实现;百科页标题栏新增勾柄与全屏切换。 3. 评论跳转:「查看全部评论」改为先收起更多面板,再由宿主打开原生评论面板, 不再在百科页之上叠一层 CommentSheet。 4. 预测性返回:更多面板主页返回时整个面板跟手缩小+圆角+滑出(复用 PredictiveBackDismiss 基建),子页返回主页仍走原有转场; ModalBottomSheetProperties(shouldDismissOnBackPress = false) 交给面板自管。 本机仅验证 :app:compileDebugKotlin;单测与 CRAP/域依赖门禁交由 GitHub CI。
在 MoreOptionsSheetNavigation 实参里新增 onOpenComments lambda 后,NowPlayingScreen 内 lambda 编译编号整体 +1(152 → 153),crap-scope.json 的 method_scopes 仍按旧名字声明, :app:crapReport 因而报 "Method scopes have no measured methods"。按 2cec540 的既有做法 跟随编号更新。
1. 新增 SongEncyclopediaSectionAction.kt:把百科各模块的「展开/收起」 「查看全部评论」「重试」「播放全部」统一收敛到 HapticTextButton 实现, 图标、尺寸、颜色、触觉、间距一致,替换原来四处各写一遍的文字按钮。 2. 评论预览的加载态改用公共组件 CommentLoadingBlock,与评论面板保持一致。 3. MoreOptionsEncyclopediaPage 的双层嵌套 lambda 改为调用命名私有函数 openEncyclopediaComments,把圈复杂度从 3 降到 2,与同文件其余 7 个 MoreOptions*Page 持平。 4. 补 4 条 MoreOptionsSheetBackTargetTest 用例覆盖 MoreOptionsSheetOwner.canPredictiveDismiss,把该 getter 覆盖率 拉到 100%,以通过 :app:verifyCrap 门禁。 本机仅验证编译;单测与 CRAP/域依赖门禁交由 GitHub CI。
合并上游 02611a5「feat(ui): add Clean preview theme and refresh app interactions (cwuom#538)」,取得 UiStyle(Material/Clean) 双风格基建。 手工解决 3 个冲突: - MoreOptionsMainContent.kt:保留我的歌曲百科入口与一起听入口, 改用上游 MoreOptionsItem 组件(hue 分别用 MoreHueViolet / MoreHueIndigo),跟随上游 Clean 视觉。 - HomeScreen.kt:上游把 HomeScrollKey* / HomePhoneGridCells / key 辅助函数挪回本文件,与我从 HomeScreen.kt 抽出到 HomeScrollKeys.kt 的行数预算修复重叠。删除冲突块,继续使用 HomeScrollKeys.kt 作为唯一定义处,并把 HomePhoneGridCells 一并 放进该文件。 - crap-scope.json:取上游 method_scopes(合并后源码以上游为准), source_patterns 取上游(HEAD 无独有条目)。 在此基建上开始 Clean 适配:B 站音乐首页卡片、音乐榜/音乐热门详情页、 歌曲百科的封面与关注按钮改用 neriRoundedShape(),使两种风格各自合适。
合并上游 Clean 主题解冲突时,listen_together 与 encyclopedia 两个入口 被写成两组重复项,且第一组末尾误删 MoreOptionsGroup 的闭合括号, 花括号失衡导致编译期语法错误。 恢复为两项共用一个 MoreOptionsGroup:listen_together(MoreHueIndigo) + encyclopedia(MoreHueViolet)。括号平衡校验 116/116。
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Merge Risk: 🔵 Low · up to
|
There was a problem hiding this comment.
Actionable comments posted: 1
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments: 4
modules/model/src/main/java/moe/ouom/neriplayer/data/model/bilibili/music/BiliMusicModels.kt: 1
152-161: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win官方 MV 条目的
uploaderMid与实际播放视频不一致
hasOfficialMv为 true 时,uploader使用playbackUploader,也就是 MV 账号名。uploaderMid却仍然是creation_up,也就是授权投稿 UP 主的 mid。因此,同一个BiliVideoItem中的名称和 mid 属于两个不同账号。如果播放页或后续流程按
uploaderMid打开 UP 主空间或执行关注操作,用户会看到 MV 账号名,但会进入投稿 UP 主的空间。这与 KDoc 中“上传者与实际播放的那支视频保持一致”的约定不符。
BiliMusicHomeRepository.enrichOfficialMv已经获取了VideoBasicInfo,其中包含ownerMid,但该值没有写回模型。建议新增mvUploaderMid,并在官方 MV 分支中使用它。🐛 建议修复
/** 官方 MV 的上传账号名(补全时带回) */ val mvUploader: String = "", + /** 官方 MV 的上传账号 mid(补全时带回) */ + val mvUploaderMid: Long = 0L,uploader = playbackUploader, - uploaderMid = uploaderMid, + uploaderMid = if (mvUploaderMid > 0L) mvUploaderMid else uploaderMid, coverUrl = videoCover,在
BiliMusicHomeRepository.enrichOfficialMv中同步写入:song.copy( mvAid = info.aid, mvDurationSec = info.durationSec, mvUploader = info.ownerName, mvUploaderMid = info.ownerMid, mvCoverUrl = normalizeVideoCover(info.coverUrl) )如果
mvUploader为空,playbackUploader会回退到投稿 UP 主名称。此时 mid 也应保持为投稿 UP 主的 mid,以保证名称和 mid 属于同一账号。app/src/test/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaCommentPreviewPolicyTest.kt: 1
53-57: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win测试校验的是本地副本逻辑,与生产代码不一致。
isPreviewEligible是测试内私有实现,要求resourceId > 0L。生产代码SongEncyclopediaBody只判断commentSource?.platform == CommentPlatform.NETEASE,没有resourceId检查。因此netease song without positive resource id is not eligible通过,但生产代码会为resourceId = 0的来源渲染评论预览。生产判断以后改动时,这组测试也不会失败。请把判断提取为
internal fun isEncyclopediaCommentPreviewEligible(source: CommentSource?),在SongEncyclopediaBody和本测试中共用。依据路径指令:"Flag a test only when it can pass despite a user-visible regression"。
Source: Path instructions
app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaContent.kt: 2
402-408: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win播放量单位硬编码为「万」,英文界面显示中文字符。
formatEncyclopediaPlayCount固定拼接"万"。英文资源encyclopedia_playlist_play_count为%1$s plays,英文用户会看到681万 plays。请把单位放入字符串资源,或按Locale选择格式(例如英文使用K/M)。🌐 修复示例
-internal fun formatEncyclopediaPlayCount(playCount: Long): String { - return if (playCount >= 10_000L) { - "${playCount / 10_000L}万" - } else { - playCount.toString() - } -} +internal fun formatEncyclopediaPlayCount(context: Context, playCount: Long): String { + return if (playCount >= 10_000L) { + context.getString(CoreCommonR.string.encyclopedia_play_count_ten_thousand, playCount / 10_000L) + } else { + playCount.toString() + } +}
304-304: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win同一字段在页面上重复显示。
SongEncyclopediaSongDetailSection已展示曲风、语种、BPM、专辑、作词、作曲、编曲、制作人。SongEncyclopediaLegacySections在同一页面再次展示「基本信息」「音乐属性」「创作团队」卡片。用户会看到曲风、BPM、专辑、作词等同一数据出现两次。请只保留一处数据源:从
SongEncyclopediaLegacySections中去掉已由歌曲详情卡片覆盖的字段,或只保留otherRoles、标签、获奖与收录这类歌曲详情卡片未覆盖的内容。Also applies to: 345-345
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: cwuom/NeriPlayer/.coderabbit.yaml
- Review profile: QUIET
- Plan: Advanced
- Run ID:
5d0597bf-5965-4b93-a4e7-87d1c2e2e011
📒 Files selected for processing: 52
.github/workflows/android_ci.ymlapp/src/main/java/moe/ouom/neriplayer/core/di/AppContainer.ktapp/src/main/java/moe/ouom/neriplayer/ui/NeriApp.ktapp/src/main/java/moe/ouom/neriplayer/ui/component/comment/CommentSheet.ktapp/src/main/java/moe/ouom/neriplayer/ui/component/sheet/HalfExpandableSheetPanel.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/host/HomeHostScreen.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/NowPlayingScreen.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/NowPlayingSecondaryActions.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/EncyclopediaSongFactory.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/MoreOptionsMainContent.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaArtistFollowButton.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaArtistSection.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaCommentPreview.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaContent.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaListeningSection.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaRelatedPlaylistsSection.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSectionAction.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSectionCard.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSectionsLegacy.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSimilarSongsSection.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSongDetailSection.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/tab/home/BiliMusicDetailScreens.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/tab/home/BiliMusicHomeSections.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/tab/home/HomeScreen.ktapp/src/main/java/moe/ouom/neriplayer/ui/screen/tab/home/HomeScrollKeys.ktapp/src/main/java/moe/ouom/neriplayer/ui/viewmodel/NowPlayingViewModel.ktapp/src/main/java/moe/ouom/neriplayer/ui/viewmodel/SongEncyclopediaUiState.ktapp/src/main/java/moe/ouom/neriplayer/ui/viewmodel/tab/HomeViewModel.ktapp/src/main/java/moe/ouom/neriplayer/ui/viewmodel/tab/bili/BiliMusicHomeRepository.ktapp/src/test/java/moe/ouom/neriplayer/ui/screen/nowplaying/MoreOptionsSheetBackTargetTest.ktapp/src/test/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaCommentPreviewPolicyTest.ktapp/src/test/java/moe/ouom/neriplayer/ui/viewmodel/NowPlayingViewModelEncyclopediaTest.ktconfig/quality/domain-dependencies.jsonmodules/common/src/main/res/values-en/strings_encyclopedia.xmlmodules/common/src/main/res/values-en/strings_home.xmlmodules/common/src/main/res/values-zh/strings_encyclopedia.xmlmodules/common/src/main/res/values-zh/strings_home.xmlmodules/common/src/main/res/values/strings_encyclopedia.xmlmodules/common/src/main/res/values/strings_home.xmlmodules/model/src/main/java/moe/ouom/neriplayer/data/model/bilibili/music/BiliMusicModels.ktmodules/model/src/main/java/moe/ouom/neriplayer/data/model/encyclopedia/SongEncyclopedia.ktmodules/platform/src/main/java/moe/ouom/neriplayer/platform/bilibili/api/client/BiliClient.ktmodules/platform/src/main/java/moe/ouom/neriplayer/platform/bilibili/api/client/BiliVideoIdCodec.ktmodules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/api/client/NeteaseClient.ktmodules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseEncyclopediaParser.ktmodules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseSongEncyclopediaRepository.ktmodules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/encyclopedia/SongEncyclopediaMemoryCache.ktmodules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/encyclopedia/SongEncyclopediaRepository.ktmodules/platform/src/test/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseEncyclopediaExtendedParserTest.ktmodules/platform/src/test/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseEncyclopediaParserTest.ktmodules/platform/src/test/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseSongEncyclopediaRepositoryTest.ktmodules/platform/src/test/java/moe/ouom/neriplayer/platform/netease/encyclopedia/SongEncyclopediaMemoryCacheTest.kt
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.
|
补充一下截图 |
|
等 #542 合并就处理你的 PR |
CodeRabbit Major 意见:百科页的 onEnterArtist / onEnterPlaylist 直接透传到 NeriApp 的 navigateToNeteaseArtist / navigateToNeteasePlaylist,这两个函数只做 navController.navigate,不会关闭播放页覆盖层;而更多面板此时仍然展开。结果是 导航发生在覆盖层下方,用户看不到任何变化,必须手动关闭面板和播放页。 改为与同文件 MoreOptionsMainActions.onAlbum 一致的处理:owner.dismiss 先收起 更多面板,收起完成后再导航并调用 navigation.onNavigateUp() 退出播放页。
新增 onEnterPlaylist 参数使 Kotlin 编译器生成的 lambda 编号整体后移, NowPlayingScreen$lambda$154$* 已不存在,CRAP 门禁报 "Method scopes have no measured methods" 导致 :app:crapReport 失败。 同步为实际编译产物中的 lambda$155$*。
MoreOptionsEncyclopediaPage 内联了四个捕获 owner/navigation 的 lambda 字面量, Compose 编译器为它们生成记忆化分支,使该 composable 的圈复杂度达到 5; 该方法在 CI 覆盖率报告中覆盖率为 0,CRAP = 5^2 + 5 = 30,超过门禁阈值 9。 按同文件 MoreOptionsSearchActions / MoreOptionsMainActions 的既有惯例,把四个 回调提成 MoreOptionsEncyclopediaActions 类字段,composable 内只传字段引用。 复杂度 5 -> 2(0% 覆盖率下 CRAP = 6),行为与顺序完全不变。
|
clean 主题没有适配好 |
回头再适配 |
等会适配,现在有事 |
CI 的 :app:lintDebug 报 LocalContextGetResourceValueCall 共 7 处: 在 composable 里通过 LocalContext.current + context.getString() 取资源 会在配置变化/语言切换时漏掉重组。改为直接调用 stringResource。 - SongEncyclopediaCommentPreview:评论总数 - SongEncyclopediaListeningSection:听歌时长三档 + 播放次数,顺带去掉了 不再需要的 remember(stringResource 本身随组合重组) - SongEncyclopediaRelatedPlaylistsSection:歌单播放量 - SongEncyclopediaSimilarSongsSection:相似歌曲序号 - SongEncyclopediaArtistSection:艺人作品数文案,buildEncyclopediaArtistMeta 改为 @composable 并在内部用 stringResource
上游 cwuom#538 引入 Clean 预览主题后,本 PR 新增的歌曲百科与 B 站音乐 UI 没有跟着接入 NeriDesign tokens,Clean 下会出现卡片与宿主面板糊在一起、 区块标题层级与全 app 其余区块不同源等问题。 - SongEncyclopediaSectionCard:卡片在 Clean 下改用墨色 5% 半透明底 (surfaceContainerLow 为纯白、surfaceTint 被置空后 elevation 染色失效, 原配色对比度仅约 1.07:1),模块标题改用 onSurface + SemiBold、分隔线改 墨色 hairlineAlpha;SongEncyclopediaSectionSpacing 改读 tokens.sectionSpacing - DensityScaledModalBottomSheet:默认 shape 显式接 tokens.sheetShape。 material3 的 ExpandedShape 派生自 MaterialTheme.shapes.extraLarge 的 top-only 版本(SheetBottomTokens.DockedContainerShape = CornerExtraLargeTop), Clean 下两者本就等价,此处只是把圆角来源显式化、不依赖 material3 内部 映射,Material/Clean 下行为均不变 - CommentItem:评论卡片在 Clean 下改墨色 4%,与 sheet 底拉开层次 - SongEncyclopediaContent:面板水平内边距接 tokens.pageHorizontalPadding - BiliMusicHomeSections:区块标题加 Clean 分支(去图标、onSurface + Bold), 榜单类型切换改 NeriChoiceChip,并收回 30dp 图标缩进 - BiliMusicDetailScreens:同上切换 chip;两个详情页 TopAppBar 与 Scaffold 透明化,自定义背景图下不再实心遮挡
7fb080c to
e1e03c8
Compare
上一提交把 Card 换成 Surface 时带进了三处与原始观感不一致的地方, 均按 material3 1.4.0 的默认值逐项核对后修正: - shadowElevation 原本写成 Material 下 1dp:CardDefaults.cardElevation() 的默认值是 FilledCardTokens.ContainerElevation = ElevationTokens.Level0 = 0dp,两套主题原本都没有阴影,改回 0dp - 标题字重原本在 Material 下覆盖成 Normal(400):titleSmall 的 M3 默认 字重是 Medium(500),覆盖会变细,改为 Material 分支原样沿用 - 补 contentColor = onSurface:原 Card 走 contentColorFor(surfaceContainerLow) = onSurface,而 Clean 的墨色半透明不在色板内,不显式指定会退化为继承 外层 LocalContentColor
SongEncyclopediaSectionSpacing 改读 NeriDesign.tokens.sectionSpacing 后, Material 主题下模块间距从原来的 12dp 被抬到 20dp(Clean 才是 28dp), 把非 Clean 主题的观感也改掉了。按 Clean 分支读 token、Material 沿用 12dp 的写法还原,保证 Material 零回归。
。我看了最新发行版本的播放页面,里面最新主题的适配和我现在适配的基本上差不多。还是说,播放界面就是没有适配新主题 |
|
已按你说的重新适配 Clean 主题,麻烦再看一眼。 一共 6 个文件,都沿用了仓库里现有的
同时合并了 upstream master(含 #542 / #543,零冲突),并修掉 之前那版变化不明显,可能和我把重点放在首页 B 站模块、播放页这边动得少有关。如果还有不满意的位置,告诉我具体是哪一块,我继续调。 |
你的主页 clean 主题的标题还是带图标的 我后面帮你改一下吧 |
|
更多菜单的面板在预测性返回时会被切掉底部一角:本地在 ModalBottomSheet 外叠加了自定义的 predictiveDismissTransform, 它的 RoundedCornerShape(cornerRadius) 会把四个角一起圆, 而 sheet 自身只有顶部圆角,底部就被多切掉一层。 改为与云端(cwuom/NeriPlayer)一致:删掉 Modifier.predictiveDismissTransform / rememberPredictiveDismissState / PredictiveDismissHandler / ModalBottomSheetProperties / MoreOptionsSheetOwner.canPredictiveDismiss 及其测试,面板关闭交给 material3 自带的预测性返回,子页返回主页仍由 MoreOptionsAnimatedPage 的 rememberHostPredictiveBackTransition 处理。
99d560e to
e997ae9
Compare
Align artist metadata and follow controls, and add a persistent Bilibili Home visibility setting. Block failed playlist loading behind safe retry dialogs while preserving offline caches and canceling stale requests. Restore anchored More sheet predictive back, proportional content scaling, and cancellation recovery with regression coverage.
Integrate the PR author update while retaining the validated sheet-specific transform, independent cancellation recovery, and proportional content scaling.
Carry the official MV uploader ID through enrichment and playback mapping. Keep encyclopedia metadata in one section, reuse localized play counts, and cover comment preview eligibility through the real UI entry point.

描述 / Description
本 PR 在「正在播放」的「更多」面板中新增歌曲百科功能,同时包含此前 PR #525 的 B 站音乐首页模块,并已把上游
02611a5cd的 Clean 预览主题合并进来、对本分支新增页面完成双主题适配。百科入口位于「更多」面板「播放统计」之后、「一起听」之前;首页两个 B 站音乐模块位于网易云「私人雷达 / 雷达歌单」之后、「热力飙升 / 我的推荐」之前。国际版(YouTube Music)分支同样插在「每日发现」之后、「更多推荐」之前。
A. 歌曲百科(Song Encyclopedia)
modules/model/.../data/model/encyclopedia/SongEncyclopedia.kt(百科数据模型);modules/platform/.../netease/encyclopedia/NeteaseEncyclopediaParser.kt(解析)、NeteaseSongEncyclopediaRepository.kt(仓库)、SongEncyclopediaMemoryCache.kt(内存缓存)。加载支持取消与重试,并通过 generation 计数避免旧请求覆盖当前歌曲的数据。SongEncyclopediaSectionAction.kt统一各模块底部的「展开 / 播放全部 / 重试」按钮样式(图标 18dp、行间距 8dp),替换原先散落的裸TextButton,共 26 处引用;同时把MoreOptionsEncyclopediaPage的双层嵌套 lambda 抽成命名私有函数,降低圈复杂度并通过:app:verifyCrap。SongEncyclopediaUiState.kt;NowPlayingViewModel增加百科加载 / 取消 / 重试入口。modules/common/src/main/res/values/、values-en/、values-zh/三套strings_encyclopedia.xml。NeteaseEncyclopediaParserTest、NeteaseEncyclopediaExtendedParserTest、NeteaseSongEncyclopediaRepositoryTest、SongEncyclopediaMemoryCacheTest、NowPlayingViewModelEncyclopediaTest、SongEncyclopediaCommentPreviewPolicyTest、MoreOptionsSheetBackTargetTest。B. Bilibili 音乐榜 / Bilibili 音乐热门(来自 PR #525)
x/copyright-music-publicity/toplist/all_period(期数)与.../music_list(曲目),支持热门榜 / 原创榜切换,并在「第 N 期」旁展示该期发布时间。x/web-interface/ranking/v2?rid=3,实测该分区榜数据已冻结在 2025-04 且不再更新(B 站已废弃该接口)。改用移动端分区榜app.bilibili.com/x/v2/rank/region?rid=3&type=all&pn=1&ps=100,实测数据新鲜。param(aid 字符串),不含bvid;新增BiliVideoIdCodec实现aidToBvid/bvidToAid双向换算(58 进制 + XOR,含两次字符交换),已用真实接口数据全量校验。creationAid/creationBvid/creationCid),无有效投稿时标记为不可播放。buildBiliThumbnailUrl(cover, 336, 189)、离线缓存请求),并接入脱机模式(只读缓存)与首页整页刷新。C. 合入上游 Clean 预览主题与适配
cwuom/NeriPlayermaster02611a5cdd3cfb56a9ee9d4a36152f1677829eaf(292 files, +27040 −3541),获得UiStyle、NeriDesign/NeriDesignTokens、neriRoundedShape、ContinuousRoundedCornerShape、CleanPressIndication、设置页 UiStyle 选择器与SettingsRepository.uiStyleFlow等双主题基建。MoreOptionsMainContent.kt、HomeScreen.kt(保留滚动锚点 key 抽取,兼容上游回迁的HomePhoneGridCells)、config/quality/crap-scope.json(取上游 patterns,追加本分支需要的作用域)。BiliMusicHomeSections.kt8 处、BiliMusicDetailScreens.kt2 处、SongEncyclopediaArtistFollowButton.kt1 处、SongEncyclopediaSimilarSongsSection.kt2 处,共 13 处RoundedCornerShape→neriRoundedShape(Clean 下经cleanCornerRadiusFor放大为连续圆角)。commit 7fb080c6 / e1e03c8e / a2af5f2e / ace338324,6 files +187/−82):沿用上游三招(MaterialTheme.colorScheme/shapes→neriRoundedShape→NeriDesign.isClean分支),未另造体系:SongEncyclopediaSectionCard.kt:Card迁移到Surface,Clean 走neriRoundedShape(16.dp)(=20dp 连续圆角,与上游MoreGroupShape同构)+onSurface×5%墨色容器 +tokens.hairlineAlpha(6%)发丝线;标题 SemiBold/onSurface、图标onSurfaceVariant(Clean 只用字重表达层级,主色留给动作,与上游HomeSectionHeader同思路)。Material 侧逐项对齐原Card默认值:shape=MaterialTheme.shapes.medium、tonalElevation=shadowElevation=0dp、contentColor=onSurface、标题沿用titleSmall自带 Medium(500) 字重、间距 12dp —— Material 零回归。DensityScaledOverlays.kt:sheet 包装层shape显式接NeriDesign.tokens.sheetShape(Clean 下二者等价,仅把圆角来源显式化)。SongEncyclopediaContent.kt:页面横向 padding 接tokens.pageHorizontalPadding(Material 16dp 不变)。CommentItem.kt:评论区容器 Clean 走onSurface×4%。BiliMusicHomeSections.kt:SectionHeader加 Clean 分支(去图标 + Bold 标题)、FilterChip→NeriChoiceChip(Clean 下胶囊)。BiliMusicDetailScreens.kt:榜单类型切走NeriChoiceChip;两处Scaffold/TopAppBar透明化以露出封面背景。commit 31b2b37c):mergeupstream/master(含 PR fix(ui): stabilize playlist reordering and selection actions #542intention/fix-library-reorder-selection、PR test(ui): stabilize preview theme prompt back dismissal #543),零冲突;合并后:app:compileDebugKotlin通过。本 PR 同时跟进上游PlaylistSelectionActions.kt、NowPlayingQueueSheet.kt、LibraryScreenLocalContent.kt、libs.versions.toml、app/build.gradle.kts等改动。:app:lintDebug报LocalContextGetResourceValueCall,将 5 处LocalContext.current+context.getString(...)改为stringResource(...);本批 Clean 适配同样以:app:lintDebug+:app:compileDebugKotlin全绿为准。D. 合并与评审修复
MoreOptionsGroup闭合括号的语法 bug(花括号平衡 116 / 116)。MoreOptionsMainActions.onAlbum一致),修复导航发生在播放页覆盖层下方、用户看不到变化的问题。NowPlayingScreenKt的 CRAP 方法作用域 lambda 编号(lambda$154→lambda$155),修复:app:crapReport因方法名漂移而退出码 2 的问题。values-zh/strings_home.xml中 15 条home_bili_music_*中文翻译,修复:common:lintDebug的MissingTranslation。界面截图 / Screenshots
类型 / Type
修复或解决的问题 / Issues Fixed or Closed by This PR
TextButton/ 手写间距):统一为SongEncyclopediaSectionAction一套组件。ranking/v2分区榜已被 B 站废弃,数据冻结在 2025-04;改用移动端分区榜接口后恢复新鲜数据。:app:crapReport因 Kotlin lambda 编号漂移失败:同步crap-scope.json中 11 处方法名。:common:lintDebug因home_bili_music_*缺少中文翻译失败:补齐values-zh。清单 / Checklist
./gradlew :app:compileDebugKotlin通过(JDK 21)./gradlew :app:assembleDebug通过,产物已真机安装测试(截图即来自该构建)connectedDebugAndroidTest(本机无连接设备,由云端 instrumented shard 0 / 1 / External Lyrics host 覆盖)其他信息 / Additional Information
cwuom/NeriPlayer:master@b0da29cff(已含 PR fix(ui): stabilize playlist reordering and selection actions #542 / test(ui): stabilize preview theme prompt back dismissal #543);本 PR 共 28 commits / 60 files。SongEncyclopedia.kt、NeteaseEncyclopediaParser.kt、NeteaseSongEncyclopediaRepository.kt、SongEncyclopediaMemoryCache.kt、SongEncyclopediaRepository.kt、SongEncyclopediaUiState.kt、EncyclopediaSongFactory.kt、SongEncyclopediaContent.kt、SongEncyclopediaSectionAction.kt、SongEncyclopediaSectionCard.kt、SongEncyclopediaSectionsLegacy.kt、SongEncyclopediaSongDetailSection.kt、SongEncyclopediaArtistSection.kt、SongEncyclopediaArtistFollowButton.kt、SongEncyclopediaListeningSection.kt、SongEncyclopediaCommentPreview.kt、SongEncyclopediaSimilarSongsSection.kt、SongEncyclopediaRelatedPlaylistsSection.kt、HalfExpandableSheetPanel.kt、BiliMusicModels.kt、BiliVideoIdCodec.kt、BiliMusicHomeSections.kt、BiliMusicDetailScreens.kt、HomeScrollKeys.kt、BiliMusicHomeRepository.kt。PlayerManager、Media3/ExoPlayer、播放队列、音源解析与音频缓存。aidToBvid与官方x/web-interface/view返回的bvid全部匹配;2000 次随机 aid 往返 0 失败);百科解析用真实接口响应做全量字段校验。HomeScreen.kt仍受OwnedMainSourceLineBudgetTest(< 2000 物理行)约束,合并后为 1983 行。np-submodule/miuix指针随合并前移,本分支未单独改动其内容。