You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.
Walkthrough
新增局域网共同聆听功能,包含房间发现、权限控制、同步播放、本地媒体共享和歌词展示。播放器命令及界面会根据 LAN 会话状态和权限调整。新增 LAN 模块、音频引擎、媒体传输、测试和调试入口。另新增 BeatSync 音频与时钟同步原型、跨运行时验证工具、构建脚本及组件研究资料。
Merge Risk: Moderate · previous review d2bd3
LAN sessions may fail to connect, remain active after setup times out, or end unexpectedly. LAN audio may also continue through the speaker after USB output disconnects. Resolve these risks before merging.
Pre-merge checks: 4 · 1
Failed checks: 1 warning
Check name
Status
Explanation
Resolution
Docstring Coverage
Docstring coverage is 1.65% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed checks: 4 passed
Check name
Status
Explanation
Description Check
Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring coverage is 1.65% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 121 functions across 55 files. (1 skipped: 1 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.
IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.
- LAN control WebSocket never uses a proxy (NO_PROXY) so RFC1918 and
loopback addresses are not handed to the system proxy selector
- join timeouts are teardown errors, not cancellations: stop resources
and fall back to IDLE instead of leaving the room stuck in CONNECTING
- ACTION_SYNC from the LAN bootstrap bypasses the empty-queue stopSelf
(the sync arrives before the queue is populated)
- service idle/sticky checks count an active LAN session, not just the
cloud one
The reason will be displayed to describe this comment to others. Learn more.
Clamp endpoint seeks before scheduling Web Audio
When a user taps or drags progress to 100%, the UI can submit exactly durationMs, and the room policy accepts that endpoint, but this branch throws POSITION for position >= buffer.duration. The room has already committed the PLAY revision, so scheduler recovery and subsequent SYNC messages keep retrying an unplayable position and leave every member paused/loading until another control changes room state; clamp to the last playable frame or handle the endpoint as track completion.
The reason will be displayed to describe this comment to others. Learn more.
Restore the pre-session position with the queue
When a listener leaves after a received local lan-session: track, this branch restores the pre-session queue and song but leaves _playbackPositionMs at the shared track's position. applyRemoteQueueUpdate also immediately snapshots that value for the restored queue, and with keep-last-progress enabled the next play uses it as a nonzero requested position, so an unrelated original song can resume and later persist at the LAN track's offset; save and restore the original position alongside the queue/index.
The reason will be displayed to describe this comment to others. Learn more.
Apply the authoritative position for paused syncs
When a client reconnects after missing a pause, or explicitly syncs while paused, and it already has this track decoded, LanPlayerAdapter.prepare() returns immediately from ready(track) without applying trackTimeSeconds; this branch then only pauses the engine. The client therefore keeps its pre-disconnect position instead of the room's authoritative paused position, so paused progress/manual sync remains wrong until a later PLAY reschedules it; seek to the supplied position after preparation (or make the ready path apply it).
- clamp endpoint seeks in the Web Audio scheduler instead of throwing
POSITION after the room already committed the PLAY revision, which
left every member retrying an unplayable position
- save and restore the pre-session playback position alongside the
restored queue so an unrelated original song does not resume at the
LAN track's offset
- apply the room's authoritative position when prepare() short-circuits
on an already-decoded track during paused syncs
…e test
The bundle now clamps out-of-range positions to the last playable frame
per the Codex review fix, so the assertion expecting a POSITION rejection
no longer matches the corrected behavior.
The reason will be displayed to describe this comment to others. Learn more.
Refresh expiring source URLs before late syncs
When a listener joins or reconnects after the current online candidate has expired, this SYNC reuses currentSources captured by the original PLAY instead of asking the host for fresh candidates. These URLs are explicitly time-limited—the YouTube path parses the expire parameter and bounds its normal cache to eight minutes (modules/platform/src/main/java/moe/ouom/neriplayer/platform/youtube/cache/YouTubePlayableAudioCache.kt:12-30)—and LanPlayerAdapter.resolveSharedAudioSources() also returns cached values whenever the host is ready. The late client therefore fails preparation, and every subsequent SYNC receives the same dead URL until the track changes.
The reason will be displayed to describe this comment to others. Learn more.
Bypass proxies for the LAN media transfer
On a device whose Wi-Fi/system proxy is configured, this client still inherits the default ProxySelector, so the http://<room-host>:<mediaPort> request in fetchResource() can be sent to the proxy instead of directly to the host. LanSessionManager explicitly uses Proxy.NO_PROXY for the control WebSocket, but LanLocalPlayback constructs a default client for this downloader; local-file joins on such networks can therefore fail with LOCAL_TRANSFER (and unnecessarily disclose the bearer request) even though the control connection succeeds.
The reason will be displayed to describe this comment to others. Learn more.
Keep sleep-timer expiry from becoming a room command
When a sleep countdown expires during a LAN session, createSleepTimerManager() calls pause() with the default LOCAL source (PlayerManager.kt:789-791), and this new early return converts it into a room PAUSE. For a listener without play/pause permission the server rejects it and local Web Audio continues past the requested timer; with permission it pauses every member instead of only satisfying the local timer. Timer expiry needs a local safety/leave path rather than the interactive room-control route.
The reason will be displayed to describe this comment to others. Learn more.
Report an address from the active LAN interface
On hosts with Wi-Fi plus mobile data or a VPN, multiple interfaces can have site-local IPv4 addresses and NetworkInterface enumeration is not ordered by the route used for the room. If a cellular/VPN address appears first, state.address and the manual-join address displayed by LanRoomPanel.kt:94 are unreachable from Wi-Fi peers even though the server is listening correctly. Resolve the address from the active LAN/Wi-Fi network rather than taking the first site-local interface.
- LAN media transfer bypasses proxies like the control channel, so
local-file joins still work on devices with a system proxy
- sleep-timer expiry stops only the local output: during a LAN session
it interrupts the local Web Audio instead of broadcasting a room pause
that could be rejected or pause every member
- the host reports an address from the active internet-capable interface
(Wi-Fi preferred, VPN excluded) instead of the first site-local one
- when every host-shared online URL has expired, the member retries
once with freshly resolved URLs from its own account before failing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
描述 / Description
在
modules/ltwlan与:playback:runtime的core.player.lan包中实现「局域网一起听」(issue #540 的延续与重构),核心是一套 Web Audio 播放架构 + BeatSync 调度语义:播放架构(与既有 ExoPlayer 云端/本地播放的关系)
decodeAudioData将整曲解码为 PCM,用AudioContext硬件时钟外推播放位置,采样级source.start(when)定点起播——获得无跳变、无漂移累积的同步基准(对拍 BeatSync 源码语义)。LanFileSnapshot边复制边算 SHA-256 生成不可变快照,副设备完整下载校验后进入解码与同步流程。与 PlayerManager 的边界
:playback:runtime通过LanPlaybackBinding门控接管:仅局域网会话激活时命令路由到 LAN 引擎、PlaybackAudioInfo由LanPlayerAdapter写入(保证播放页编码/码率徽章正常显示);非会话时所有钩子直接短路,播放行为与原实现完全一致。播放页显示组件、PlaybackAudioInfo模型与音频信息提取管线零改动。入口与 UI
播放页「更多」菜单中与云端「一起听」并列;局域网会话期间播放页可直接打开房间菜单;局域网房间页与云端一起听页同样禁用面板滑动手势(本 PR 已与上游
isMetadataApplicationBusy条目合并为并集条件)。上游合并说明
已合入上游 master
51f0100c(14 个提交)。冲突 6 处均已按机械方式解决:NowPlayingSecondaryActions.kt:手势条件取并集(LAN 页面集合 + 上游isMetadataApplicationBusy);PlayerManagerPlaybackExtensions.kt:LAN 路由门与上游serverRecoveryPosition = null均保留;CONTRIBUTING*/docs/implementation*):采用上游表格结构,补回:ltwlan条目、库数 14→15。因合并叠加使
PlayerManagerPlaybackExtensions.kt(2004 行)与PlayerManagerPersistenceExtensions.kt(2002 行)超出仓库 2000 行门禁,按门禁意图做纯搬移拆分(无逻辑改动):seek 簇 →PlayerManagerSeekExtensions.kt,歌词获取簇 →PlayerManagerLyricsExtensions.kt。质量门禁与测试
verifyModularization(含模块归属/依赖边界/文件行数门禁)、:ltwlan:check、assembleDebug通过;类型 / Type
修复或解决的问题 / Issues Fixed or Closed by This PR
Closes #540
清单 / Checklist
其他信息 / Additional Information
tools_pub/beatsync-prototype为同步语义验证原型,docs/lan-listen-together-beatsync-design.md为设计文档,供审查参考。UI 截图 / Screenshots
双机同步播放(同秒截屏,进度一致):
局域网一起听
测试